| @@ -459,7 +459,9 @@ namespace GrafanaAdapters · 458 unmodified lines |
| 459 | 459 | /// <summary> |
| 460 | 460 | /// Renames a series with the specified label value. If multiple series are targeted, labels will be indexed starting at one, e.g., if there are three |
| 461 | 461 | /// series in the target expression with a label value of "Max", series would be labeled as "Max 1", "Max 2" and "Max 3". Group operations on this |
| 462 | | − /// function will be ignored. |
| 462 | + /// function will be ignored. The label parameter also supports substitutions when root target metadata can be resolved. For series values that directly |
| 463 | + /// map to a point tag, one of the following metadata value substitutions can be used in the label value: {ID}, {SignalID}, {PointTag}, {AlternateTag}, |
| 464 | + /// {SignalReference}, {Device} or {SignalType} - where applicable, these substitutions can be used in any combination. |
| 463 | 465 | /// </summary> |
| 464 | 466 | /// <remarks> |
| 465 | 467 | /// Signature: <c>Label(value, expression)</c><br/> |
| @@ -797,6 +799,7 @@ namespace GrafanaAdapters · 331 unmodified lines |
| 797 | 799 | yield return new DataSourceValueGroup |
| 798 | 800 | { |
| 799 | 801 | Target = target.Value, |
| 802 | + RootTarget = target.Value, |
| 800 | 803 | Source = dataValues.Where(dataValue => dataValue.Target.Equals(target.Value)) |
| 801 | 804 | }; |
| 802 | 805 | } |
| @@ -898,9 +901,29 @@ namespace GrafanaAdapters · 95 unmodified lines |
| 898 | 901 | |
| 899 | 902 | for (int i = 0; i < groups.Length; i++) |
| 900 | 903 | { |
| 904 | + string target = groups[i].RootTarget; |
| 905 | + |
| 906 | + string seriesLabel = TargetCache<string>.GetOrAdd($"{label}@{target}", () => |
| 907 | + { |
| 908 | + string derivedLabel = label; |
| 909 | + DataRow record = target.MetadataRecordFromTag(Metadata); |
| 910 | + |
| 911 | + if ((object)record != null) |
| 912 | + { |
| 913 | + foreach (string fieldName in s_metadataRecordFields) |
| 914 | + derivedLabel = derivedLabel.ReplaceCaseInsensitive($"{{{fieldName}}}", record[fieldName].ToString()); |
| 915 | + } |
| 916 | + |
| 917 | + if (derivedLabel.Equals(label, StringComparison.Ordinal)) |
| 918 | + derivedLabel = $"{label}{(groups.Length > 1 ? $" {i + 1}" : "")}"; |
| 919 | + |
| 920 | + return derivedLabel; |
| 921 | + }); |
| 922 | + |
| 901 | 923 | yield return new DataSourceValueGroup |
| 902 | 924 | { |
| 903 | | − Target = $"{label}{(groups.Length > 1 ? $" {i + 1}" : "")}", |
| 925 | + Target = seriesLabel, |
| 926 | + RootTarget = target, |
| 904 | 927 | Source = groups[i].Source |
| 905 | 928 | }; |
| 906 | 929 | } |
| @@ -914,6 +937,7 @@ namespace GrafanaAdapters · 7 unmodified lines |
| 914 | 937 | DataSourceValueGroup result = new DataSourceValueGroup |
| 915 | 938 | { |
| 916 | 939 | Target = $"Set{seriesFunction}({string.Join(", ", parameters)}{(parameters.Length > 0 ? ", " : "")}{targetExpression})", |
| 940 | + RootTarget = targetExpression, |
| 917 | 941 | Source = ExecuteSeriesFunctionOverSource(dataset.AsParallel().WithCancellation(cancellationToken).SelectMany(source => source.Source), seriesFunction, parameters) |
| 918 | 942 | }; |
| 919 | 943 | |
| @@ -922,6 +946,7 @@ namespace GrafanaAdapters · 2 unmodified lines |
| 922 | 946 | { |
| 923 | 947 | DataSourceValue dataValue = result.Source.First(); |
| 924 | 948 | result.Target = $"Set{seriesFunction} = {dataValue.Target}"; |
| 949 | + result.RootTarget = dataValue.Target; |
| 925 | 950 | } |
| 926 | 951 | |
| 927 | 952 | yield return result; |
| @@ -935,6 +960,7 @@ namespace GrafanaAdapters · 7 unmodified lines |
| 935 | 960 | yield return new DataSourceValueGroup |
| 936 | 961 | { |
| 937 | 962 | Target = $"Slice{seriesFunction}({string.Join(", ", parameters)}{(parameters.Length > 0 ? ", " : "")}{targetExpression})", |
| 963 | + RootTarget = targetExpression, |
| 938 | 964 | Source = ExecuteSeriesFunctionOverTimeSlices(scanner, seriesFunction, parameters) |
| 939 | 965 | }; |
| 940 | 966 | |
| @@ -944,6 +970,7 @@ namespace GrafanaAdapters · 3 unmodified lines |
| 944 | 970 | yield return new DataSourceValueGroup |
| 945 | 971 | { |
| 946 | 972 | Target = $"{seriesFunction}({string.Join(", ", parameters)}{(parameters.Length > 0 ? ", " : "")}{dataValues.Target})", |
| 973 | + RootTarget = dataValues.Target, |
| 947 | 974 | Source = ExecuteSeriesFunctionOverSource(dataValues.Source, seriesFunction, parameters) |
| 948 | 975 | }; |
| 949 | 976 | |
| @@ -995,6 +1022,7 @@ namespace GrafanaAdapters · 45 unmodified lines |
| 995 | 1022 | private static readonly Dictionary<SeriesFunction, int> s_requiredParameters; |
| 996 | 1023 | private static readonly Dictionary<SeriesFunction, int> s_optionalParameters; |
| 997 | 1024 | private static readonly string[] s_groupOperationNames; |
| 1025 | + private static readonly string[] s_metadataRecordFields; |
| 998 | 1026 | |
| 999 | 1027 | // Static Constructor |
| 1000 | 1028 | static GrafanaDataSourceBase() |
| @@ -1004,6 +1032,7 @@ namespace GrafanaAdapters · 3 unmodified lines |
| 1004 | 1032 | // RegEx instance to find all series functions |
| 1005 | 1033 | s_groupOperationNames = Enum.GetNames(typeof(GroupOperation)); |
| 1006 | 1034 | s_seriesFunctions = new Regex($@"({string.Join("|", s_groupOperationNames)})?\w+\s*(?<!\s+IN\s+)\((([^\(\)]|(?<counter>\()|(?<-counter>\)))*(?(counter)(?!)))\)", RegexOptions.Compiled | RegexOptions.IgnoreCase); |
| 1035 | + s_metadataRecordFields = new[] { "ID", "SignalID", "PointTag", "AlternateTag", "SignalReference", "Device", "SignalType" }; |
| 1007 | 1036 | |
| 1008 | 1037 | // RegEx instances to identify specific functions and extract internal expressions |
| 1009 | 1038 | s_averageExpression = new Regex(string.Format(GetExpression, "(Average|Avg|Mean)"), RegexOptions.Compiled | RegexOptions.IgnoreCase); |