Commit Graph

3 Commits

Author SHA1 Message Date
Joseph Doherty 371bcb3f91 Resolve Worker.Tests-008..015 code-review findings
Worker.Tests-008: moved the misplaced WorkerLogRedactor test out of
VariantConverterTests into Bootstrap/WorkerLogRedactorTests.

Worker.Tests-009: renamed 46 snake_case alarm-test methods to PascalCase
Method_Scenario_Expectation.

Worker.Tests-010: replaced a weak Assert.Contains with an exact assertion
against the real diagnostic message and corrected the XML doc.

Worker.Tests-011: renamed and re-documented a cancellation test that
overstated what it proved.

Worker.Tests-012: added an oversized-frame (MessageTooLarge) test; renamed
the mislabeled zero-length-payload test.

Worker.Tests-013: removed the fixed-100ms ThrowIfCompletedAsync helper; the
caller now races runTask deterministically.

Worker.Tests-014: consolidated duplicated test fakes/helpers
(FakeRuntimeSession, NoopComApartmentInitializer, NoopEventSink, frame
helpers) into a shared TestSupport namespace.

Worker.Tests-015: added MxAccessEventQueue coverage for drain-all (maxEvents
0), empty-queue drain, and enqueue-after-fault.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 22:59:07 -04:00
Joseph Doherty f711a55be4 A.2: replace AlarmClientConsumer with wnwrap-based polling consumer
Switch the worker's alarm-consumer surface from `aaAlarmManagedClient.AlarmClient`
to `WNWRAPCONSUMERLib.wwAlarmConsumerClass` (CLSID 7AB52E5F-…) hosted by
`wnwrapConsumer.dll`. The new path returns alarm records as a BSTR XML
payload via `GetXmlCurrentAlarms2`, bypassing the FILETIME→DateTime
auto-marshaling that crashed `GetHighPriAlarm` with
ArgumentOutOfRangeException on every poll. Live captured 60/60 polls
clean against `\DESKTOP-6JL3KKO\Galaxy!DEV` while a System Platform
script flipped TestMachine_001.TestAlarm001 every 10s; the GUID,
priority, state (UNACK_ALM ↔ UNACK_RTN), and ASCII-formatted timestamps
arrived end-to-end.

Implementation:
- `Interop.WNWRAPCONSUMERLib.dll` generated via tlbimp, checked in under
  `lib/` so dev boxes don't need the SDK to build.
- New `WnWrapAlarmConsumer` (replaces `AlarmClientConsumer`): owns a
  500ms polling timer, parses `GetXmlCurrentAlarms2` output, diffs the
  snapshot keyed by alarm GUID, and raises one
  `MxAlarmTransitionEvent` per state change. Includes the
  Initialize→Register-before-Subscribe ordering fix found during
  Discovery probe runs.
- New library-agnostic types `MxAlarmSnapshotRecord` /
  `MxAlarmStateKind` / `MxAlarmTransitionEvent` so the proto-build
  path is testable without an AVEVA install.
- `AlarmRecordTransitionMapper` retired the COM-coupled
  `MapTransitionKind(eAlmTransitions)`; new pure helpers
  `ParseStateKind`, `MapTransition(prev, curr)`, and
  `ParseTransitionTimestampUtc` cover XML decode + state-delta logic.
- `IMxAccessAlarmConsumer` event surface changed from
  `EventHandler<AlarmRecord>` to `EventHandler<MxAlarmTransitionEvent>`
  and `SnapshotActiveAlarms()` returns `MxAlarmSnapshotRecord` —
  decoupling the interface from any specific COM library.
- Worker csproj drops `aaAlarmManagedClient` / `IAlarmMgrDataProvider`
  refs; adds `Interop.WNWRAPCONSUMERLib`.

Tests:
- 36 new unit tests (state-string mapping, prev/current → proto kind
  decision table, timestamp UTC reassembly, XML payload parser, 32-char
  hex GUID round-trip) covering everything that doesn't touch the live
  COM surface — all passing.
- Skip-gated `WnWrapConsumerProbeTests.ProbeWnWrapConsumer` archives
  the live capture flow for regression / future probes.

Docs:
- `docs/AlarmClientDiscovery.md` "Option A — captured" section records
  sample XML payloads, the mangled `SetXmlAlarmQuery` round-trip
  (prefer `Subscribe` for filtering), the `GetStatistics`
  AccessViolationException quirk, and the worker-integration outline.

Pre-existing failure noted (separate):
`MxAccessInteropReference_ExistsOnlyInWorkerProject` was already
failing on HEAD — the test project still references `ArchestrA.MxAccess`
for the Skip-gated discovery probes. Not regressed by this change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 09:44:15 -04:00
Joseph Doherty 1ac5bcafb2 worker: AlarmClientConsumer + transition mapper (PR A.5)
Wires the worker-side consumer for AVEVA alarm transitions over the
aaAlarmManagedClient API discovered in the prior foundation PR.

- IAlarmMgrDataProvider.dll referenced — exposes AlarmRecord +
  eAlmTransitions / eQueryType / eSortFlags / eAlarmFilterState.
  Both DLLs (aaAlarmManagedClient + IAlarmMgrDataProvider) load in
  the worker's existing net48 x86 process; no new bitness boundary.
- IMxAccessAlarmConsumer abstraction — Subscribe / AcknowledgeByGuid
  / SnapshotActiveAlarms / AlarmRecordReceived event. Test seam.
- AlarmClientConsumer production wrapper — RegisterConsumer +
  Subscribe + AlarmAckByGUID + GetStatistics-based active-alarm
  walk, all delegated to AlarmClient. Uses AVEVA's managed event
  surface (GetAlarmChangesCompleted on IAlarmMgrDataProvider) so
  no Windows message pump is required — plain .NET events arrive
  on the alarm-client's internal callback thread.
- AlarmRecordTransitionMapper — pure-function helpers:
    MapTransitionKind(eAlmTransitions): ALM→Raise, ACK→Acknowledge,
        RTN→Clear, others (SUB/ENB/DIS/SUP/REL/REMOVE)→Unspecified
        so EventPump's decoding-failure counter records them.
    ComposeFullReference(provider, group, name): Provider!Group.Name
        format matching AVEVA's standard alarm-reference syntax.

Pinned during dev-rig validation (subsequent commits):

1. Confirm RegisterConsumer accepts hWnd=0 — if it requires a real
   hwnd, the worker creates a hidden message-only window and
   passes that handle. The managed event surface should make
   this irrelevant but the AVEVA API is older than its managed
   wrapper.
2. Wire AlarmClientConsumer.AlarmRecordReceived: the AVEVA
   IAlarmMgrDataProvider.GetAlarmChangesCompleted event needs to
   be hooked from inside the AlarmClient — find the proper
   accessor (likely a property exposing the inner provider).
3. AlarmRecord field-by-field translation into the proto event
   uses MxAccessAlarmEventSink.EnqueueTransition (existing
   plumbing). The AlarmRecord field names (ar_OrigTime,
   AlarmName, AckOperatorFullName, AckComment, etc.) are
   pinned in the discovery dump preserved in
   AlarmClientDiscoveryTests.

Tests: 127 pass (4 new ComposeFullReference cases + 1 Skip-gated
discovery probe). Transition-kind enum mapping is dev-rig-validated
rather than unit-tested because the AVEVA assembly is Private=false
on the reference and isn't copied to the test bin directory.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 22:42:22 -04:00