Merge hw5-657 (#657)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -1887,9 +1887,10 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit
|
||||
|
||||
/// <summary>
|
||||
/// #619 re-review: open (or re-open) the host's alarm subscription over <paramref name="sourceNodeIds"/>.
|
||||
/// A CHANGED set means the host rebuilt its address space (an alarm-bearing delta always does), recreating
|
||||
/// every condition node inactive+acked — so every last-told state is forgotten and the post-subscribe
|
||||
/// ConditionRefresh re-reports from that default. The SAME set (a reinit / reconnect re-subscribe) keeps
|
||||
/// A CHANGED set means the host applied an alarm-bearing delta, so every last-told state is forgotten and the
|
||||
/// post-subscribe ConditionRefresh re-reports as first sight. Since #641 a rebuild KEEPS native condition state,
|
||||
/// so the host may hold ANY state here — which is why first sight is flagged (#656/#657) rather than assuming
|
||||
/// an inactive+acked default. The SAME set (a reinit / reconnect re-subscribe) keeps
|
||||
/// them, so the refresh copy of an already-reported state emits nothing. Ack targets of refs no longer
|
||||
/// subscribed are pruned either way.
|
||||
/// </summary>
|
||||
@@ -2069,7 +2070,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit
|
||||
/// <summary>
|
||||
/// #619 review — the <see cref="AlarmTransitionKind"/> sequence that moves the host's
|
||||
/// <c>NativeAlarmProjector</c> (which knows only kinds: Raise ⇒ active+unacked — or, on an already-active condition, a re-statement keeping the ack (#647) — Acknowledge ⇒ acked,
|
||||
/// ActiveAcknowledged ⇒ active+acked in one step (#656), Clear ⇒ inactive keeping acked) from <paramref name="prior"/> to <paramref name="target"/>. A single
|
||||
/// ActiveAcknowledged ⇒ active+acked in one step (#656), InactiveUnacknowledged ⇒ inactive+unacked in one step (#657), Clear ⇒ inactive keeping acked) from <paramref name="prior"/> to <paramref name="target"/>. A single
|
||||
/// upstream event can need two (an alarm that went active and was acknowledged between two notifications).
|
||||
/// No state change ⇒ empty (the caller decides whether a message/severity change warrants an Unspecified).
|
||||
/// </summary>
|
||||
@@ -2086,6 +2087,16 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit
|
||||
internal static IReadOnlyList<AlarmTransitionKind> AlarmTransitions(
|
||||
(bool Active, bool Acked) prior, (bool Active, bool Acked) target, bool priorKnown = true)
|
||||
{
|
||||
// Gitea #657: on first sight the host may hold ANY state (it retains a still-deployed condition across a
|
||||
// respawn), so an inactive target is stated in kinds that land on it from every host state: inactive+unacked
|
||||
// as ONE InactiveUnacknowledged; inactive+acked as Clear then Acknowledge (flagged by the caller, so a host
|
||||
// already showing it writes no row, and a host retaining it Active gets a real "Cleared"). Pre-fix an
|
||||
// inactive+acked first sight equalled the default prior and went out as Unspecified — an "Activated" row for
|
||||
// an inactive alarm, and a retained-Active host condition was never cleared.
|
||||
if (!priorKnown && !target.Active)
|
||||
return target.Acked
|
||||
? [AlarmTransitionKind.Clear, AlarmTransitionKind.Acknowledge]
|
||||
: [AlarmTransitionKind.InactiveUnacknowledged];
|
||||
if (prior == target) return [];
|
||||
var kinds = Transitions(prior, target);
|
||||
return priorKnown || target.Acked
|
||||
@@ -2109,11 +2120,12 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit
|
||||
(false, true) => prior.Active
|
||||
? prior.Acked ? [AlarmTransitionKind.Clear] : [AlarmTransitionKind.Clear, AlarmTransitionKind.Acknowledge]
|
||||
: [AlarmTransitionKind.Acknowledge],
|
||||
// Inactive+UNacked (went active and cleared unacknowledged — e.g. first seen by a ConditionRefresh):
|
||||
// the only kinds that un-ack are Raise/Retrigger, so pass through active, then Clear.
|
||||
(false, false) => prior.Active
|
||||
? prior.Acked ? [AlarmTransitionKind.Retrigger, AlarmTransitionKind.Clear] : [AlarmTransitionKind.Clear]
|
||||
: [AlarmTransitionKind.Raise, AlarmTransitionKind.Clear],
|
||||
// Inactive+UNacked (went active and cleared unacknowledged — e.g. first seen by a ConditionRefresh): ONE
|
||||
// atomic InactiveUnacknowledged (Gitea #657) — Raise/Retrigger-then-Clear showed clients the condition
|
||||
// Active again for an instant. From Active+unacked a plain Clear already lands there.
|
||||
(false, false) => prior is (true, false)
|
||||
? [AlarmTransitionKind.Clear]
|
||||
: [AlarmTransitionKind.InactiveUnacknowledged],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user