docs(audit): apply global term/path substitutions across living docs

This commit is contained in:
Joseph Doherty
2026-06-03 15:50:13 -04:00
parent a60c1e3f66
commit f84e0c3474
16 changed files with 46 additions and 46 deletions
+6 -6
View File
@@ -19,7 +19,7 @@ The worker must do all MXAccess COM calls on its dedicated STA thread, and the S
```powershell ```powershell
# Full solution build (gateway, worker, contracts, tests) # Full solution build (gateway, worker, contracts, tests)
dotnet build src/MxGateway.sln dotnet build src/ZB.MOM.WW.MxGateway.slnx
# Worker must be built x86 — the gateway looks for MxGateway.Worker.exe under bin\x86 # Worker must be built x86 — the gateway looks for MxGateway.Worker.exe under bin\x86
dotnet build src/MxGateway.Worker/MxGateway.Worker.csproj -p:Platform=x86 dotnet build src/MxGateway.Worker/MxGateway.Worker.csproj -p:Platform=x86
@@ -29,10 +29,10 @@ dotnet test src/MxGateway.Tests/MxGateway.Tests.csproj
dotnet test src/MxGateway.Worker.Tests/MxGateway.Worker.Tests.csproj -p:Platform=x86 dotnet test src/MxGateway.Worker.Tests/MxGateway.Worker.Tests.csproj -p:Platform=x86
# Run gateway locally (defaults bound under MxGateway:* in src/MxGateway.Server/appsettings.json) # Run gateway locally (defaults bound under MxGateway:* in src/MxGateway.Server/appsettings.json)
dotnet run --project src/MxGateway.Server/MxGateway.Server.csproj dotnet run --project src/ZB.MOM.WW.MxGateway.Server/ZB.MOM.WW.MxGateway.Server.csproj
# API-key admin CLI (same exe, "apikey" subcommand) # API-key admin CLI (same exe, "apikey" subcommand)
dotnet run --project src/MxGateway.Server/MxGateway.Server.csproj -- apikey create --display-name "dev" --scopes session,invoke,event,metadata,admin dotnet run --project src/ZB.MOM.WW.MxGateway.Server/ZB.MOM.WW.MxGateway.Server.csproj -- apikey create --display-name "dev" --scopes session,invoke,event,metadata,admin
``` ```
Single test by name (xUnit `--filter`): Single test by name (xUnit `--filter`):
@@ -54,7 +54,7 @@ Live LDAP tests use `MXGATEWAY_RUN_LIVE_LDAP_TESTS=1`. See `docs/GatewayTesting.
Each language client is in `clients/<lang>/` with its own README. They all consume the shared `.proto` files in `src/MxGateway.Contracts/Protos`: Each language client is in `clients/<lang>/` with its own README. They all consume the shared `.proto` files in `src/MxGateway.Contracts/Protos`:
- `clients/dotnet`: `dotnet build clients/dotnet/MxGateway.Client.sln` - `clients/dotnet`: `dotnet build clients/dotnet/ZB.MOM.WW.MxGateway.Client.slnx`
- `clients/python`: `python -m pip install -e ".[dev]"; python -m pytest` - `clients/python`: `python -m pip install -e ".[dev]"; python -m pytest`
- `clients/rust`: `cargo test --workspace; cargo clippy --workspace --all-targets -- -D warnings` - `clients/rust`: `cargo test --workspace; cargo clippy --workspace --all-targets -- -D warnings`
- `clients/java`: `gradle test` (Java 21) - `clients/java`: `gradle test` (Java 21)
@@ -90,7 +90,7 @@ When source code changes, build and test the affected component before reporting
| Contracts or `.proto` files | regenerate generated code, then build gateway, worker, and every generated client touched by the contract | | Contracts or `.proto` files | regenerate generated code, then build gateway, worker, and every generated client touched by the contract |
| Gateway server, sessions, workers, gRPC, dashboard, metrics | `dotnet build src/MxGateway.Server` and run affected gateway / fake-worker tests | | Gateway server, sessions, workers, gRPC, dashboard, metrics | `dotnet build src/MxGateway.Server` and run affected gateway / fake-worker tests |
| Worker IPC, STA, MXAccess, conversion | `dotnet build src/MxGateway.Worker -p:Platform=x86` and run worker tests | | Worker IPC, STA, MXAccess, conversion | `dotnet build src/MxGateway.Worker -p:Platform=x86` and run worker tests |
| .NET client | `dotnet build clients/dotnet/MxGateway.Client.sln` and run its tests | | .NET client | `dotnet build clients/dotnet/ZB.MOM.WW.MxGateway.Client.slnx` and run its tests |
| Go client | `gofmt`, `go build ./...`, `go test ./...` from `clients/go` | | Go client | `gofmt`, `go build ./...`, `go test ./...` from `clients/go` |
| Rust client | `cargo fmt`, `cargo check --workspace`, `cargo test --workspace`, `cargo clippy --all-targets -- -D warnings` from `clients/rust` | | Rust client | `cargo fmt`, `cargo check --workspace`, `cargo test --workspace`, `cargo clippy --all-targets -- -D warnings` from `clients/rust` |
| Python client | `python -m pytest` from `clients/python` | | Python client | `python -m pytest` from `clients/python` |
@@ -116,7 +116,7 @@ External analysis sources referenced by design docs:
Gateway gRPC clients authenticate with an API key in metadata: `authorization: Bearer mxgw_<key-id>_<secret>`. Keys are stored hashed (with a peppered SHA) in a gateway-owned SQLite DB (default `C:\ProgramData\MxGateway\gateway-auth.db`). Scopes (`session`, `invoke`, `event`, `metadata`, `admin`) gate specific RPCs; missing → `Unauthenticated`, insufficient → `PermissionDenied`. The `apikey` subcommand on the server exe manages keys; see `src/MxGateway.Server/Security/Authentication/`. Gateway gRPC clients authenticate with an API key in metadata: `authorization: Bearer mxgw_<key-id>_<secret>`. Keys are stored hashed (with a peppered SHA) in a gateway-owned SQLite DB (default `C:\ProgramData\MxGateway\gateway-auth.db`). Scopes (`session`, `invoke`, `event`, `metadata`, `admin`) gate specific RPCs; missing → `Unauthenticated`, insufficient → `PermissionDenied`. The `apikey` subcommand on the server exe manages keys; see `src/MxGateway.Server/Security/Authentication/`.
Dashboard auth is LDAP-backed (separate from the gRPC API-key model). `/login` binds against `MxGateway:Ldap` and maps the user's LDAP groups to `Admin` or `Viewer` via `MxGateway:Dashboard:GroupToRole`, then issues an HTTP-only secure `__Host-MxGatewayDashboard` cookie. SignalR hubs at `/hubs/{snapshot,alarms,events}` accept either the cookie or a 30-minute bearer minted at `/hubs/token`. `Dashboard:AllowAnonymousLocalhost` bypasses auth on loopback when enabled. Dashboard auth is LDAP-backed (separate from the gRPC API-key model). `/login` binds against `MxGateway:Ldap` and maps the user's LDAP groups to `Administrator` or `Viewer` via `MxGateway:Dashboard:GroupToRole`, then issues an HTTP-only secure `MxGatewayDashboard` cookie. SignalR hubs at `/hubs/{snapshot,alarms,events}` accept either the cookie or a 30-minute bearer minted at `/hubs/token`. `Dashboard:AllowAnonymousLocalhost` bypasses auth on loopback when enabled.
## Process / Platform Notes ## Process / Platform Notes
+1 -1
View File
@@ -212,7 +212,7 @@ Use bounded smoke flow and always attempt `close_session` in `finally`.
Use `pyproject.toml`. Publishable package name should be stable, for example: Use `pyproject.toml`. Publishable package name should be stable, for example:
```text ```text
mxaccess-gateway-client zb-mom-ww-mxaccess-gateway-client
``` ```
Generated protobuf code should be regenerated through a documented command, not Generated protobuf code should be regenerated through a documented command, not
+1 -1
View File
@@ -407,7 +407,7 @@ The stable client proto manifest defines the generated-code directories:
clients/dotnet/generated clients/dotnet/generated
clients/go/internal/generated clients/go/internal/generated
clients/rust/src/generated clients/rust/src/generated
clients/python/src/mxgateway/generated clients/python/src/zb_mom_ww_mxgateway/generated
clients/java/src/main/generated clients/java/src/main/generated
``` ```
+9 -9
View File
@@ -113,7 +113,7 @@ Pop-Location
## Rust ## Rust
The Rust workspace builds the `mxgateway-client` library crate and the `mxgw` The Rust workspace builds the `zb-mom-ww-mxgateway-client` library crate and the `mxgw`
CLI crate. `build.rs` generates `tonic` and `prost` modules into Cargo build CLI crate. `build.rs` generates `tonic` and `prost` modules into Cargo build
output on each build that needs updated protobuf output. output on each build that needs updated protobuf output.
@@ -156,8 +156,8 @@ Pop-Location
## Python ## Python
The Python package is `mxaccess-gateway-client`. Generated modules live under The Python package is `zb-mom-ww-mxaccess-gateway-client`. Generated modules live under
`clients/python/src/mxgateway/generated`. `clients/python/src/zb_mom_ww_mxgateway/generated`.
Regenerate the Python bindings: Regenerate the Python bindings:
@@ -184,7 +184,7 @@ Push-Location clients/python
mxgw-py version --json mxgw-py version --json
mxgw-py smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json mxgw-py smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json
mxgw-py smoke --endpoint mxgateway.example.local:5001 --tls --ca-file C:\certs\mxgateway-ca.pem --server-name-override mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json mxgw-py smoke --endpoint mxgateway.example.local:5001 --tls --ca-file C:\certs\mxgateway-ca.pem --server-name-override mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json
python -m mxgateway_cli version --json python -m zb_mom_ww_mxgateway_cli version --json
Pop-Location Pop-Location
``` ```
@@ -198,7 +198,7 @@ Regenerate Java bindings:
```powershell ```powershell
Push-Location clients/java Push-Location clients/java
gradle :mxgateway-client:generateProto gradle :zb-mom-ww-mxgateway-client:generateProto
Pop-Location Pop-Location
``` ```
@@ -214,7 +214,7 @@ Create local library and CLI artifacts:
```powershell ```powershell
Push-Location clients/java Push-Location clients/java
gradle :mxgateway-client:jar :mxgateway-cli:installDist gradle :zb-mom-ww-mxgateway-client:jar :zb-mom-ww-mxgateway-cli:installDist
Pop-Location Pop-Location
``` ```
@@ -222,9 +222,9 @@ Run the CLI through Gradle:
```powershell ```powershell
Push-Location clients/java Push-Location clients/java
gradle :mxgateway-cli:run --args="version --json" gradle :zb-mom-ww-mxgateway-cli:run --args="version --json"
gradle :mxgateway-cli:run --args="smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json" gradle :zb-mom-ww-mxgateway-cli:run --args="smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json"
gradle :mxgateway-cli:run --args="smoke --endpoint mxgateway.example.local:5001 --ca-file C:\certs\mxgateway-ca.pem --server-name-override mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json" gradle :zb-mom-ww-mxgateway-cli:run --args="smoke --endpoint mxgateway.example.local:5001 --ca-file C:\certs\mxgateway-ca.pem --server-name-override mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json"
Pop-Location Pop-Location
``` ```
+2 -2
View File
@@ -77,7 +77,7 @@ The manifest declares these generated-code directories:
| .NET | `clients/dotnet/generated` | | .NET | `clients/dotnet/generated` |
| Go | `clients/go/internal/generated` | | Go | `clients/go/internal/generated` |
| Rust | `clients/rust/src/generated` | | Rust | `clients/rust/src/generated` |
| Python | `clients/python/src/mxgateway/generated` | | Python | `clients/python/src/zb_mom_ww_mxgateway/generated` |
| Java | `clients/java/src/main/generated` | | Java | `clients/java/src/main/generated` |
Only generator output belongs in these directories. Handwritten client wrappers Only generator output belongs in these directories. Handwritten client wrappers
@@ -142,7 +142,7 @@ cargo check --workspace
``` ```
Python clients should use `grpc_tools.protoc` and write generated modules under Python clients should use `grpc_tools.protoc` and write generated modules under
`clients/python/src/mxgateway/generated` so imports stay separate from `clients/python/src/zb_mom_ww_mxgateway/generated` so imports stay separate from
handwritten async wrappers. handwritten async wrappers.
The Python scaffold provides a repo-local generation script: The Python scaffold provides a repo-local generation script:
+1 -1
View File
@@ -288,7 +288,7 @@ Use this checklist when applying the design to another project:
- Use top-bordered sections for page groups instead of nested cards. - Use top-bordered sections for page groups instead of nested cards.
- Centralize formatting and redaction outside Razor markup. - Centralize formatting and redaction outside Razor markup.
- Hide every destructive admin affordance from viewers; render it only for - Hide every destructive admin affordance from viewers; render it only for
the `Admin` role and re-check the role server-side on every invocation. the `Administrator` role and re-check the role server-side on every invocation.
- Route every destructive action (Close session, Kill worker, Rotate / - Route every destructive action (Close session, Kill worker, Rotate /
Revoke / Delete API key) through the shared `ConfirmDialog` component so Revoke / Delete API key) through the shared `ConfirmDialog` component so
the operator always gets one explicit confirmation step before the call the operator always gets one explicit confirmation step before the call
+2 -2
View File
@@ -162,7 +162,7 @@ public static IApplicationBuilder UseGatewayRequestLoggingScope(this IApplicatio
{ {
ILogger logger = context.RequestServices ILogger logger = context.RequestServices
.GetRequiredService<ILoggerFactory>() .GetRequiredService<ILoggerFactory>()
.CreateLogger("ZB.MOM.WW.MxGateway.Request"); .CreateLogger("MxGateway.Request");
using IDisposable? scope = logger.BeginGatewayScope(new GatewayLogScope( using IDisposable? scope = logger.BeginGatewayScope(new GatewayLogScope(
SessionId: ReadHeader(context, SessionIdHeaderName), SessionId: ReadHeader(context, SessionIdHeaderName),
@@ -188,7 +188,7 @@ The scope is keyed off four custom headers and the standard `authorization` head
The numeric headers use `int.TryParse` and `ulong.TryParse`; missing or unparseable values become `null` and are dropped by `GatewayLogScope.ToDictionary`. This keeps the middleware tolerant of clients that do not yet emit every header, which matters because the earliest call in a session (`OpenSession`) has no `SessionId` to send. The numeric headers use `int.TryParse` and `ulong.TryParse`; missing or unparseable values become `null` and are dropped by `GatewayLogScope.ToDictionary`. This keeps the middleware tolerant of clients that do not yet emit every header, which matters because the earliest call in a session (`OpenSession`) has no `SessionId` to send.
The logger category is `ZB.MOM.WW.MxGateway.Request`, which lets operators filter the request scope events independently from per-component categories. The logger category is `MxGateway.Request`, which lets operators filter the request scope events independently from per-component categories.
### Pipeline ordering ### Pipeline ordering
+3 -3
View File
@@ -419,17 +419,17 @@ embedded in the status detail.
The gateway's Blazor dashboard surfaces a Galaxy summary in two places: The gateway's Blazor dashboard surfaces a Galaxy summary in two places:
- An overview card on `/dashboard` showing connectivity status, last deploy - An overview card on `/` showing connectivity status, last deploy
timestamp, object count (with area count), attribute total, historized and timestamp, object count (with area count), attribute total, historized and
alarm counts, and last successful refresh. alarm counts, and last successful refresh.
- A dedicated `/dashboard/galaxy` page with object-category and top-template - A dedicated `/galaxy` page with object-category and top-template
breakdowns plus a Sync Info table covering last successful refresh, last breakdowns plus a Sync Info table covering last successful refresh, last
attempt, refresh interval, redacted connection string, and command timeout. attempt, refresh interval, redacted connection string, and command timeout.
Both views are projected from the same `IGalaxyHierarchyCache` that backs the Both views are projected from the same `IGalaxyHierarchyCache` that backs the
gRPC service. The dashboard does not run its own refresh — when the gRPC service. The dashboard does not run its own refresh — when the
background `GalaxyHierarchyRefreshService` updates the cache, both the background `GalaxyHierarchyRefreshService` updates the cache, both the
overview card and the `/dashboard/galaxy` page pick up the new state on the overview card and the `/galaxy` page pick up the new state on the
next dashboard tick. When SQL is unreachable, the cache retains the previous next dashboard tick. When SQL is unreachable, the cache retains the previous
data and flips `Status` to `Stale` or `Unavailable`; the dashboard surfaces data and flips `Status` to `Stale` or `Unavailable`; the dashboard surfaces
that as a yellow or red status badge plus the truncated error. that as a yellow or red status badge plus the truncated error.
+4 -4
View File
@@ -52,7 +52,7 @@ paths, timeouts, queue sizes, enum values, or protocol values are invalid.
"RecentSessionLimit": 200, "RecentSessionLimit": 200,
"ShowTagValues": false, "ShowTagValues": false,
"GroupToRole": { "GroupToRole": {
"GwAdmin": "Admin", "GwAdmin": "Administrator",
"GwReader": "Viewer" "GwReader": "Viewer"
} }
}, },
@@ -153,7 +153,7 @@ the affected stream while the MXAccess session remains active.
| `MxGateway:Dashboard:RecentFaultLimit` | `100` | Maximum number of fault summaries projected into each dashboard snapshot. | | `MxGateway:Dashboard:RecentFaultLimit` | `100` | Maximum number of fault summaries projected into each dashboard snapshot. |
| `MxGateway:Dashboard:RecentSessionLimit` | `200` | Maximum number of session summaries projected into each dashboard snapshot. | | `MxGateway:Dashboard:RecentSessionLimit` | `200` | Maximum number of session summaries projected into each dashboard snapshot. |
| `MxGateway:Dashboard:ShowTagValues` | `false` | Reserved display control for tag values. The dashboard does not show full tag values by default. | | `MxGateway:Dashboard:ShowTagValues` | `false` | Reserved display control for tag values. The dashboard does not show full tag values by default. |
| `MxGateway:Dashboard:GroupToRole` | _(empty)_ | LDAP group → dashboard role mapping. Keys are LDAP group names (short CN or full DN — leading-RDN match). Values must be `Admin` (read/write, API-key CRUD) or `Viewer` (read-only). A user whose LDAP groups don't intersect this map cannot sign in; with no mapping at all, only the loopback bypass admits anyone. | | `MxGateway:Dashboard:GroupToRole` | _(empty)_ | LDAP group → dashboard role mapping. Keys are LDAP group names (short CN or full DN — leading-RDN match). Values must be `Administrator` (read/write, API-key CRUD) or `Viewer` (read-only). A user whose LDAP groups don't intersect this map cannot sign in; with no mapping at all, only the loopback bypass admits anyone. |
`SnapshotIntervalMilliseconds` must be greater than zero. `RecentFaultLimit` `SnapshotIntervalMilliseconds` must be greater than zero. `RecentFaultLimit`
and `RecentSessionLimit` must be greater than or equal to zero. and `RecentSessionLimit` must be greater than or equal to zero.
@@ -166,10 +166,10 @@ users) but practical deployments populate at least one Admin group.
Three authorization policies are registered out of these options: Three authorization policies are registered out of these options:
- `MxGateway.Dashboard.Viewer` — gates the Razor component routes. Satisfied by - `MxGateway.Dashboard.Viewer` — gates the Razor component routes. Satisfied by
either dashboard role (Admin or Viewer), by `AllowAnonymousLocalhost` on either dashboard role (Administrator or Viewer), by `AllowAnonymousLocalhost` on
loopback, or by `Authentication.Mode = Disabled`. loopback, or by `Authentication.Mode = Disabled`.
- `MxGateway.Dashboard.Admin` — gates write-capable surfaces (API-key CRUD). - `MxGateway.Dashboard.Admin` — gates write-capable surfaces (API-key CRUD).
Satisfied only by the Admin role (same environmental bypasses). Satisfied only by the Administrator role (same environmental bypasses).
- `MxGateway.Dashboard.HubClients` — attached to the SignalR hubs. Accepts - `MxGateway.Dashboard.HubClients` — attached to the SignalR hubs. Accepts
either the dashboard cookie scheme or the `MxGateway.Dashboard.HubToken` either the dashboard cookie scheme or the `MxGateway.Dashboard.HubToken`
bearer scheme (used by SignalR's WebSocket upgrade path where the HttpOnly bearer scheme (used by SignalR's WebSocket upgrade path where the HttpOnly
+4 -4
View File
@@ -288,7 +288,7 @@ it opt-in and redacted.
### Browse page ### Browse page
`/dashboard/browse` lets an operator explore the Galaxy tag hierarchy and watch `/browse` lets an operator explore the Galaxy tag hierarchy and watch
live values. The tree is built in-process by `DashboardBrowseTreeBuilder` from live values. The tree is built in-process by `DashboardBrowseTreeBuilder` from
`IGalaxyHierarchyCache.Current` — the same cache the Galaxy page reads — so a `IGalaxyHierarchyCache.Current` — the same cache the Galaxy page reads — so a
render costs no gRPC call and no SQL round-trip. Each node shows its child render costs no gRPC call and no SQL round-trip. Each node shows its child
@@ -306,7 +306,7 @@ diagnostic session/worker views.
### Alarms page ### Alarms page
`/dashboard/alarms` lists the alarms the gateway's central alarm monitor `/alarms` lists the alarms the gateway's central alarm monitor
currently holds as Active or ActiveAcked, refreshed every three seconds. It currently holds as Active or ActiveAcked, refreshed every three seconds. It
defaults to showing unacknowledged `Active` alarms; filters add acknowledged defaults to showing unacknowledged `Active` alarms; filters add acknowledged
alarms and narrow by area, severity range, and a reference/source/description alarms and narrow by area, severity range, and a reference/source/description
@@ -335,7 +335,7 @@ the monitor never starts and the cache stays empty.
### API keys page ### API keys page
`/dashboard/apikeys` lists the gateway's API keys and, for authorized `/apikeys` lists the gateway's API keys and, for authorized
operators, manages them. It reads key metadata through the same operators, manages them. It reads key metadata through the same
`IApiKeyAdminStore` the `apikey` CLI uses, so the dashboard and the CLI act `IApiKeyAdminStore` the `apikey` CLI uses, so the dashboard and the CLI act
on one source of truth. on one source of truth.
@@ -421,7 +421,7 @@ Implemented behavior:
`ClaimTypes.Role` claims, alongside the per-group `mxgateway:ldap_group` `ClaimTypes.Role` claims, alongside the per-group `mxgateway:ldap_group`
claims; claims;
- a successful login signs in the `MxGateway.Dashboard` cookie scheme - a successful login signs in the `MxGateway.Dashboard` cookie scheme
(`__Host-MxGatewayDashboard`, HttpOnly, SameSite=Strict, Secure); (`MxGatewayDashboard`, HttpOnly, SameSite=Strict, Secure);
- a user with no matching group cannot sign in — the login screen returns the - a user with no matching group cannot sign in — the login screen returns the
generic credential-rejected message; generic credential-rejected message;
- antiforgery tokens guard the login and logout POSTs. - antiforgery tokens guard the login and logout POSTs.
+6 -6
View File
@@ -247,12 +247,12 @@ Technology:
Suggested routes: Suggested routes:
```text ```text
/dashboard /
/dashboard/sessions /sessions
/dashboard/sessions/{sessionId} /sessions/{sessionId}
/dashboard/workers /workers
/dashboard/events /events
/dashboard/settings /settings
``` ```
Dashboard pages: Dashboard pages:
+1 -1
View File
@@ -320,7 +320,7 @@ writes its `{"error":...}` envelope and the loop continues; the harness treats
that envelope as the operation failure (used by the parity and auth phases). that envelope as the operation failure (used by the parity and auth phases).
Before the per-client phases run, the script builds the .NET CLI Before the per-client phases run, the script builds the .NET CLI
(`dotnet build`) and installs the Java CLI (`gradle :mxgateway-cli:installDist`) (`dotnet build`) and installs the Java CLI (`gradle :zb-mom-ww-mxgateway-cli:installDist`)
once, so the `batch` process launches straight from the compiled exe / the once, so the `batch` process launches straight from the compiled exe / the
installed launcher. The Go, Rust, and Python batch processes are launched via installed launcher. The Go, Rust, and Python batch processes are launched via
`go run` / `cargo run` / `python -m`, which compile-or-start once when that `go run` / `cargo run` / `python -m`, which compile-or-start once when that
+1 -1
View File
@@ -251,7 +251,7 @@ The loop should update a heartbeat timestamp after:
- processing an MXAccess event. - processing an MXAccess event.
`StaRuntime` implements this runtime boundary in the worker. It starts one `StaRuntime` implements this runtime boundary in the worker. It starts one
background thread named `ZB.MOM.WW.MxGateway.Worker.STA`, sets it to `ApartmentState.STA`, background thread named `MxGateway.Worker.STA`, sets it to `ApartmentState.STA`,
initializes COM through `StaComApartmentInitializer`, and runs initializes COM through `StaComApartmentInitializer`, and runs
`StaMessagePump`. Commands are scheduled through `InvokeAsync`; the command `StaMessagePump`. Commands are scheduled through `InvokeAsync`; the command
queue signals an `AutoResetEvent` so `MsgWaitForMultipleObjectsEx` can wake the queue signals an `AutoResetEvent` so `MsgWaitForMultipleObjectsEx` can wake the
+2 -2
View File
@@ -20,13 +20,13 @@ The installed MXAccess interop assembly declares an `Apartment` threading model
## STA Thread Initialization ## STA Thread Initialization
`StaRuntime`'s constructor configures a background `Thread` named `ZB.MOM.WW.MxGateway.Worker.STA` and forces it into `ApartmentState.STA` before the thread starts. `Start()` releases the thread and then blocks on `startedEvent` so callers observe a fully-initialized apartment (or a captured `startupException`) before the first `InvokeAsync` call: `StaRuntime`'s constructor configures a background `Thread` named `MxGateway.Worker.STA` and forces it into `ApartmentState.STA` before the thread starts. `Start()` releases the thread and then blocks on `startedEvent` so callers observe a fully-initialized apartment (or a captured `startupException`) before the first `InvokeAsync` call:
```csharp ```csharp
staThread = new Thread(ThreadMain) staThread = new Thread(ThreadMain)
{ {
IsBackground = true, IsBackground = true,
Name = "ZB.MOM.WW.MxGateway.Worker.STA" Name = "MxGateway.Worker.STA"
}; };
staThread.SetApartmentState(ApartmentState.STA); staThread.SetApartmentState(ApartmentState.STA);
``` ```
+1 -1
View File
@@ -22,7 +22,7 @@ library, CLI, and tests.
## Packages ## Packages
- Use lowercase package names under `com.dohertylan.mxgateway`. - Use lowercase package names under `com.zb.mom.ww.mxgateway`.
- Keep client library code separate from CLI code. - Keep client library code separate from CLI code.
- Keep generated protobuf classes in a generated package. - Keep generated protobuf classes in a generated package.
- Do not expose implementation-only transport helpers as public API. - Do not expose implementation-only transport helpers as public API.
+2 -2
View File
@@ -24,8 +24,8 @@ CLI, and tests.
## Package Structure ## Package Structure
- Put library code under `src/mxgateway/`. - Put library code under `src/zb_mom_ww_mxgateway/`.
- Put CLI entry points under `src/mxgateway_cli/`. - Put CLI entry points under `src/zb_mom_ww_mxgateway_cli/`.
- Keep generated protobuf modules under a clearly named `generated` package. - Keep generated protobuf modules under a clearly named `generated` package.
- Avoid import side effects that open channels, read environment variables, or - Avoid import side effects that open channels, read environment variables, or
start background tasks. start background tasks.