From c215eb786d88c85d21baac0cff41b44c59270a0b Mon Sep 17 00:00:00 2001 From: "J. Ritchie Carroll" Date: Mon, 2 Mar 2026 11:48:31 -0600 Subject: [PATCH 1/7] DynamcCalculator.DynamicFilter: Added `SetFlagsWhenTrue` filtering operation that sets measurement state flags when expression evaluates to `true`. --- .../DynamicCalculator/DynamicFilter.cs | 94 +++++++++++++++---- 1 file changed, 76 insertions(+), 18 deletions(-) diff --git a/Source/Libraries/Adapters/DynamicCalculator/DynamicFilter.cs b/Source/Libraries/Adapters/DynamicCalculator/DynamicFilter.cs index c8d2f3366e2..c4936fd3a2e 100644 --- a/Source/Libraries/Adapters/DynamicCalculator/DynamicFilter.cs +++ b/Source/Libraries/Adapters/DynamicCalculator/DynamicFilter.cs @@ -54,7 +54,13 @@ public enum FilterOperation /// Defines filter operation that changes measurement values based on expression evaluation. /// [Description("Changes measurement values based on expression evaluation.")] - ValueAugmentation + ValueAugmentation, + + /// + /// Sets measurement state flags when expression evaluates to true. + /// + [Description("Sets measurement state flags when expression evaluates to true.")] + SetFlagsWhenTrue } /// @@ -88,6 +94,7 @@ public class DynamicFilter : DynamicCalculator, IFilterAdapter private long m_processedMeasurements; private long m_removedMeasurements; private long m_skippedRemovalSets; + private long m_measurementFlagsAssigned; private bool m_valueIsArray; private int m_valueArrayLength; private object m_result; @@ -157,10 +164,12 @@ public DynamicFilter() => public FilterOperation FilterOperation { get; set; } = DefaultFilterOperation; /// - /// Gets or sets measurement state flags that are applied when a value has been replaced when filter operation is set to . + /// Gets or sets measurement state flags that are applied when a value has been replaced when filter operation is set to + /// or when flags are set when filter operation is set to . /// [ConnectionStringParameter] - [Description("Defines measurement state flags that are applied when a value has been replaced when filter operation is set to value augmentation.")] + [Description($"Defines measurement state flags that are applied when a value has been replaced when filter operation is set to '{nameof(FilterOperation.ValueAugmentation)}' " + + $"or when flags are set when filter operation is set to '{nameof(FilterOperation.SetFlagsWhenTrue)}'.")] [DefaultValue(DefaultAugmentationFlags)] public MeasurementStateFlags AugmentationFlags { get; set; } = DefaultAugmentationFlags; @@ -208,7 +217,7 @@ public override IMeasurement[] OutputMeasurements // Redeclared to hide property } /// - /// Gets or sets the allowed past time deviation tolerance, in seconds (can be sub-second). + /// Gets or sets the allowed past-time deviation tolerance, in seconds (can be sub-second). /// [EditorBrowsable(EditorBrowsableState.Never)] public new double LagTime // Redeclared to hide property - not relevant to this adapter @@ -301,16 +310,22 @@ public override string Status status.AppendLine($" Filter Operation: {FilterOperation}"); status.AppendLine($" Target Value Type: {(m_valueIsArray ? "Array" : "Singleton")}"); - if (FilterOperation == FilterOperation.RemoveWhenTrue) - { - status.AppendLine($" Processed Measurements: {m_processedMeasurements:N0}"); - status.AppendLine($" Removed Measurements: {m_removedMeasurements:N0}"); - status.AppendLine($" Skipped Removal Sets: {m_skippedRemovalSets:N0}"); - } - else + switch (FilterOperation) { - status.AppendLine($" Augmentation Flags: {AugmentationFlags}"); - status.AppendLine($" Augmented Measurements: {m_processedMeasurements:N0}"); + case FilterOperation.RemoveWhenTrue: + status.AppendLine($" Processed Measurements: {m_processedMeasurements:N0}"); + status.AppendLine($" Removed Measurements: {m_removedMeasurements:N0}"); + status.AppendLine($" Skipped Removal Sets: {m_skippedRemovalSets:N0}"); + break; + case FilterOperation.ValueAugmentation: + status.AppendLine($" Augmentation Flags: {AugmentationFlags}"); + status.AppendLine($" Augmented Measurements: {m_processedMeasurements:N0}"); + break; + case FilterOperation.SetFlagsWhenTrue: + status.AppendLine($" Augmentation Flags: {AugmentationFlags}"); + status.AppendLine($" Processed Measurements: {m_processedMeasurements:N0}"); + status.AppendLine($"Measurement Flags Assigned: {m_measurementFlagsAssigned:N0}"); + break; } return status.ToString(); @@ -402,11 +417,9 @@ public void HandleNewMeasurements(ICollection measurements) Interlocked.Increment(ref m_skippedRemovalSets); return; } - else - { - int index = 0; - indexes = measurements.ToDictionary(measurement => measurement.Key, _ => index++); - } + + int index = 0; + indexes = measurements.ToDictionary(measurement => measurement.Key, _ => index++); } void removeMeasurements(List indexesToBeRemoved) @@ -446,12 +459,18 @@ void removeMeasurements(List indexesToBeRemoved) case FilterOperation.ValueAugmentation when m_valueIsArray: ProcessValueAugmentationForArray(inputs); break; + case FilterOperation.SetFlagsWhenTrue when m_valueIsArray: + ProcessSetFlagsWhenTrueForArray(inputs); + break; case FilterOperation.RemoveWhenTrue when !m_valueIsArray: removeMeasurements(ProcessRemoveWhenTrueForSingleton(inputs, indexes)); break; case FilterOperation.ValueAugmentation when !m_valueIsArray: ProcessValueAugmentationForSingleton(inputs); break; + case FilterOperation.SetFlagsWhenTrue when !m_valueIsArray: + ProcessSetFlagsWhenTrueForSingleton(inputs); + break; default: if (m_processedMeasurements == 0L) OnStatusMessage(MessageLevel.Warning, $"Filter operation \"{FilterOperation}\" is not supported by the {nameof(DynamicFilter)} \"{Name}\"."); @@ -500,6 +519,29 @@ private void ProcessValueAugmentationForArray(IReadOnlyDictionary inputs) + { + long measurementFlagsAssigned = 0L; + + for (m_index = 0; m_index < m_valueArrayLength; m_index++) + { + if (!inputs.TryGetValue(m_variableKeys[$"value[{m_index}]"], out IMeasurement measurement)) + continue; + + Calculate(inputs, new Dictionary { ["value"] = m_index }); + + // If calculation result is true, we update measurement flags + if (!m_result.ToString().ParseBoolean()) + continue; + + measurement.StateFlags |= AugmentationFlags; + measurementFlagsAssigned++; + } + + Interlocked.Add(ref m_measurementFlagsAssigned, measurementFlagsAssigned); + } + [MethodImpl(MethodImplOptions.Synchronized)] // Access to expression context and m_result requires synchronization private List ProcessRemoveWhenTrueForSingleton(IReadOnlyDictionary inputs, IReadOnlyDictionary indexes) { @@ -533,6 +575,22 @@ private void ProcessValueAugmentationForSingleton(IReadOnlyDictionary inputs) + { + if (!inputs.TryGetValue(m_variableKeys["value"], out IMeasurement measurement)) + return; + + Calculate(inputs); + + // If calculation result is true, we update measurement flags + if (!m_result.ToString().ParseBoolean()) + return; + + measurement.StateFlags |= AugmentationFlags; + Interlocked.Increment(ref m_measurementFlagsAssigned); + } + /// /// Handler for assignment of special variables, e.g., constants, for the . /// From 4aeda529706e0b956f84d8c61fcfcfb7e929ce55 Mon Sep 17 00:00:00 2001 From: "J. Ritchie Carroll" Date: Mon, 2 Mar 2026 13:27:46 -0600 Subject: [PATCH 2/7] Refactor Description attribute formatting --- .../Libraries/Adapters/DynamicCalculator/DynamicFilter.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/Libraries/Adapters/DynamicCalculator/DynamicFilter.cs b/Source/Libraries/Adapters/DynamicCalculator/DynamicFilter.cs index c4936fd3a2e..c3928d45908 100644 --- a/Source/Libraries/Adapters/DynamicCalculator/DynamicFilter.cs +++ b/Source/Libraries/Adapters/DynamicCalculator/DynamicFilter.cs @@ -168,8 +168,11 @@ public DynamicFilter() => /// or when flags are set when filter operation is set to . /// [ConnectionStringParameter] - [Description($"Defines measurement state flags that are applied when a value has been replaced when filter operation is set to '{nameof(FilterOperation.ValueAugmentation)}' " + - $"or when flags are set when filter operation is set to '{nameof(FilterOperation.SetFlagsWhenTrue)}'.")] + [Description + ( + $"Defines measurement state flags that are applied when a value has been replaced when filter operation is set to '{nameof(FilterOperation.ValueAugmentation)}' " + + $"or when flags are set when filter operation is set to '{nameof(FilterOperation.SetFlagsWhenTrue)}'." + )] [DefaultValue(DefaultAugmentationFlags)] public MeasurementStateFlags AugmentationFlags { get; set; } = DefaultAugmentationFlags; From b32135847d958460343a94ec0dd34937bc4079a1 Mon Sep 17 00:00:00 2001 From: "J. Ritchie Carroll" Date: Mon, 2 Mar 2026 13:54:10 -0600 Subject: [PATCH 3/7] DataQualityMontitoring: Added `ExecutionOrder` to connection string parameters of `StateFlagsTransferAdapter` filter-type adapter --- .../StateFlagsTransferAdapter.cs | 95 ++++++++----------- 1 file changed, 41 insertions(+), 54 deletions(-) diff --git a/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs b/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs index 774ca2a916c..0246a51e1a3 100644 --- a/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs +++ b/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs @@ -52,6 +52,11 @@ public class StateFlagsTransferAdapter : FilterAdapterBase /// public const bool DefaultSupportsTemporalProcessing = false; + /// + /// Default value for the property. + /// + public const int DefaultExecutionOrder = 0; + // Fields private MeasurementStateFlags m_flags; private MeasurementKey[] m_sourceMeasurements; @@ -63,78 +68,61 @@ public class StateFlagsTransferAdapter : FilterAdapterBase #endregion - #region [ Constructors ] - - #endregion - #region [ Properties ] /// - /// Gets or sets the collection of measurements from which state will be transfered. + /// Gets or sets the collection of measurements from which state will be transferred. /// - [ConnectionStringParameter, - Description("Defines the collection of measurements from which state will be transfered.")] + [ConnectionStringParameter] + [Description("Defines the collection of measurements from which state will be transfered.")] public MeasurementKey[] SourceMeasurements { - get - { - return m_sourceMeasurements; - } - set - { - m_sourceMeasurements = value; - } + get => m_sourceMeasurements; + set => m_sourceMeasurements = value; } /// /// Gets or sets the collection of measurements to which state will be transferred. /// - [ConnectionStringParameter, - Description("Defines the collection of measurements to which state will be transferred.")] + [ConnectionStringParameter] + [Description("Defines the collection of measurements to which state will be transferred.")] public MeasurementKey[] DestinationMeasurements { - get - { - return m_destinationMeasurements; - } - set - { - m_destinationMeasurements = value; - } + get => m_destinationMeasurements; + set => m_destinationMeasurements = value; } /// - /// Gets or sets the flags to be transfered from source + /// Gets or sets the flags to be transferred from source /// measurements to the corresponding destination measurements. /// - [ConnectionStringParameter, - DefaultValue(DefaultFlags), - Description("Defines the set of flags to be transfered.")] + [ConnectionStringParameter] + [DefaultValue(DefaultFlags)] + [Description("Defines the set of flags to be transfered.")] public MeasurementStateFlags Flags { - get - { - return m_flags; - } - set - { - m_flags = value; - } + get => m_flags; + set => m_flags = value; } + /// + /// Gets or sets the values that determines the order in which filter adapters are executed. + /// + /// + /// Gets or sets the value that determines the order in which filter adapters are executed. + /// + [ConnectionStringParameter] + [Description("Defines the value that determines the order in which filter adapters are executed.")] + [DefaultValue(DefaultExecutionOrder)] + public override int ExecutionOrder { get; set; } + /// /// Gets the flag indicating if this adapter supports temporal processing. /// - [ConnectionStringParameter, - Description("Define the flag indicating if this adapter supports temporal processing."), - DefaultValue(DefaultSupportsTemporalProcessing)] - public override bool SupportsTemporalProcessing - { - get - { - return m_supportsTemporalProcessing; - } - } + [ConnectionStringParameter] + [Description("Define the flag indicating if this adapter supports temporal processing.")] + [DefaultValue(DefaultSupportsTemporalProcessing)] + public override bool SupportsTemporalProcessing => m_supportsTemporalProcessing; #endregion @@ -155,15 +143,15 @@ public override void Initialize() if (settings.TryGetValue(nameof(SourceMeasurements), out setting)) m_sourceMeasurements = ParseInputMeasurementKeys(DataSource, true, setting); else - throw new ArgumentException("Missing required connection string parameter: SourceMeasurements", nameof(SourceMeasurements)); + throw new ArgumentException("Missing required connection string parameter", nameof(SourceMeasurements)); if (settings.TryGetValue(nameof(DestinationMeasurements), out setting)) m_destinationMeasurements = ParseInputMeasurementKeys(DataSource, true, setting); else - throw new ArgumentException("Missing required connection string parameter: DestinationMeasurements", nameof(DestinationMeasurements)); + throw new ArgumentException("Missing required connection string parameter", nameof(DestinationMeasurements)); if (m_sourceMeasurements.Length != m_destinationMeasurements.Length) - throw new ArgumentException($"Source and destination measurements are parallel arrays, therefore their lengths must match - Src: {m_sourceMeasurements.Length}, Dst: {m_destinationMeasurements.Length}"); + throw new ArgumentException($"Source and destination measurements are parallel arrays, therefore their lengths must match - Src: {m_sourceMeasurements.Length:N0}, Dst: {m_destinationMeasurements.Length:N0}"); InputMeasurementKeys = m_sourceMeasurements.Concat(m_destinationMeasurements).ToArray(); @@ -182,10 +170,9 @@ public override void Initialize() if (!settings.TryGetValue(nameof(Flags), out setting) || !Enum.TryParse(setting, out m_flags)) m_flags = DefaultFlags; - if (settings.TryGetValue(nameof(SupportsTemporalProcessing), out setting)) - m_supportsTemporalProcessing = setting.ParseBoolean(); - else - m_supportsTemporalProcessing = DefaultSupportsTemporalProcessing; + ExecutionOrder = settings.TryGetValue(nameof(ExecutionOrder), out setting) && int.TryParse(setting, out int executionOrder) ? executionOrder : DefaultExecutionOrder; + + m_supportsTemporalProcessing = settings.TryGetValue(nameof(SupportsTemporalProcessing), out setting) && setting.ParseBoolean(); } /// From d4029e027a375e95fc5f8e76158ae29723fd4584 Mon Sep 17 00:00:00 2001 From: "J. Ritchie Carroll" Date: Mon, 2 Mar 2026 14:02:08 -0600 Subject: [PATCH 4/7] DataQualityMonitoring: Updated `StateFlagsTransferAdapter` to hide `InputMeasurementKeys` from UI, value is auto-assigned from source and destination measurements. --- .../DataQualityMonitoring/StateFlagsTransferAdapter.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs b/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs index 0246a51e1a3..923d09aca3e 100644 --- a/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs +++ b/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs @@ -92,6 +92,14 @@ public MeasurementKey[] DestinationMeasurements set => m_destinationMeasurements = value; } + /// + [EditorBrowsable(EditorBrowsableState.Never)] // Overriden to hide from UI + public override MeasurementKey[] InputMeasurementKeys + { + get; + set; + } + /// /// Gets or sets the flags to be transferred from source /// measurements to the corresponding destination measurements. From 68db52cbd1588722df8dba9571c6c8579b2c54ab Mon Sep 17 00:00:00 2001 From: "J. Ritchie Carroll" Date: Mon, 2 Mar 2026 14:03:22 -0600 Subject: [PATCH 5/7] DataQualityMonitoring: Fixed overridden implementation of `InputMeasurementKeys` --- .../DataQualityMonitoring/StateFlagsTransferAdapter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs b/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs index 923d09aca3e..be12dccd03f 100644 --- a/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs +++ b/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs @@ -96,8 +96,8 @@ public MeasurementKey[] DestinationMeasurements [EditorBrowsable(EditorBrowsableState.Never)] // Overriden to hide from UI public override MeasurementKey[] InputMeasurementKeys { - get; - set; + get => base.InputMeasurementKeys; + set => base.InputMeasurementKeys = value; } /// From 0f6b732cae2f2767ebccf743d377bc271736266d Mon Sep 17 00:00:00 2001 From: "J. Ritchie Carroll" Date: Mon, 2 Mar 2026 14:04:26 -0600 Subject: [PATCH 6/7] DataQualityMonitoring: Updated comments --- .../DataQualityMonitoring/StateFlagsTransferAdapter.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs b/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs index be12dccd03f..9fe9ea681ad 100644 --- a/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs +++ b/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs @@ -113,12 +113,7 @@ public MeasurementStateFlags Flags set => m_flags = value; } - /// - /// Gets or sets the values that determines the order in which filter adapters are executed. - /// - /// - /// Gets or sets the value that determines the order in which filter adapters are executed. - /// + /// [ConnectionStringParameter] [Description("Defines the value that determines the order in which filter adapters are executed.")] [DefaultValue(DefaultExecutionOrder)] From 37a4729835dad18ecb35dd5737f3a7da1d0f1c81 Mon Sep 17 00:00:00 2001 From: "Stephen C. Wills" Date: Tue, 3 Mar 2026 18:26:44 -0500 Subject: [PATCH 7/7] Update Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs --- .../Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs b/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs index 9fe9ea681ad..86a09fba663 100644 --- a/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs +++ b/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs @@ -106,7 +106,7 @@ public override MeasurementKey[] InputMeasurementKeys /// [ConnectionStringParameter] [DefaultValue(DefaultFlags)] - [Description("Defines the set of flags to be transfered.")] + [Description("Defines the set of flags to be transferred.")] public MeasurementStateFlags Flags { get => m_flags;