feat(historian-sidecar): live aahClientManaged alarm-event write path (C.1)
SdkAlarmHistorianWriteBackend.WriteBatchAsync replaces the RetryPlease placeholder with the real entry point — HistorianAccess.AddStreamedValue (HistorianEvent, out HistorianAccessError) in aahClientManaged, pinned by decompiling the installed SDK. The write path opens its own ReadOnly=false connection: the query-side HistorianDataSource opens ReadOnly sessions and AddStreamedValue fails on those with WriteToReadOnlyFile. IHistorianConnectionFactory gains a readOnly parameter (default true, query path unchanged); BuildConnectionArgs is extracted as a pure helper. HistorianClusterEndpointPicker is shared for node failover; connection-class errors abort the batch as RetryPlease and reset the connection, malformed-input codes map to PermanentFail. Tests: connection-unavailable batch deferral, ClassifyOutcome error-code table, BuildConnectionArgs read-vs-write shaping (80 pass, 2 rig-skipped). Live_* round-trip tests stay Skip-gated for the D.1 rollout smoke. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -212,36 +212,40 @@ x64, which is not bitness-constrained like the worker). C.1 is independently
|
||||
unblockable from A.2 if the goal is to wire up the scripted-alarm historian
|
||||
path.
|
||||
|
||||
**Current state**:
|
||||
**Current state (DONE — code)**:
|
||||
|
||||
`SdkAlarmHistorianWriteBackend` in `src\MxGateway.Worker\MxAccess\` is a
|
||||
placeholder returning `RetryPlease`. The lmxopcua sidecar's `WriteAlarmEvents`
|
||||
IPC slot is defined in `Ipc\Contracts.cs` but `Program.cs` constructs
|
||||
`HistorianFrameHandler` without an `alarmWriter` (line 57 per the alarms plan).
|
||||
The `IAlarmEventWriter` interface exists; only the production implementation
|
||||
and the consumer wiring are missing.
|
||||
C.1 shipped. `SdkAlarmHistorianWriteBackend.WriteBatchAsync` writes through the
|
||||
real SDK entry point — **`HistorianAccess.AddStreamedValue(HistorianEvent, out
|
||||
HistorianAccessError)`** in `aahClientManaged` — pinned 2026-05-18 by
|
||||
decompiling the installed SDK. `Program.cs` and `Install-Services.ps1` were
|
||||
already wired in the PR C.1 scaffolding. Two corrections to the assumptions
|
||||
this doc was written under:
|
||||
|
||||
**What it needs**:
|
||||
- **There is no `ArchestrAAlarmsAndEvents.SDK` writer.** That assembly
|
||||
(`ArchestrAAlarmsAndEvents.SDK.Common.dll`, the only one installed) is a WCF
|
||||
query-proxy base — no `AlarmHistorianWriter` type. The write path is the
|
||||
`aahClientManaged` `HistorianAccess` surface.
|
||||
- **The write path needs its own connection.** The query-side
|
||||
`HistorianDataSource` opens `ReadOnly` sessions; `AddStreamedValue` on a
|
||||
read-only session fails with `WriteToReadOnlyFile`.
|
||||
`SdkAlarmHistorianWriteBackend` opens a dedicated `ReadOnly=false` connection
|
||||
and shares only `HistorianClusterEndpointPicker` (not the connection object).
|
||||
|
||||
1. New `AahClientManagedAlarmEventWriter.cs` implementing `IAlarmEventWriter`
|
||||
(defined in `Ipc\HistorianFrameHandler.cs`). Calls `aahClientManaged`'s
|
||||
alarm-event write API — same path v1's `GalaxyHistorianWriter` used.
|
||||
Uses `HistorianClusterEndpointPicker` for multi-node routing.
|
||||
Maps `MxStatus` write outcomes to `HistorianWriteOutcome` enum
|
||||
(Ack / PermanentFail / RetryPlease).
|
||||
**What it needed** (all done):
|
||||
|
||||
2. `Program.cs` — build `AahClientManagedAlarmEventWriter` next to the
|
||||
existing `BuildHistorian()` call; pass it to `HistorianFrameHandler`.
|
||||
Gate behind `OTOPCUA_HISTORIAN_ALARM_WRITE_ENABLED` env var (default `true`
|
||||
when `OTOPCUA_HISTORIAN_ENABLED=true`).
|
||||
1. `SdkAlarmHistorianWriteBackend` builds a `HistorianEvent` per
|
||||
`AlarmHistorianEventDto`, calls `AddStreamedValue`, and maps
|
||||
`HistorianAccessError.ErrorValue` codes through
|
||||
`AahClientManagedAlarmEventWriter.MapOutcome` (Ack / PermanentFail /
|
||||
RetryPlease). `HistorianClusterEndpointPicker` drives multi-node failover.
|
||||
2. `Program.cs` — `BuildAlarmWriter()` constructs the backend gated behind
|
||||
`OTOPCUA_HISTORIAN_ALARM_WRITE_ENABLED`.
|
||||
3. `Install-Services.ps1` — env var present in the install-time block.
|
||||
|
||||
3. `Install-Services.ps1` — add the new env var to the install-time block.
|
||||
|
||||
**What blocks C.1**: access to the `aahClientManaged` SDK on the dev box
|
||||
(confirmed available per `project_aveva_platform_installed.md` — AVEVA
|
||||
Historian SDK is present). C.1 can proceed without A.2 since the sidecar's
|
||||
`aahClientManaged` is x64 and does not share the worker's x86 bitness
|
||||
constraint.
|
||||
**What remains for C.1**: only the live-rig write smoke — the `Live_*` tests
|
||||
in `SdkAlarmHistorianWriteBackendTests` stay `Skip`-gated until D.1 confirms a
|
||||
round-trip against a real AVEVA Historian, including the exact mandatory
|
||||
`HistorianEvent` field set.
|
||||
|
||||
**Tests to write**:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user