fix(abcip): report ALMD transitions with ConditionId = RawPath and a Kind (#641)

Found while verifying item 4 of #641. The host routes a native alarm transition
on an exact ConditionId == RawPath match (DriverHostActor.ForwardNativeAlarm),
but AbCipAlarmProjection emitted "{ref}#active" and no Kind, so every ALMD
raise/clear was dropped as "no alarm condition" — and even if routed, an
Unspecified kind projects no state change. It now reports the subscribed
reference (the RawPath) and Raise/Clear. A host test pins that a decorated id
is dropped.

FOCAS has the same id shape plus a device filter keyed by host address (a
RawPath subscription polls nothing); it needs a mapping decision and is
documented in docs/drivers/FOCAS.md rather than patched here.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 17:08:47 -04:00
parent 3fe6cbb905
commit 64d2514e2a
5 changed files with 57 additions and 4 deletions
@@ -193,23 +193,29 @@ internal sealed class AbCipAlarmProjection : IAsyncDisposable
var wasFaulted = sub.LastInFaulted.GetValueOrDefault(nodeId, false);
sub.LastInFaulted[nodeId] = nowFaulted;
// ConditionId is the subscribed reference itself — the alarm tag's RawPath, the key the host routes
// native transitions on — and Kind tells the host's projector what changed (Gitea #641). This used
// to send "{ref}#active" with no Kind, so the host dropped every ALMD transition as unrouted (and an
// Unspecified kind would have projected no state change anyway).
if (!wasFaulted && nowFaulted)
{
_driver.InvokeAlarmEvent(new AlarmEventArgs(
sub.Handle, nodeId, ConditionId: $"{nodeId}#active",
sub.Handle, nodeId, ConditionId: nodeId,
AlarmType: "ALMD",
Message: $"ALMD {nodeId} raised",
Severity: MapSeverity(severity),
SourceTimestampUtc: DateTime.UtcNow));
SourceTimestampUtc: DateTime.UtcNow,
Kind: AlarmTransitionKind.Raise));
}
else if (wasFaulted && !nowFaulted)
{
_driver.InvokeAlarmEvent(new AlarmEventArgs(
sub.Handle, nodeId, ConditionId: $"{nodeId}#active",
sub.Handle, nodeId, ConditionId: nodeId,
AlarmType: "ALMD",
Message: $"ALMD {nodeId} cleared",
Severity: MapSeverity(severity),
SourceTimestampUtc: DateTime.UtcNow));
SourceTimestampUtc: DateTime.UtcNow,
Kind: AlarmTransitionKind.Clear));
}
}
}