Phase 2 Streams A+B+C feature-complete — real Win32 pump, all 9 IDriver capabilities, end-to-end IPC dispatch. Streams D+E remain (Galaxy MXAccess code lift + parity-debug cycle, plan-budgeted 3-4 weeks). The 494 v1 IntegrationTests still pass — legacy OtOpcUa.Host untouched. StaPump replaces the BlockingCollection placeholder with a real Win32 message pump lifted from v1 StaComThread per CLAUDE.md "Reference Implementation": dedicated STA Thread with SetApartmentState(STA), GetMessage/PostThreadMessage/PeekMessage/TranslateMessage/DispatchMessage/PostQuitMessage P/Invoke, WM_APP=0x8000 for work-item dispatch, WM_APP+1 for graceful-drain → PostQuitMessage, peek-pm-noremove on entry to force the system to create the thread message queue before signalling Started, IsResponsiveAsync probe still no-op-round-trips through PostThreadMessage so the wedge detection works against the real pump. Concurrent ConcurrentQueue<WorkItem> drains on every WM_APP; fault path on dispose drains-and-faults all pending work-item TCSes with InvalidOperationException("STA pump has exited"). All three StaPumpTests pass against the real pump (apartment state STA, healthy probe true, wedged probe false). GalaxyProxyDriver now implements every Phase 2 Stream C capability — IDriver, ITagDiscovery, IReadable, IWritable, ISubscribable, IAlarmSource, IHistoryProvider, IRediscoverable, IHostConnectivityProbe — each forwarding through the matching IPC contract. ReadAsync preserves request order even when the Host returns out-of-order values; WriteAsync MessagePack-serializes the value into ValueBytes; SubscribeAsync wraps SubscriptionId in a GalaxySubscriptionHandle record; UnsubscribeAsync uses the new SendOneWayAsync helper on GalaxyIpcClient (fire-and-forget but still gated through the call-semaphore so it doesn't interleave with CallAsync); AlarmSubscribe is one-way and the Host pushes events back via OnAlarmEvent; ReadProcessedAsync short-circuits to NotSupportedException (Galaxy historian only does raw); IRediscoverable's OnRediscoveryNeeded fires when the Host pushes a deploy-watermark notification; IHostConnectivityProbe.GetHostStatuses() snapshots and OnHostStatusChanged fires on Running↔Stopped/Faulted transitions, with IpcHostConnectivityStatus aliased to disambiguate from the Core.Abstractions namespace's same-named type. Internal RaiseDataChange/RaiseAlarmEvent/RaiseRediscoveryNeeded/OnHostConnectivityUpdate methods are the entry points the IPC client will invoke when push frames arrive. Host side: new Backend/IGalaxyBackend interface defines the seam between IPC dispatch and the live MXAccess code (so the dispatcher is unit-testable against an in-memory mock without needing live Galaxy); Backend/StubGalaxyBackend returns success for OpenSession/CloseSession/Subscribe/Unsubscribe/AlarmSubscribe/AlarmAck/Recycle and a recognizable "stub: MXAccess code lift pending (Phase 2 Task B.1)"-tagged error for Discover/ReadValues/WriteValues/HistoryRead — keeps the IPC end-to-end testable today and gives the parity team a clear seam to slot the real implementation into; Ipc/GalaxyFrameHandler is the new real dispatcher (replaces StubFrameHandler in Program.cs) — switch on MessageKind, deserialize the matching contract, await backend method, write the response (one-way for Unsubscribe/AlarmSubscribe/AlarmAck/CloseSession), heartbeat handled inline so liveness still works if the backend is sick, exceptions caught and surfaced as ErrorResponse with code "handler-exception" so the Proxy raises GalaxyIpcException instead of disconnecting. End-to-end IPC integration test (EndToEndIpcTests) drives every operation through the full stack — Initialize → Read → Write → Subscribe → Unsubscribe → SubscribeAlarms → AlarmAck → ReadRaw → ReadProcessed (short-circuit) — proving the wire protocol, dispatcher, capability forwarding, and one-way semantics agree end-to-end. Skipped on Windows administrator shells per the same PipeAcl-denies-Administrators reasoning the IpcHandshakeIntegrationTests use. Full solution 952 pass / 1 pre-existing Phase 0 baseline. Phase 2 evidence doc updated: status header now reads "Streams A+B+C complete... Streams D+E remain — gated only on the iterative Galaxy code lift + parity-debug cycle"; new Update 2026-04-17 (later) callout enumerates the upgrade with explicit "what's left for the Phase 2 exit gate" — replace StubGalaxyBackend with a MxAccessClient-backed implementation calling on the StaPump, then run the v1 IntegrationTests against the v2 topology and iterate on parity defects until green, then delete legacy OtOpcUa.Host.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
> deferred. See `phase-2-galaxy-out-of-process.md` for the full task plan; this is the as-built
|
||||
> delta.
|
||||
|
||||
## Status: **Streams A + B + C scaffolded and test-green. Streams D + E deferred — runtime now in place.**
|
||||
## Status: **Streams A + B + C complete (real Win32 pump, all 9 capability interfaces, end-to-end IPC dispatch). Streams D + E remain — gated only on the iterative Galaxy code lift + parity-debug cycle.**
|
||||
|
||||
The goal per the plan is "parity, not regression" — the phase exit gate requires v1
|
||||
IntegrationTests to pass against the v2 Galaxy.Proxy + Galaxy.Host topology byte-for-byte.
|
||||
@@ -12,6 +12,30 @@ Achieving that requires live MXAccess runtime plus the Galaxy code lift out of t
|
||||
`OtOpcUa.Host`. Without that cycle, deleting the legacy Host would break the 494 passing v1
|
||||
tests that are the parity baseline.
|
||||
|
||||
> **Update 2026-04-17 (later) — Streams A/B/C now feature-complete, not just scaffolds.**
|
||||
> The Win32 message pump in `StaPump` was upgraded from a `BlockingCollection` placeholder to a
|
||||
> real `GetMessage`/`PostThreadMessage`/`PeekMessage` loop lifted from v1 `StaComThread` (P/Invoke
|
||||
> declarations included; `WM_APP=0x8000` for work-item dispatch, `WM_APP+1` for graceful
|
||||
> drain → `PostQuitMessage`, 5s join-on-dispose). `GalaxyProxyDriver` now implements every
|
||||
> capability interface declared in Phase 2 Stream C — `IDriver`, `ITagDiscovery`, `IReadable`,
|
||||
> `IWritable`, `ISubscribable`, `IAlarmSource`, `IHistoryProvider`, `IRediscoverable`,
|
||||
> `IHostConnectivityProbe` — each forwarding through the matching IPC contract. `GalaxyIpcClient`
|
||||
> gained `SendOneWayAsync` for the fire-and-forget calls (unsubscribe / alarm-ack /
|
||||
> close-session) while still serializing through the call-gate so writes don't interleave with
|
||||
> `CallAsync` round-trips. Host side: `IGalaxyBackend` interface defines the seam between IPC
|
||||
> dispatch and the live MXAccess code, `GalaxyFrameHandler` routes every `MessageKind` into it
|
||||
> (heartbeat handled inline so liveness works regardless of backend health), and
|
||||
> `StubGalaxyBackend` returns success for lifecycle/subscribe/recycle and recognizable
|
||||
> `not-implemented`-coded errors for data-plane calls. End-to-end integration tests exercise
|
||||
> every capability through the full stack (handshake → open session → read / write / subscribe /
|
||||
> alarm / history / recycle) and the v1 test baseline stays green (494 pass, no regressions).
|
||||
>
|
||||
> **What's left for the Phase 2 exit gate:** the actual Galaxy code lift (Task B.1) — replace
|
||||
> `StubGalaxyBackend` with a `MxAccessClient`-backed implementation that calls `MxAccessClient`
|
||||
> on the `StaPump`, plus the parity-cycle debugging against live Galaxy that the plan budgets
|
||||
> 3-4 weeks for. Removing the legacy `OtOpcUa.Host` (Task D.1) follows once the parity tests
|
||||
> are green against the v2 topology.
|
||||
|
||||
> **Update 2026-04-17 — runtime confirmed local.** The dev box has the full AVEVA stack required
|
||||
> for the LmxOpcUa breakout: 27 ArchestrA / Wonderware / AVEVA services running including
|
||||
> `aaBootstrap`, `aaGR` (Galaxy Repository), `aaLogger`, `aaUserValidator`, `aaPim`,
|
||||
|
||||
Reference in New Issue
Block a user