9cff87fe85
Remove project bookkeeping citations from shipped code comments across the solution: hyphenated task IDs (WP-14, StoreAndForward-025), milestone/task/ issue refs (M3, Task 4, Audit Log #23, #21), Bundle X task-bundle labels, and C/D/K/S/T phase labels. Comment text only — no code logic, string/log literals, or XML-doc structure changed. Genuine descriptions are preserved (only the citation is stripped), and technical lookalikes are retained (UTF-8, SHA-256, T00:00:00, M365, UTC-5, pre-C4/pre-C5 schema versions). Flagged by the new CommentChecker TaskReferenceInComment / TrackingReferenceInComment checks plus targeted grep passes; full solution builds clean, append-only guard tests pass.
105 lines
5.9 KiB
C#
105 lines
5.9 KiB
C#
using ZB.MOM.WW.ScadaBridge.Commons.Types.Alarms;
|
||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||
|
||
namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;
|
||
|
||
/// <summary>
|
||
/// Pure mapping helpers turning OPC UA Alarms & Conditions event fields into the
|
||
/// protocol-neutral <see cref="AlarmConditionState"/> / transition shape. Kept
|
||
/// free of any OPC UA SDK types so it is unit-testable without a live server;
|
||
/// the SDK field extraction lives in <c>RealOpcUaClient</c> and is exercised by
|
||
/// the live smoke test.
|
||
/// </summary>
|
||
public static class OpcUaAlarmMapper
|
||
{
|
||
/// <summary>Clamps an OPC UA severity (1–1000, sometimes out of range) to the unified 0–1000 scale.</summary>
|
||
/// <param name="severity">The raw OPC UA severity value to clamp.</param>
|
||
/// <returns>The severity clamped to the range [0, 1000].</returns>
|
||
public static int NormalizeSeverity(int severity) => Math.Clamp(severity, 0, 1000);
|
||
|
||
/// <summary>Builds an <see cref="AlarmConditionState"/> from the orthogonal A&C sub-states.</summary>
|
||
/// <param name="active">Whether the alarm condition is currently active.</param>
|
||
/// <param name="acked">Whether the alarm has been acknowledged.</param>
|
||
/// <param name="confirmed">Whether the alarm has been confirmed; null if not supported by the server.</param>
|
||
/// <param name="shelve">The current shelving state of the alarm.</param>
|
||
/// <param name="suppressed">Whether the alarm is suppressed.</param>
|
||
/// <param name="severity">The raw OPC UA severity (1–1000); clamped via <see cref="NormalizeSeverity"/>.</param>
|
||
/// <returns>The composed <see cref="AlarmConditionState"/>.</returns>
|
||
public static AlarmConditionState BuildCondition(
|
||
bool active, bool acked, bool? confirmed, AlarmShelveState shelve, bool suppressed, int severity) =>
|
||
new(Active: active, Acknowledged: acked, Confirmed: confirmed,
|
||
Shelve: shelve, Suppressed: suppressed, Severity: NormalizeSeverity(severity));
|
||
|
||
/// <summary>
|
||
/// Derives the transition kind from the change in active/acked sub-states.
|
||
/// Acknowledgement takes precedence over an active/inactive edge when both
|
||
/// change in the same event; an unchanged event is reported as a StateChange.
|
||
/// </summary>
|
||
/// <param name="prevAcked">Acknowledged state before the event.</param>
|
||
/// <param name="nowAcked">Acknowledged state after the event.</param>
|
||
/// <param name="prevActive">Active state before the event.</param>
|
||
/// <param name="nowActive">Active state after the event.</param>
|
||
/// <returns>The <see cref="AlarmTransitionKind"/> that best describes the state change.</returns>
|
||
public static AlarmTransitionKind DeriveKind(bool prevAcked, bool nowAcked, bool prevActive, bool nowActive)
|
||
{
|
||
if (!prevAcked && nowAcked)
|
||
return AlarmTransitionKind.Acknowledge;
|
||
if (!prevActive && nowActive)
|
||
return AlarmTransitionKind.Raise;
|
||
if (prevActive && !nowActive)
|
||
return AlarmTransitionKind.Clear;
|
||
if (prevActive && nowActive)
|
||
return AlarmTransitionKind.Retrigger;
|
||
return AlarmTransitionKind.StateChange;
|
||
}
|
||
|
||
/// <summary>Maps the OPC UA ShelvingState current-state node name to the shelve enum.</summary>
|
||
/// <param name="shelvingStateName">The OPC UA ShelvingState node name, or null when unshelved.</param>
|
||
/// <returns>The corresponding <see cref="AlarmShelveState"/>; defaults to <see cref="AlarmShelveState.Unshelved"/>.</returns>
|
||
public static AlarmShelveState MapShelve(string? shelvingStateName) => shelvingStateName switch
|
||
{
|
||
"OneShotShelved" => AlarmShelveState.OneShotShelved,
|
||
"TimedShelved" => AlarmShelveState.TimedShelved,
|
||
// OPC UA does not expose a distinct "permanent" shelve; treat any other
|
||
// shelved name as one-shot and "Unshelved"/null as unshelved.
|
||
null or "Unshelved" => AlarmShelveState.Unshelved,
|
||
_ => AlarmShelveState.OneShotShelved
|
||
};
|
||
|
||
/// <summary>
|
||
/// Picks a representative display-only limit value from the four standard
|
||
/// <c>LimitAlarmType</c> set-point fields (HighHighLimit, HighLimit, LowLimit,
|
||
/// LowLowLimit) returned by the OPC UA event SelectClause.
|
||
///
|
||
/// <para>
|
||
/// The fields are absent (null raw value) on non-limit alarm types (discrete,
|
||
/// off-normal, etc.). When present, the first non-null value is returned in
|
||
/// priority order: HighHigh → High → Low → LowLow. The caller may use
|
||
/// <c>AlarmTypeName</c> or <c>ConditionName</c> to determine which specific
|
||
/// limit is active; this method intentionally returns the coarsest useful value
|
||
/// for the common single-limit case without requiring callers to understand the
|
||
/// OPC UA limit hierarchy.
|
||
/// </para>
|
||
/// </summary>
|
||
/// <param name="highHighRaw">Raw HighHighLimit field value (null when absent).</param>
|
||
/// <param name="highRaw">Raw HighLimit field value (null when absent).</param>
|
||
/// <param name="lowRaw">Raw LowLimit field value (null when absent).</param>
|
||
/// <param name="lowLowRaw">Raw LowLowLimit field value (null when absent).</param>
|
||
/// <returns>
|
||
/// A formatted string representation of the first non-null limit value, or an
|
||
/// empty string when all four fields are absent (non-limit alarm type).
|
||
/// </returns>
|
||
public static string PickLimitValue(object? highHighRaw, object? highRaw, object? lowRaw, object? lowLowRaw)
|
||
{
|
||
// Standard OPC UA LimitAlarmType limit values are numeric (Double/Float/Int).
|
||
// Convert with InvariantCulture so the decimal separator is always '.' regardless
|
||
// of the server's locale.
|
||
foreach (var raw in new[] { highHighRaw, highRaw, lowRaw, lowLowRaw })
|
||
{
|
||
if (raw is not null)
|
||
return Convert.ToString(raw, System.Globalization.CultureInfo.InvariantCulture) ?? "";
|
||
}
|
||
return "";
|
||
}
|
||
}
|