Lands the five concrete code-level follow-ups identified after Phase 7.1:
#1 GalaxyDriver.ReadAsync now works in production. Previously threw
NotSupportedException when no test reader was injected. New path
subscribes through the existing SubscriptionRegistry + EventPump,
waits for the first OnDataChange per item handle (gw pushes the
initial value after SubscribeBulk), then unsubscribes. Tags the gw
rejects up front, or that don't publish before the caller's CT
fires, return Bad-status snapshots in input order so callers still
get one snapshot per requested reference.
#2 ResolveApiKey() routes Gateway.ApiKeySecretRef through three forms:
env:NAME, file:PATH, or literal-string fallback. A future DPAPI arm
slots in here without touching the call site.
#3 GatewayGalaxySubscriber actually honors bufferedUpdateIntervalMs now
(was being silently dropped). Calls SetBufferedUpdateInterval via
the gw's MxCommandKind.SetBufferedUpdateInterval before SubscribeBulk
when the requested interval differs from the cached last-applied
value. Soft-fails on a non-Ok protocol status (the SubscribeBulk
still succeeds at gw cadence).
#4 GalaxyMxAccessOptions.EventPumpChannelCapacity surfaces the bounded-
channel size through DriverConfig JSON, defaulting to 50_000.
#5 Stale doc-comments in HostStatusAggregator and GatewayGalaxySubscriber
describing follow-ups that already shipped.
Tests: +6 (read subscribe-once happy path + rejected-tag fallback;
five resolver scenarios). Total Galaxy driver tests now 180/180 green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Read path scaffold + the byte→uint quality mapping table that the parity
matrix (PR 5.x) pins. PR 4.4 supplies the production GW-backed reader; this
PR ships the abstraction and the supporting infrastructure so 4.4 just
plugs the implementation in.
Files:
- Runtime/StatusCodeMap.cs — explicit OPC DA quality byte → OPC UA
StatusCode uint mapping. Extends the legacy Galaxy.Host
HistorianQualityMapper with named constants (Good / GoodLocalOverride,
Uncertain + 4 substatuses, Bad + 7 substatuses, BadInternalError) and an
MxStatusProxy → uint helper that honors success flag → detail byte →
detected_by transport-error fallback. Unknown bytes fall back to category
bucket with a once-per-session diagnostic log so field captures can
extend the table.
- Runtime/MxValueDecoder.cs — gateway MxValue → boxed CLR value for the
seven Galaxy data types (Boolean, Int32, Int64, Float32, Float64, String,
DateTime) plus their array variants. Honors MxValue.IsNull and
RawValue passthrough.
- Runtime/IGalaxyDataReader.cs — driver-side seam for one-shot reads. PR
4.4 ships the production wrapper around MxGatewaySession.SubscribeBulk +
StreamEvents + UnsubscribeBulk; this PR exposes the contract so
GalaxyDriver.ReadAsync wires through it.
- Runtime/GalaxyMxSession.cs — wrapper around MxGatewaySession that owns
the Register handle. ConnectAsync opens session + Register; AttachForTests
lets tests bypass real gw construction. PR 4.3/4.4/4.5 add write,
subscribe, and reconnect surfaces.
GalaxyDriver:
- Implements IReadable. ReadAsync routes through the injected
IGalaxyDataReader (test seam) when present; production path throws
NotSupportedException pointing at PR 4.4 — protects deployments running
this PR from silent wrong reads while signaling that the legacy-host
backend (Galaxy:Backend=legacy-host) handles reads in the meantime.
- Internal ctor extended with optional dataReader parameter (default null,
preserves PR 4.0/4.1 callers).
Tests: 42 new — exhaustive byte→uint table for StatusCodeMap (15 known
codes + category-bucket fallback for unknowns + MxStatusProxy precedence
rules + OPC UA top-byte invariants), every MxValue oneof case for the
decoder (bool/int32/int64/float/double/string/timestamp/3 array variants/
raw bytes/null), GalaxyDriver IReadable wiring (route-through, empty-
request, no-reader-throws, post-dispose-throws, status-code preservation).
62 Galaxy tests total pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>