diff --git a/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs b/Source/Libraries/Adapters/DataQualityMonitoring/StateFlagsTransferAdapter.cs index 774ca2a916..86a09fba66 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,64 @@ 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; + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] // Overriden to hide from UI + public override MeasurementKey[] InputMeasurementKeys + { + get => base.InputMeasurementKeys; + set => base.InputMeasurementKeys = 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 transferred.")] public MeasurementStateFlags Flags { - get - { - return m_flags; - } - set - { - m_flags = value; - } + get => m_flags; + set => m_flags = value; } + /// + [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 +146,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 +173,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(); } /// diff --git a/Source/Libraries/Adapters/DynamicCalculator/DynamicFilter.cs b/Source/Libraries/Adapters/DynamicCalculator/DynamicFilter.cs index c8d2f3366e..c3928d4590 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,15 @@ 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 +220,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 +313,22 @@ public override string Status status.AppendLine($" Filter Operation: {FilterOperation}"); status.AppendLine($" Target Value Type: {(m_valueIsArray ? "Array" : "Singleton")}"); - if (FilterOperation == FilterOperation.RemoveWhenTrue) + switch (FilterOperation) { - status.AppendLine($" Processed Measurements: {m_processedMeasurements:N0}"); - status.AppendLine($" Removed Measurements: {m_removedMeasurements:N0}"); - status.AppendLine($" Skipped Removal Sets: {m_skippedRemovalSets:N0}"); - } - else - { - 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 +420,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 +462,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 +522,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 +578,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 . ///