| @@ -459,7 +459,9 @@ namespace GrafanaAdapters · 458 unmodified lines |
| 459 | /// <summary> | 459 | /// <summary> |
| 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 | 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 | /// 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 | 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 | /// </summary> | 465 | /// </summary> |
| 464 | /// <remarks> | 466 | /// <remarks> |
| 465 | /// Signature: <c>Label(value, expression)</c><br/> | 467 | /// Signature: <c>Label(value, expression)</c><br/> |
| @@ -797,6 +799,7 @@ namespace GrafanaAdapters · 331 unmodified lines |
| 797 | yield return new DataSourceValueGroup | 799 | yield return new DataSourceValueGroup |
| 798 | { | 800 | { |
| 799 | Target = target.Value, | 801 | Target = target.Value, |
| | 802 | RootTarget = target.Value, |
| 800 | Source = dataValues.Where(dataValue => dataValue.Target.Equals(target.Value)) | 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 | for (int i = 0; i < groups.Length; i++) | 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 | yield return new DataSourceValueGroup | 923 | yield return new DataSourceValueGroup |
| 902 | { | 924 | { |
| 903 | Target = $"{label}{(groups.Length > 1 ? $" {i + 1}" : "")}", | 925 | Target = seriesLabel, |
| | 926 | RootTarget = target, |
| 904 | Source = groups[i].Source | 927 | Source = groups[i].Source |
| 905 | }; | 928 | }; |
| 906 | } | 929 | } |
| @@ -914,6 +937,7 @@ namespace GrafanaAdapters · 7 unmodified lines |
| 914 | DataSourceValueGroup result = new DataSourceValueGroup | 937 | DataSourceValueGroup result = new DataSourceValueGroup |
| 915 | { | 938 | { |
| 916 | Target = $"Set{seriesFunction}({string.Join(", ", parameters)}{(parameters.Length > 0 ? ", " : "")}{targetExpression})", | 939 | Target = $"Set{seriesFunction}({string.Join(", ", parameters)}{(parameters.Length > 0 ? ", " : "")}{targetExpression})", |
| | 940 | RootTarget = targetExpression, |
| 917 | Source = ExecuteSeriesFunctionOverSource(dataset.AsParallel().WithCancellation(cancellationToken).SelectMany(source => source.Source), seriesFunction, parameters) | 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 | DataSourceValue dataValue = result.Source.First(); | 947 | DataSourceValue dataValue = result.Source.First(); |
| 924 | result.Target = $"Set{seriesFunction} = {dataValue.Target}"; | 948 | result.Target = $"Set{seriesFunction} = {dataValue.Target}"; |
| | 949 | result.RootTarget = dataValue.Target; |
| 925 | } | 950 | } |
| 926 | | 951 | |
| 927 | yield return result; | 952 | yield return result; |
| @@ -935,6 +960,7 @@ namespace GrafanaAdapters · 7 unmodified lines |
| 935 | yield return new DataSourceValueGroup | 960 | yield return new DataSourceValueGroup |
| 936 | { | 961 | { |
| 937 | Target = $"Slice{seriesFunction}({string.Join(", ", parameters)}{(parameters.Length > 0 ? ", " : "")}{targetExpression})", | 962 | Target = $"Slice{seriesFunction}({string.Join(", ", parameters)}{(parameters.Length > 0 ? ", " : "")}{targetExpression})", |
| | 963 | RootTarget = targetExpression, |
| 938 | Source = ExecuteSeriesFunctionOverTimeSlices(scanner, seriesFunction, parameters) | 964 | Source = ExecuteSeriesFunctionOverTimeSlices(scanner, seriesFunction, parameters) |
| 939 | }; | 965 | }; |
| 940 | | 966 | |
| @@ -944,6 +970,7 @@ namespace GrafanaAdapters · 3 unmodified lines |
| 944 | yield return new DataSourceValueGroup | 970 | yield return new DataSourceValueGroup |
| 945 | { | 971 | { |
| 946 | Target = $"{seriesFunction}({string.Join(", ", parameters)}{(parameters.Length > 0 ? ", " : "")}{dataValues.Target})", | 972 | Target = $"{seriesFunction}({string.Join(", ", parameters)}{(parameters.Length > 0 ? ", " : "")}{dataValues.Target})", |
| | 973 | RootTarget = dataValues.Target, |
| 947 | Source = ExecuteSeriesFunctionOverSource(dataValues.Source, seriesFunction, parameters) | 974 | Source = ExecuteSeriesFunctionOverSource(dataValues.Source, seriesFunction, parameters) |
| 948 | }; | 975 | }; |
| 949 | | 976 | |
| @@ -995,6 +1022,7 @@ namespace GrafanaAdapters · 45 unmodified lines |
| 995 | private static readonly Dictionary<SeriesFunction, int> s_requiredParameters; | 1022 | private static readonly Dictionary<SeriesFunction, int> s_requiredParameters; |
| 996 | private static readonly Dictionary<SeriesFunction, int> s_optionalParameters; | 1023 | private static readonly Dictionary<SeriesFunction, int> s_optionalParameters; |
| 997 | private static readonly string[] s_groupOperationNames; | 1024 | private static readonly string[] s_groupOperationNames; |
| | 1025 | private static readonly string[] s_metadataRecordFields; |
| 998 | | 1026 | |
| 999 | // Static Constructor | 1027 | // Static Constructor |
| 1000 | static GrafanaDataSourceBase() | 1028 | static GrafanaDataSourceBase() |
| @@ -1004,6 +1032,7 @@ namespace GrafanaAdapters · 3 unmodified lines |
| 1004 | // RegEx instance to find all series functions | 1032 | // RegEx instance to find all series functions |
| 1005 | s_groupOperationNames = Enum.GetNames(typeof(GroupOperation)); | 1033 | s_groupOperationNames = Enum.GetNames(typeof(GroupOperation)); |
| 1006 | s_seriesFunctions = new Regex($@"({string.Join("|", s_groupOperationNames)})?\w+\s*(?<!\s+IN\s+)\((([^\(\)]|(?<counter>\()|(?<-counter>\)))*(?(counter)(?!)))\)", RegexOptions.Compiled | RegexOptions.IgnoreCase); | 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 | // RegEx instances to identify specific functions and extract internal expressions | 1037 | // RegEx instances to identify specific functions and extract internal expressions |
| 1009 | s_averageExpression = new Regex(string.Format(GetExpression, "(Average|Avg|Mean)"), RegexOptions.Compiled | RegexOptions.IgnoreCase); | 1038 | s_averageExpression = new Regex(string.Format(GetExpression, "(Average|Avg|Mean)"), RegexOptions.Compiled | RegexOptions.IgnoreCase); |