docs(audit): apply per-cluster judgment fixes across living docs
Resolve audit findings: correct WorkerEnvelope proto/route/metric/session facts; rewrite auth (ZB.MOM.WW.Auth migration), dashboard (ZB.MOM.WW.Theme), and StyleGuide (foreign-project copy-paste); document alarm subsystem, Ldap options, and gateway alarm broker; fix client CLI flags and package paths.
This commit is contained in:
@@ -94,9 +94,11 @@ Expected protected environment values:
|
||||
|
||||
```text
|
||||
MXGATEWAY_WORKER_NONCE=<random nonce>
|
||||
MXGATEWAY_WORKER_LOG_CONTEXT=<optional context>
|
||||
```
|
||||
|
||||
The nonce travels through the environment rather than the command line so it
|
||||
never appears in process-listing tools that expose argument vectors.
|
||||
|
||||
Startup sequence:
|
||||
|
||||
1. Parse command-line arguments.
|
||||
@@ -114,16 +116,26 @@ Startup sequence:
|
||||
If validation fails before MXAccess creation, exit quickly with a non-zero exit
|
||||
code. If MXAccess creation fails, send `WorkerFault` when possible and exit.
|
||||
|
||||
The bootstrap layer returns structured exit codes before it creates pipes,
|
||||
starts the STA, or touches MXAccess:
|
||||
`WorkerApplication.Run` returns one of the structured `WorkerExitCode` values.
|
||||
Codes `2`–`4` are produced by the bootstrap parse phase before any pipe, STA, or
|
||||
MXAccess work happens; codes `5`–`6` and a clean `0` only become reachable once
|
||||
the parse succeeds and the worker runs its pipe session:
|
||||
|
||||
| Exit code | Name | Meaning |
|
||||
|-----------|------|---------|
|
||||
| `0` | `Success` | Required bootstrap options are valid. |
|
||||
| `0` | `Success` | The pipe session ran to a clean close. |
|
||||
| `1` | `UnexpectedFailure` | A non-bootstrap exception reaches the process boundary. |
|
||||
| `2` | `InvalidArguments` | Required arguments are missing or unknown arguments are present. |
|
||||
| `3` | `InvalidProtocolVersion` | `--protocol-version` is not numeric or does not match the supported worker protocol. |
|
||||
| `4` | `MissingNonce` | `MXGATEWAY_WORKER_NONCE` is absent or empty. |
|
||||
| `5` | `PipeConnectionFailed` | The pipe connection raised an `IOException` or `TimeoutException`. |
|
||||
| `6` | `ProtocolViolation` | A `WorkerFrameProtocolException` escaped the pipe session. |
|
||||
|
||||
`WorkerBootstrapResult.Succeeded` is a separate parse-phase gate: it reports
|
||||
whether argument parsing produced usable `WorkerOptions`. A `false` result
|
||||
carries one of codes `2`–`4` and the worker exits before running a session, so a
|
||||
successful parse is distinct from the `0` exit code, which only follows a clean
|
||||
pipe-session close.
|
||||
|
||||
Bootstrap logs use `WorkerConsoleLogger` key/value output. `WorkerLogRedactor`
|
||||
redacts fields whose names indicate nonce, secret, password, token,
|
||||
@@ -133,30 +145,35 @@ credential, or API key values before the message is written.
|
||||
|
||||
```text
|
||||
ZB.MOM.WW.MxGateway.Worker
|
||||
Program
|
||||
Program (calls WorkerApplication.Run)
|
||||
WorkerApplication (parse, bootstrap, run pipe session, map exit code)
|
||||
Bootstrap
|
||||
WorkerOptionsParser (parse args + env into WorkerOptions)
|
||||
WorkerOptions
|
||||
WorkerHost
|
||||
WorkerBootstrapResult (parse outcome + WorkerExitCode)
|
||||
WorkerExitCode
|
||||
WorkerConsoleLogger / WorkerLogRedactor
|
||||
Ipc
|
||||
PipeClient
|
||||
FrameReader
|
||||
FrameWriter
|
||||
WorkerProtocol
|
||||
WorkerPipeClient (named-pipe connect + retry, owns the session)
|
||||
WorkerPipeSession (handshake, read/write/drain/heartbeat loops)
|
||||
WorkerFrameReader / WorkerFrameWriter
|
||||
WorkerEnvelopeValidator
|
||||
WorkerContractInfo (protocol version + descriptor names)
|
||||
Sta
|
||||
StaRuntime
|
||||
StaCommandQueue
|
||||
MessagePump
|
||||
StaWatchdog
|
||||
StaRuntime (the dedicated STA thread + message pump loop)
|
||||
StaCommandDispatcher
|
||||
StaMessagePump
|
||||
MxAccess
|
||||
MxAccessSession
|
||||
MxAccessCommandDispatcher
|
||||
MxAccessEventSink
|
||||
MxAccessStaSession (IWorkerRuntimeSession over the STA)
|
||||
MxAccessSession (handle registry + COM-call orchestration)
|
||||
MxAccessCommandExecutor (IStaCommandExecutor; runs commands on the STA)
|
||||
MxAccessBaseEventSink (OnDataChange tag-data events)
|
||||
MxAccessHandleRegistry
|
||||
(alarm subsystem — see below)
|
||||
Conversion
|
||||
VariantConverter
|
||||
SafeArrayConverter
|
||||
StatusProxyConverter
|
||||
HResultMapper
|
||||
VariantConverter (MxValue <-> COM VARIANT, both directions)
|
||||
MxStatusProxyConverter
|
||||
HResultConverter / HResultConversion
|
||||
```
|
||||
|
||||
## Threading Model
|
||||
@@ -330,13 +347,19 @@ cleanup path completes.
|
||||
|
||||
## Event Sink
|
||||
|
||||
The worker must subscribe to every public MXAccess event family:
|
||||
The worker subscribes to every public MXAccess event family through
|
||||
`MxAccessBaseEventSink`:
|
||||
|
||||
- `OnDataChange`
|
||||
- `OnWriteComplete`
|
||||
- `OperationComplete`
|
||||
- `OnBufferedDataChange`
|
||||
|
||||
Alarm transitions arrive on a separate path. They do not originate from the
|
||||
`LMXProxyServerClass` connection points, so `MxAccessAlarmEventSink` (driven by
|
||||
the alarm subsystem below) feeds them onto the same `MxAccessEventQueue` rather
|
||||
than `MxAccessBaseEventSink`.
|
||||
|
||||
Forward these event families only when the native MXAccess COM object raises
|
||||
them. Do not synthesize `OperationComplete` from write completion or command
|
||||
status. `OnBufferedDataChange` must be represented in the protocol now, but
|
||||
@@ -368,16 +391,49 @@ type on buffered events. `OperationComplete` is only emitted from the native
|
||||
`MxAccessEventQueue` is the bounded outbound event queue for one worker
|
||||
session. It assigns the monotonic `WorkerSequence` and `WorkerTimestamp` when an
|
||||
event is accepted, preserving the order in which MXAccess handlers enqueue
|
||||
events. The default capacity is `10000`. When the queue reaches capacity it
|
||||
records a `WorkerFaultCategory.QueueOverflow` fault and rejects further events.
|
||||
The event handler catches conversion and enqueue failures, records the first
|
||||
fault on the queue, and returns to the STA message pump instead of writing to
|
||||
the pipe.
|
||||
events. The default capacity is `10000`. When the queue reaches capacity, `Enqueue`
|
||||
records a `WorkerFaultCategory.QueueOverflow` fault and then throws
|
||||
`MxAccessEventQueueOverflowException` so the caller cannot silently drop the
|
||||
event. The event handler catches conversion and enqueue failures (including this
|
||||
overflow exception), records the first fault on the queue, and returns to the
|
||||
STA message pump instead of writing to the pipe.
|
||||
|
||||
If event conversion throws, catch it inside the event handler, record a
|
||||
structured `WorkerFault`, and keep the worker alive only if the fault policy
|
||||
allows it.
|
||||
|
||||
## Alarm Subsystem
|
||||
|
||||
Alarms come from a different COM surface than tag data, so the worker carries a
|
||||
separate pipeline rather than folding alarms into `MxAccessBaseEventSink`. The
|
||||
MXAccess `LMXProxyServerClass` does not expose alarm subscription, so the worker
|
||||
hosts AVEVA's standalone alarm-consumer COM object instead.
|
||||
|
||||
- `WnWrapAlarmConsumer` is the production `IMxAccessAlarmConsumer`, backed by
|
||||
`WNWRAPCONSUMERLib.wwAlarmConsumerClass`. It returns the active alarm set as a
|
||||
BSTR XML string through `GetXmlCurrentAlarms2`, which avoids the FILETIME→
|
||||
`DateTime` marshaling that crashed the earlier managed alarm client. The CLSID
|
||||
is registered `ThreadingModel=Apartment`, so the consumer is created and
|
||||
driven entirely on the worker's STA. It owns no internal timer.
|
||||
- `MxAccessStaSession` drives the **STA alarm poll loop**: `RunAlarmPollLoopAsync`
|
||||
awaits a fixed `500 ms` interval and then calls `IAlarmCommandHandler.PollOnce`
|
||||
on the STA via the runtime, so every `GetXmlCurrentAlarms2` call stays on the
|
||||
apartment that owns the consumer. A poll failure is recorded as a
|
||||
`WorkerFault` on the event queue rather than terminating the worker.
|
||||
- `AlarmCommandHandler` owns one `AlarmDispatcher` per session and is the entry
|
||||
point for the alarm IPC commands (`SubscribeAlarms`, `AcknowledgeAlarm` by GUID
|
||||
or name, `QueryActiveAlarms`, `Unsubscribe`). It rejects a second subscribe
|
||||
before an unsubscribe, mirroring the consumer's non-idempotent `Subscribe`.
|
||||
- `AlarmDispatcher` wires the consumer's `AlarmTransitionEmitted` stream onto
|
||||
`MxAccessAlarmEventSink.EnqueueTransition`. It maps state transitions through
|
||||
`AlarmRecordTransitionMapper`, composes the canonical
|
||||
`\\<machine>\Galaxy!<area>` full reference, and projects active-alarm
|
||||
snapshots to `ActiveAlarmSnapshot` protos for the `QueryActiveAlarms` refresh
|
||||
stream.
|
||||
- `MxAccessAlarmEventSink` enqueues each decoded transition onto the shared
|
||||
`MxAccessEventQueue` as a proto alarm-transition event, stamping the session
|
||||
id, so alarms ride the same outbound IPC path as tag-data events.
|
||||
|
||||
## Command Queue
|
||||
|
||||
The pipe reader converts `WorkerCommand` messages into `StaCommand` entries.
|
||||
|
||||
Reference in New Issue
Block a user