rename: prefix gateway projects/namespaces with ZB.MOM.WW + sln→slnx
Apply the ZB.MOM.WW. prefix to all gateway-side projects, folders,
.csproj/.sln contents, C# namespaces, using directives, generated proto
C# (csharp_namespace + checked-in generated files), InternalsVisibleTo
attributes, project-name string literals (LoadProject, .sln lookups,
worker exe paths, staticwebassets manifest), and the install/script/doc
references that point at any of the above. Migrate the solution from
.sln to .slnx via `dotnet sln migrate` and delete the old file.
External-runtime identifiers are intentionally NOT prefixed so external
configuration keeps working:
- GatewayMetrics.cs MeterName ("MxGateway.Server")
- DashboardAuthenticationDefaults Scheme/Policy ("MxGateway.Dashboard")
- GatewayRequestLoggingMiddleware logger category ("MxGateway.Request")
- StaRuntime thread name ("MxGateway.Worker.STA")
- appsettings.json root section "MxGateway" + env-var prefix
MxGateway__... and secret-name MxGateway:ApiKeyPepper
- C:\ProgramData\MxGateway\ data dir paths
Also fixes two tests that were not rename-related but became visible
while validating the rename:
- WorkerLiveMxAccessSmokeTests.ShutDownAsync: cancellation that the
gateway service correctly maps to RpcException(Cancelled) per gRPC
convention was being misclassified as a stream fault. Added a sibling
catch on RpcException with StatusCode.Cancelled.
- IntegrationTestEnvironment.ResolveRepositoryRoot: extracted IsRepositoryRoot
and made it accept either a .git marker OR a .sln/.slnx next to src/
so the worker-exe walker works in non-git working copies.
clients/proto/proto-inputs.json's protoRoot updated to point at
src/ZB.MOM.WW.MxGateway.Contracts/Protos.
Verified by `dotnet build` and a full `dotnet test` of the .slnx with
MXGATEWAY_RUN_LIVE_{MXACCESS,LDAP,GALAXY}_TESTS=1:
Tests: 472/472 pass
Worker.Tests: 280/280 pass (4 dev-rig [Fact(Skip=...)] skipped)
IntegrationTests: 18/18 pass
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -82,6 +82,18 @@ fan-out may be added later with explicit backpressure semantics.
|
||||
Rationale: one subscriber preserves simple event ordering and failure behavior
|
||||
while parity is being proven.
|
||||
|
||||
### Alarms — superseded for the alarm subsystem
|
||||
|
||||
The single-subscriber rule above no longer applies to alarms. The gateway runs
|
||||
an always-on central alarm monitor (`GatewayAlarmMonitor`) that owns one
|
||||
gateway-managed worker session, caches the active-alarm set, and fans it out to
|
||||
any number of clients through the session-less `StreamAlarms` RPC. Per-session
|
||||
alarm auto-subscribe is removed; `AcknowledgeAlarm` is session-less and routes
|
||||
through the monitor. Data-side `StreamEvents` remains one subscriber per
|
||||
session. Rationale: alarm state is gateway-wide, not session-scoped — every
|
||||
client wants the same current set plus updates, and forcing each to own a
|
||||
worker would multiply AVEVA polling load for no benefit.
|
||||
|
||||
## Authentication
|
||||
|
||||
Decision: API key authentication for the public gateway.
|
||||
@@ -199,6 +211,57 @@ and failure behavior are easy to compare against direct MXAccess.
|
||||
|
||||
Batch tag registration can be added later if measured setup latency requires it.
|
||||
|
||||
## Bulk Command Family
|
||||
|
||||
Decision: the gateway exposes a fixed set of *bulk* command kinds —
|
||||
`AddItemBulk`, `AdviseItemBulk`, `RemoveItemBulk`, `UnAdviseItemBulk`,
|
||||
`SubscribeBulk`, `UnsubscribeBulk`, `WriteBulk`, `Write2Bulk`,
|
||||
`WriteSecuredBulk`, `WriteSecured2Bulk`, `ReadBulk` — that carry a list of
|
||||
entries in one round-trip and return one per-entry result. Each command kind
|
||||
runs the corresponding single-item MXAccess COM call sequentially on the
|
||||
worker STA; per-entry failures populate `was_successful = false` with the
|
||||
underlying HRESULT and never throw. There is no transactional / fail-fast
|
||||
semantic — bulk here means "one round-trip, per-entry results", not
|
||||
"atomic".
|
||||
|
||||
Rationale: MXAccess COM itself has no native bulk API for any of these
|
||||
operations. Surfacing the per-entry result list keeps parity transparent —
|
||||
the caller sees the same per-item HRESULT they would see calling MXAccess
|
||||
N times directly — while the bulk shape collapses the gateway/IPC overhead
|
||||
to one round-trip per batch and lets the worker keep the STA hot.
|
||||
|
||||
`ReadBulk` is the only bulk command without a 1:1 MXAccess analogue. Two
|
||||
choices were considered:
|
||||
|
||||
1. **Cache-then-snapshot** (chosen): when a requested tag is already in the
|
||||
session's item registry AND advised, the worker returns the last cached
|
||||
`OnDataChange` value without touching the subscription
|
||||
(`was_cached = true`). Otherwise it takes the full `AddItem + Advise +
|
||||
wait-for-first-OnDataChange + UnAdvise + RemoveItem` lifecycle itself
|
||||
(`was_cached = false`) and leaves the session exactly as it was before
|
||||
the call. The cache lives on a per-session `MxAccessValueCache`,
|
||||
populated by `MxAccessBaseEventSink` on every `OnDataChange` after the
|
||||
event clears the outbound queue.
|
||||
|
||||
2. **Always-snapshot**: take the AddItem-through-RemoveItem lifecycle for
|
||||
every requested tag. Cleaner conceptually but pays the full lifecycle
|
||||
cost on every call and would interfere with existing subscriptions if
|
||||
MXAccess reuses item handles.
|
||||
|
||||
The chosen behavior matches what callers actually want from "current
|
||||
value" — a free read of an already-streaming tag, and a one-shot snapshot
|
||||
otherwise — and never disturbs subscriptions the caller did not create.
|
||||
The decision intentionally does NOT synthesize an `OnDataChange` event
|
||||
from the snapshot path: the snapshot value reaches the caller through
|
||||
`ReadBulk`'s reply payload only, not through the event stream. This
|
||||
preserves the "Don't synthesize events" rule that scopes the rest of the
|
||||
worker.
|
||||
|
||||
`ReadBulk`'s wait loop pumps Windows messages on the worker STA
|
||||
(`StaRuntime.PumpPendingMessages`) on every poll iteration so the inbound
|
||||
MXAccess COM event can dispatch while the bulk executor still holds the
|
||||
thread — without the pump the OnDataChange would never deliver.
|
||||
|
||||
## Graceful Worker Shutdown
|
||||
|
||||
Decision: best-effort cleanup before COM release.
|
||||
|
||||
Reference in New Issue
Block a user