Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ public class StateFlagsTransferAdapter : FilterAdapterBase
/// </summary>
public const bool DefaultSupportsTemporalProcessing = false;

/// <summary>
/// Default value for the <see cref="ExecutionOrder"/> property.
/// </summary>
public const int DefaultExecutionOrder = 0;

// Fields
private MeasurementStateFlags m_flags;
private MeasurementKey[] m_sourceMeasurements;
Expand All @@ -63,78 +68,64 @@ public class StateFlagsTransferAdapter : FilterAdapterBase

#endregion

#region [ Constructors ]

#endregion

#region [ Properties ]

/// <summary>
/// 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.
/// </summary>
[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;
}

/// <summary>
/// Gets or sets the collection of measurements to which state will be transferred.
/// </summary>
[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;
}

/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)] // Overriden to hide from UI
public override MeasurementKey[] InputMeasurementKeys
{
get => base.InputMeasurementKeys;
set => base.InputMeasurementKeys = value;
}

/// <summary>
/// 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.
/// </summary>
[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;
}

/// <inheritdoc/>
[ConnectionStringParameter]
[Description("Defines the value that determines the order in which filter adapters are executed.")]
[DefaultValue(DefaultExecutionOrder)]
public override int ExecutionOrder { get; set; }

/// <summary>
/// Gets the flag indicating if this adapter supports temporal processing.
/// </summary>
[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

Expand All @@ -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();

Expand All @@ -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();
}

/// <summary>
Expand Down
97 changes: 79 additions & 18 deletions Source/Libraries/Adapters/DynamicCalculator/DynamicFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,13 @@ public enum FilterOperation
/// Defines filter operation that changes measurement values based on expression evaluation.
/// </summary>
[Description("Changes measurement values based on expression evaluation.")]
ValueAugmentation
ValueAugmentation,

/// <summary>
/// Sets measurement state flags when expression evaluates to true.
/// </summary>
[Description("Sets measurement state flags when expression evaluates to true.")]
SetFlagsWhenTrue
}

/// <summary>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -157,10 +164,15 @@ public DynamicFilter() =>
public FilterOperation FilterOperation { get; set; } = DefaultFilterOperation;

/// <summary>
/// Gets or sets measurement state flags that are applied when a value has been replaced when filter operation is set to <see cref="FilterOperation.ValueAugmentation"/>.
/// Gets or sets measurement state flags that are applied when a value has been replaced when filter operation is set to <see cref="FilterOperation.ValueAugmentation"/>
/// or when flags are set when filter operation is set to <see cref="FilterOperation.SetFlagsWhenTrue"/>.
/// </summary>
[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;

Expand Down Expand Up @@ -208,7 +220,7 @@ public override IMeasurement[] OutputMeasurements // Redeclared to hide property
}

/// <summary>
/// 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).
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public new double LagTime // Redeclared to hide property - not relevant to this adapter
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -402,11 +420,9 @@ public void HandleNewMeasurements(ICollection<IMeasurement> 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<int> indexesToBeRemoved)
Expand Down Expand Up @@ -446,12 +462,18 @@ void removeMeasurements(List<int> 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}\".");
Expand Down Expand Up @@ -500,6 +522,29 @@ private void ProcessValueAugmentationForArray(IReadOnlyDictionary<MeasurementKey
}
}

[MethodImpl(MethodImplOptions.Synchronized)] // Access to expression context, m_result and m_index requires synchronization
private void ProcessSetFlagsWhenTrueForArray(IReadOnlyDictionary<MeasurementKey, IMeasurement> 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<string, int> { ["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<int> ProcessRemoveWhenTrueForSingleton(IReadOnlyDictionary<MeasurementKey, IMeasurement> inputs, IReadOnlyDictionary<MeasurementKey, int> indexes)
{
Expand Down Expand Up @@ -533,6 +578,22 @@ private void ProcessValueAugmentationForSingleton(IReadOnlyDictionary<Measuremen
measurement.StateFlags |= AugmentationFlags;
}

[MethodImpl(MethodImplOptions.Synchronized)] // Access to expression context and m_result requires synchronization
private void ProcessSetFlagsWhenTrueForSingleton(IReadOnlyDictionary<MeasurementKey, IMeasurement> 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);
}

/// <summary>
/// Handler for assignment of special variables, e.g., constants, for the <see cref="DynamicCalculator"/>.
/// </summary>
Expand Down
Loading