| | | 1 | | using System.Numerics; |
| | | 2 | | |
| | | 3 | | namespace pva.SuperV.Engine.Processing |
| | | 4 | | { |
| | | 5 | | /// <summary> |
| | | 6 | | /// Alarm state generation processing on a field. |
| | | 7 | | /// It sets the alarm state based on a 2 or 4 limits with optional deadband and optionally sets an acknowledgement. |
| | | 8 | | /// </summary> |
| | | 9 | | /// <typeparam name="T"></typeparam> |
| | | 10 | | /// <seealso cref="FieldValueProcessing{T}" /> |
| | | 11 | | public class AlarmStateProcessing<T> : FieldValueProcessing<T>, IAlarmStateProcessing where T : INumber<T> |
| | | 12 | | { |
| | | 13 | | /// <summary> |
| | | 14 | | /// The high-high alarm state value. |
| | | 15 | | /// </summary> |
| | | 16 | | private const int HighHighAlarmState = 2; |
| | | 17 | | |
| | | 18 | | /// <summary> |
| | | 19 | | /// The high alarm state value. |
| | | 20 | | /// </summary> |
| | | 21 | | private const int HighAlarmState = 1; |
| | | 22 | | |
| | | 23 | | /// <summary> |
| | | 24 | | /// The ok alarm state value |
| | | 25 | | /// </summary> |
| | | 26 | | private const int OkAlarmState = 0; |
| | | 27 | | |
| | | 28 | | /// <summary> |
| | | 29 | | /// The low alarm state value. |
| | | 30 | | /// </summary> |
| | | 31 | | private const int LowAlarmState = -1; |
| | | 32 | | |
| | | 33 | | /// <summary> |
| | | 34 | | /// The low-low alarm state value. |
| | | 35 | | /// </summary> |
| | | 36 | | private const int LowLowAlarmState = -2; |
| | | 37 | | |
| | | 38 | | /// <summary> |
| | | 39 | | /// The unacknowledge state value. |
| | | 40 | | /// </summary> |
| | | 41 | | private const int UnackState = 1; |
| | | 42 | | |
| | | 43 | | /// <summary> |
| | | 44 | | /// Gets or sets the high-high limit field definition. |
| | | 45 | | /// </summary> |
| | | 46 | | /// <value> |
| | | 47 | | /// The high-high limit field definition. |
| | | 48 | | /// </value> |
| | 180 | 49 | | public IFieldDefinition? HighHighLimitField { get; set; } |
| | | 50 | | |
| | | 51 | | /// <summary> |
| | | 52 | | /// Gets or sets the high limit field definition. |
| | | 53 | | /// </summary> |
| | | 54 | | /// <value> |
| | | 55 | | /// The high limit field definition. |
| | | 56 | | /// </value> |
| | 180 | 57 | | public IFieldDefinition? HighLimitField { get; set; } |
| | | 58 | | |
| | | 59 | | /// <summary> |
| | | 60 | | /// Gets or sets the low limit field definition. |
| | | 61 | | /// </summary> |
| | | 62 | | /// <value> |
| | | 63 | | /// The low limit field definition. |
| | | 64 | | /// </value> |
| | 180 | 65 | | public IFieldDefinition? LowLimitField { get; set; } |
| | | 66 | | |
| | | 67 | | /// <summary> |
| | | 68 | | /// Gets or sets the low-low limit field definition. |
| | | 69 | | /// </summary> |
| | | 70 | | /// <value> |
| | | 71 | | /// The low-low limit field definition. |
| | | 72 | | /// </value> |
| | 180 | 73 | | public IFieldDefinition? LowLowLimitField { get; set; } |
| | | 74 | | |
| | | 75 | | /// <summary> |
| | | 76 | | /// Gets or sets the deadband field definition. |
| | | 77 | | /// </summary> |
| | | 78 | | /// <value> |
| | | 79 | | /// The deadband field definition. |
| | | 80 | | /// </value> |
| | 160 | 81 | | public IFieldDefinition? DeadbandField { get; set; } |
| | | 82 | | |
| | | 83 | | /// <summary> |
| | | 84 | | /// Gets or sets the alarm state field definition. |
| | | 85 | | /// </summary> |
| | | 86 | | /// <value> |
| | | 87 | | /// The alarm state field definition. |
| | | 88 | | /// </value> |
| | 180 | 89 | | public IFieldDefinition? AlarmStateField { get; set; } |
| | | 90 | | |
| | | 91 | | /// <summary> |
| | | 92 | | /// Gets or sets the acknowledgement state field definition. |
| | | 93 | | /// </summary> |
| | | 94 | | /// <value> |
| | | 95 | | /// The acknowledgement state field definition. |
| | | 96 | | /// </value> |
| | 180 | 97 | | public IFieldDefinition? AckStateField { get; set; } |
| | | 98 | | |
| | | 99 | | /// <summary> |
| | | 100 | | /// Initializes a new instance of the <see cref="AlarmStateProcessing{T}"/> class. Used for deserialization. |
| | | 101 | | /// </summary> |
| | 1 | 102 | | public AlarmStateProcessing() |
| | 1 | 103 | | { |
| | 1 | 104 | | } |
| | | 105 | | |
| | | 106 | | /// <summary> |
| | | 107 | | /// Initializes a new instance of the <see cref="AlarmStateProcessing{T}"/> class. |
| | | 108 | | /// </summary> |
| | | 109 | | /// <param name="name">The name of processing.</param> |
| | | 110 | | /// <param name="clazz">The class of the instance to be processed.</param> |
| | | 111 | | /// <param name="trigerringFieldName">Name of the trigerring field.</param> |
| | | 112 | | /// <param name="highHighLimitFieldName">Name of the high-high limit field. Can be null/empty if not used.</para |
| | | 113 | | /// <param name="highLimitFieldName">Name of the high limit field.</param> |
| | | 114 | | /// <param name="lowLimitFieldName">Name of the low limit field.</param> |
| | | 115 | | /// <param name="lowLowLimitFieldName">Name of the low-low limit field. Can be null/empty if not used.</param> |
| | | 116 | | /// <param name="deadbandFieldName">Name of the deadband field. Can be null/empty if not used.</param> |
| | | 117 | | /// <param name="alarmStateFieldName">Name of the alarm state field.</param> |
| | | 118 | | /// <param name="ackStateFieldName">Name of the acknowledgment state field. Can be null/empty if not used</param |
| | | 119 | | public AlarmStateProcessing(string name, Class clazz, string trigerringFieldName, |
| | | 120 | | string? highHighLimitFieldName, string highLimitFieldName, string lowLimitFieldName, string? lowLowLimitFiel |
| | | 121 | | string? deadbandFieldName, string alarmStateFieldName, string? ackStateFieldName) |
| | 151 | 122 | | : base(name) |
| | 151 | 123 | | { |
| | 151 | 124 | | CtorArguments.Add(trigerringFieldName); |
| | 151 | 125 | | CtorArguments.Add(highHighLimitFieldName ?? ""); |
| | 151 | 126 | | CtorArguments.Add(highLimitFieldName); |
| | 151 | 127 | | CtorArguments.Add(lowLimitFieldName); |
| | 151 | 128 | | CtorArguments.Add(lowLowLimitFieldName ?? ""); |
| | 151 | 129 | | CtorArguments.Add(deadbandFieldName ?? ""); |
| | 151 | 130 | | CtorArguments.Add(alarmStateFieldName); |
| | 151 | 131 | | CtorArguments.Add(ackStateFieldName ?? ""); |
| | | 132 | | |
| | 151 | 133 | | ValidateParameters(clazz, trigerringFieldName, |
| | 151 | 134 | | highHighLimitFieldName, highLimitFieldName, lowLimitFieldName, lowLowLimitFieldName, deadbandFieldName, |
| | 151 | 135 | | alarmStateFieldName, ackStateFieldName); |
| | 151 | 136 | | } |
| | | 137 | | |
| | | 138 | | /// <summary> |
| | | 139 | | /// Builds the field value processing from the <see cref="P:pva.SuperV.Engine.FieldValueProcessing`1.CtorArgumen |
| | | 140 | | /// </summary> |
| | | 141 | | /// <param name="project">The project.</param> |
| | | 142 | | /// <param name="clazz">The clazz.</param> |
| | | 143 | | public override void BuildAfterDeserialization(Project project, Class clazz) |
| | 1 | 144 | | { |
| | 1 | 145 | | string? trigerringFieldName = GetCtorArgument<string>(0); |
| | 1 | 146 | | string? highHighLimitFieldName = GetCtorArgument<string>(1); |
| | 1 | 147 | | string? highLimitFieldName = GetCtorArgument<string>(2); |
| | 1 | 148 | | string? lowLimitFieldName = GetCtorArgument<string>(3); |
| | 1 | 149 | | string? lowLowLimitFieldName = GetCtorArgument<string>(4); |
| | 1 | 150 | | string? deadbandFieldName = GetCtorArgument<string>(5); |
| | 1 | 151 | | string? alarmStateFieldName = GetCtorArgument<string>(6); |
| | 1 | 152 | | string? ackStateFieldName = GetCtorArgument<string>(7); |
| | | 153 | | |
| | 1 | 154 | | ValidateParameters(clazz, trigerringFieldName, |
| | 1 | 155 | | highHighLimitFieldName, highLimitFieldName, lowLimitFieldName, lowLowLimitFieldName, deadbandFieldName, |
| | 1 | 156 | | alarmStateFieldName, ackStateFieldName); |
| | 1 | 157 | | } |
| | | 158 | | |
| | | 159 | | private void ValidateParameters(Class clazz, string? trigerringFieldName, string? highHighLimitFieldName, string |
| | 152 | 160 | | { |
| | 152 | 161 | | TrigerringFieldDefinition = GetFieldDefinition<T>(clazz, trigerringFieldName); |
| | 152 | 162 | | HighHighLimitField = GetFieldDefinition<T>(clazz, highHighLimitFieldName); |
| | 152 | 163 | | HighLimitField = GetFieldDefinition<T>(clazz, highLimitFieldName)!; |
| | 152 | 164 | | LowLimitField = GetFieldDefinition<T>(clazz, lowLimitFieldName)!; |
| | 152 | 165 | | LowLowLimitField = GetFieldDefinition<T>(clazz, lowLowLimitFieldName); |
| | 152 | 166 | | DeadbandField = GetFieldDefinition<T>(clazz, deadbandFieldName); |
| | 152 | 167 | | AlarmStateField = GetFieldDefinition<int>(clazz, alarmStateFieldName)!; |
| | 152 | 168 | | AckStateField = GetFieldDefinition<int>(clazz, ackStateFieldName)!; |
| | 152 | 169 | | } |
| | | 170 | | |
| | | 171 | | public override bool IsFieldUsed(string fieldName) |
| | 0 | 172 | | => IsFieldUsed(HighHighLimitField, fieldName) || IsFieldUsed(HighLimitField, fieldName) || |
| | 0 | 173 | | IsFieldUsed(LowLimitField, fieldName) || IsFieldUsed(LowLowLimitField, fieldName) || |
| | 0 | 174 | | IsFieldUsed(DeadbandField, fieldName) || |
| | 0 | 175 | | IsFieldUsed(AlarmStateField, fieldName) || IsFieldUsed(AckStateField, fieldName); |
| | | 176 | | |
| | | 177 | | private static bool IsFieldUsed(IFieldDefinition? field, string fieldName) |
| | 0 | 178 | | { |
| | 0 | 179 | | return (field is not null && field.Name.Equals(fieldName)); |
| | 0 | 180 | | } |
| | | 181 | | |
| | | 182 | | /// <summary> |
| | | 183 | | /// Processes the value change. |
| | | 184 | | /// </summary> |
| | | 185 | | /// <param name="instance">Instance on which the triggering field changed.</param> |
| | | 186 | | /// <param name="changedField">The <see cref="Field{T}" /> which changed.</param> |
| | | 187 | | /// <param name="valueChanged">If <c>true</c>, indicates that the trigerring field value changed.</param> |
| | | 188 | | /// <param name="previousValue">The previous value of field.</param> |
| | | 189 | | /// <param name="currentValue">The current value of field.</param> |
| | | 190 | | public override void ProcessValue(IInstance instance, Field<T> changedField, bool valueChanged, T previousValue, |
| | 22 | 191 | | { |
| | 22 | 192 | | if (!valueChanged) |
| | 2 | 193 | | { |
| | 2 | 194 | | return; |
| | | 195 | | } |
| | 20 | 196 | | Field<T>? highHighLimit = GetInstanceField<T>(instance, HighHighLimitField?.Name); |
| | 20 | 197 | | Field<T>? highLimit = GetInstanceField<T>(instance, HighLimitField!.Name); |
| | 20 | 198 | | Field<T>? lowLimit = GetInstanceField<T>(instance, LowLimitField!.Name); |
| | 20 | 199 | | Field<T>? lowLowLimit = GetInstanceField<T>(instance, LowLowLimitField?.Name); |
| | | 200 | | // TODO: Add deadband |
| | | 201 | | // Field<T>? deadband = GetInstanceField<T>(instance, DeadbandField?.Name); |
| | 20 | 202 | | Field<int> alarmState = GetInstanceField<int>(instance, AlarmStateField!.Name)!; |
| | 20 | 203 | | Field<int>? ackState = GetInstanceField<int>(instance, AckStateField?.Name); |
| | 20 | 204 | | int previousAlarmState = alarmState.Value; |
| | | 205 | | int newAlarmState; |
| | | 206 | | // TODO: Handle deadband |
| | 20 | 207 | | if (highHighLimit is not null && currentValue >= highHighLimit.Value) |
| | 12 | 208 | | { |
| | 12 | 209 | | newAlarmState = HighHighAlarmState; |
| | 12 | 210 | | } |
| | 8 | 211 | | else if (highHighLimit is not null && currentValue < highHighLimit.Value && currentValue >= highLimit!.Value |
| | 1 | 212 | | { |
| | 1 | 213 | | newAlarmState = HighAlarmState; |
| | 1 | 214 | | } |
| | 7 | 215 | | else if (lowLowLimit is not null && currentValue <= lowLowLimit.Value) |
| | 1 | 216 | | { |
| | 1 | 217 | | newAlarmState = LowLowAlarmState; |
| | 1 | 218 | | } |
| | 6 | 219 | | else if (currentValue <= lowLimit!.Value) |
| | 1 | 220 | | { |
| | 1 | 221 | | newAlarmState = LowAlarmState; |
| | 1 | 222 | | } |
| | | 223 | | else |
| | 5 | 224 | | { |
| | 5 | 225 | | newAlarmState = OkAlarmState; |
| | 5 | 226 | | } |
| | | 227 | | |
| | 20 | 228 | | if (newAlarmState == previousAlarmState) |
| | 2 | 229 | | { |
| | 2 | 230 | | return; |
| | | 231 | | } |
| | | 232 | | |
| | 18 | 233 | | alarmState.SetValue(newAlarmState); |
| | 18 | 234 | | if (ackState is not null && newAlarmState != OkAlarmState && ackState.Value != UnackState) |
| | 4 | 235 | | { |
| | 4 | 236 | | ackState.SetValue(UnackState); |
| | 4 | 237 | | } |
| | 22 | 238 | | } |
| | | 239 | | } |
| | | 240 | | } |