482 lines
16 KiB
Markdown
482 lines
16 KiB
Markdown
# MXAccess Gateway Agent Guide
|
|
|
|
Repository: https://gitea.dohertylan.com/dohertj2/mxaccessgw
|
|
|
|
This project builds a gateway that gives modern clients full MXAccess parity
|
|
without requiring those clients to load MXAccess COM, run as x86, or own an STA
|
|
message pump. Treat the installed MXAccess COM component as the compatibility
|
|
baseline.
|
|
|
|
Toolchain paths, versions, and external analysis locations are recorded in
|
|
`docs/toolchain-links.md`. Use that file before searching for compilers,
|
|
runtimes, protobuf tools, MXAccess notes, or Galaxy Repository SQL notes.
|
|
|
|
Implementation planning is recorded in `docs/implementation-plan-index.md`.
|
|
Follow the order there unless the user explicitly reprioritizes: gateway first,
|
|
MXAccess worker instance second, clients third.
|
|
|
|
## Core Contract
|
|
|
|
Preserve MXAccess behavior first:
|
|
|
|
- public MXAccess command semantics,
|
|
- native MXAccess event families,
|
|
- STA/message-pump delivery behavior,
|
|
- installed-provider quirks,
|
|
- HRESULT/status/value marshaling,
|
|
- per-client isolation.
|
|
|
|
Do not simplify, normalize, or "fix" MXAccess behavior unless an explicit
|
|
non-parity mode is being implemented and tested. `MxAsbClient` and managed NMX
|
|
are future acceleration paths only; they do not define the parity contract.
|
|
|
|
## Architecture
|
|
|
|
The intended split is:
|
|
|
|
```text
|
|
client
|
|
-> gRPC over TCP
|
|
-> .NET 10 x64 gateway
|
|
-> session manager
|
|
-> per-session .NET Framework 4.8 x86 worker process
|
|
-> dedicated STA thread
|
|
-> MXAccess COM instance
|
|
-> Windows/COM message pump
|
|
-> command queue
|
|
-> event sink
|
|
```
|
|
|
|
The gateway must never instantiate or call MXAccess directly. All MXAccess COM
|
|
interaction belongs in the worker process on its dedicated STA thread.
|
|
|
|
The worker must not host public gRPC. Gateway-to-worker communication should use
|
|
a small local IPC protocol, with named pipes and protobuf-framed messages as the
|
|
default design.
|
|
|
|
## Runtime Targets
|
|
|
|
- Gateway: .NET 10, C#, x64 preferred, ASP.NET Core gRPC.
|
|
- Worker: .NET Framework 4.8, C#, x86 by default.
|
|
- Worker IPC: one bidirectional named pipe per worker.
|
|
- Worker process model: one external client session maps to one worker by
|
|
default.
|
|
|
|
## Style Guides
|
|
|
|
Follow the project documentation guide and the language guide for every changed
|
|
area:
|
|
|
|
| Area | Style guide |
|
|
|------|-------------|
|
|
| Documentation | `StyleGuide.md` |
|
|
| Gateway, worker, .NET client, and C# tests | `docs/style-guides/CSharpStyleGuide.md` |
|
|
| Public gRPC and worker IPC contracts | `docs/style-guides/ProtobufStyleGuide.md` |
|
|
| Go client | `docs/style-guides/GoStyleGuide.md` |
|
|
| Rust client | `docs/style-guides/RustStyleGuide.md` |
|
|
| Python client | `docs/style-guides/PythonStyleGuide.md` |
|
|
| Java client | `docs/style-guides/JavaStyleGuide.md` |
|
|
|
|
When a change crosses languages, apply every affected style guide. Generated
|
|
code follows its generator output; do not hand-edit it to match handwritten
|
|
style.
|
|
|
|
## Expected Layout
|
|
|
|
Prefer this structure unless there is a strong reason to adjust it:
|
|
|
|
```text
|
|
src/MxGateway.Contracts/
|
|
Protos/
|
|
mxaccess_gateway.proto
|
|
mxaccess_worker.proto
|
|
Generated/
|
|
|
|
src/MxGateway.Server/
|
|
Program.cs
|
|
Sessions/
|
|
Workers/
|
|
Grpc/
|
|
Dashboard/
|
|
Metrics/
|
|
|
|
src/MxGateway.Worker/
|
|
Program.cs
|
|
Ipc/
|
|
Sta/
|
|
MxAccess/
|
|
Conversion/
|
|
|
|
src/MxGateway.Tests/
|
|
contract tests
|
|
gateway session tests
|
|
fake worker tests
|
|
|
|
src/MxGateway.Worker.Tests/
|
|
value/status conversion tests
|
|
STA queue tests
|
|
|
|
src/MxGateway.IntegrationTests/
|
|
optional live MXAccess tests
|
|
|
|
clients/dotnet/
|
|
.NET 10 C# client library, test CLI, and tests
|
|
|
|
clients/go/
|
|
Go client module, test CLI, and tests
|
|
|
|
clients/rust/
|
|
Rust client crate, test CLI, and tests
|
|
|
|
clients/python/
|
|
Python client package, test CLI, and tests
|
|
|
|
clients/java/
|
|
Java client library, test CLI, and tests
|
|
```
|
|
|
|
The contracts project may multi-target, or the `.proto` files may be shared as
|
|
source inputs to both gateway and worker builds.
|
|
|
|
## Public API Shape
|
|
|
|
The external API should be session-oriented. Initial rollout should prefer
|
|
unary `OpenSession`, `CloseSession`, and `Invoke`, plus server-streaming
|
|
`StreamEvents`. Add a bidirectional `Session` stream after the command and event
|
|
model is stable.
|
|
|
|
Do not compress MXAccess into generic verbs too early. Use a command enum with
|
|
method-specific payloads so parity can be tested method by method.
|
|
|
|
Core MXAccess commands to represent:
|
|
|
|
- `Register`
|
|
- `Unregister`
|
|
- `AddItem`
|
|
- `AddItem2`
|
|
- `RemoveItem`
|
|
- `Advise`
|
|
- `UnAdvise`
|
|
- `AdviseSupervisory`
|
|
- `AddBufferedItem`
|
|
- `SetBufferedUpdateInterval`
|
|
- `Suspend`
|
|
- `Activate`
|
|
- `Write`
|
|
- `Write2`
|
|
- `WriteSecured`
|
|
- `WriteSecured2`
|
|
- `AuthenticateUser`
|
|
- `ArchestrAUserToId`
|
|
|
|
Diagnostics may include `Ping`, `GetSessionState`, `GetWorkerInfo`,
|
|
`DrainEvents`, and `ShutdownWorker`.
|
|
|
|
## Event Requirements
|
|
|
|
Represent every public MXAccess event family:
|
|
|
|
- `OnDataChange`
|
|
- `OnWriteComplete`
|
|
- `OperationComplete`
|
|
- `OnBufferedDataChange`
|
|
|
|
Preserve per-worker event order. The gateway must not reorder events emitted by
|
|
the same MXAccess instance.
|
|
|
|
Event DTOs should carry event family, session id, server handle, item handle,
|
|
value, quality, timestamp, `MXSTATUS_PROXY[]` equivalent, raw HRESULT/status
|
|
fields when available, event sequence, worker timestamp, and gateway receive
|
|
timestamp.
|
|
|
|
## Value And Status Rules
|
|
|
|
Use a protobuf value union that can represent COM `VARIANT` values and arrays.
|
|
When a value cannot be losslessly converted, preserve both the best typed
|
|
projection and enough raw diagnostic metadata to reproduce the case.
|
|
|
|
Represent `MXSTATUS_PROXY` explicitly. Do not collapse status arrays into a
|
|
single success flag.
|
|
|
|
Command replies should include protocol status, COM HRESULT if available,
|
|
MXAccess return values, method-specific out parameters, and status arrays where
|
|
the MXAccess method emits them.
|
|
|
|
## Galaxy Repository SQL Discovery
|
|
|
|
Galaxy tags, hierarchy, and attribute details can be queried from the AVEVA /
|
|
Wonderware System Platform Galaxy Repository SQL Server database. Use this as a
|
|
discovery and metadata path only; runtime MXAccess parity still belongs to the
|
|
MXAccess-backed worker unless an explicit non-parity backend is being designed.
|
|
|
|
Full notes, schema details, screenshots, and query examples are in:
|
|
|
|
```text
|
|
C:\Users\dohertj2\Desktop\lmxopcua\gr
|
|
```
|
|
|
|
Important files in that notes directory:
|
|
|
|
- `connectioninfo.md` - SQL Server connection details and `sqlcmd` usage.
|
|
- `layout.md` - hierarchy vs `tag_name` relationship.
|
|
- `build_layout_plan.md` - extraction plan for hierarchy and attributes.
|
|
- `schema.md` and `ddl/` - Galaxy Repository schema reference.
|
|
- `queries/hierarchy.sql` - deployed object hierarchy.
|
|
- `queries/attributes.sql` - user-defined dynamic attributes.
|
|
- `queries/attributes_extended.sql` - system plus user-defined attributes.
|
|
- `queries/change_detection.sql` - deployment-change polling via
|
|
`galaxy.time_of_last_deploy`.
|
|
|
|
Current documented connection is SQL Server `localhost`, database `ZB`, Windows
|
|
Auth. Example:
|
|
|
|
```powershell
|
|
sqlcmd -S localhost -d ZB -E -Q "SELECT time_of_last_deploy FROM galaxy;"
|
|
```
|
|
|
|
Key tables from the notes are `gobject`, `template_definition`,
|
|
`dynamic_attribute`, `attribute_definition`, `primitive_instance`, and
|
|
`galaxy`. The hierarchy uses contained names for human-readable browsing, while
|
|
runtime tag references use globally unique `tag_name` values such as
|
|
`<tag_name>.<AttributeName>`.
|
|
|
|
## MXAccess Analysis Source
|
|
|
|
Use the local MXAccess analysis project when answering questions about installed
|
|
MXAccess classes, interfaces, fields, events, HRESULT/status behavior, value
|
|
projection, captures, and parity gaps:
|
|
|
|
```text
|
|
C:\Users\dohertj2\Desktop\mxaccess
|
|
```
|
|
|
|
Primary files:
|
|
|
|
- `README.md` - overview of available analysis and capture artifacts.
|
|
- `docs/MXAccess-Public-API.md` - COM class, ProgID, CLSID, method list,
|
|
event signatures, `MxDataType`, `MxStatus`, and `MXSTATUS_PROXY`.
|
|
- `docs/MXAccess-Reverse-Engineering.md` - installed runtime path and x86 COM
|
|
constraints.
|
|
- `docs/Current-Sprint-State.md` and `docs/DotNet10-Native-Library-Plan.md` -
|
|
current parity gaps and managed native-client research status.
|
|
- `src/MxTraceHarness/` - x86 MXAccess harness examples using the real COM
|
|
interop assembly.
|
|
- `captures/` and `analysis/` - observed native behavior and generated
|
|
reverse-engineering artifacts.
|
|
|
|
Concrete MXAccess COM target from the analysis:
|
|
|
|
- class: `ArchestrA.MxAccess.LMXProxyServerClass`
|
|
- CLSID: `{C30B52F5-2CB5-4760-AF0A-3A344A7EB5DC}`
|
|
- ProgID: `LMXProxy.LMXProxyServer.1`
|
|
- version-independent ProgID: `LMXProxy.LMXProxyServer`
|
|
- registered server: `C:\Program Files (x86)\ArchestrA\Framework\Bin\LmxProxy.dll`
|
|
- interop assembly:
|
|
`C:\Program Files (x86)\ArchestrA\Framework\Bin\ArchestrA.MXAccess.dll`
|
|
- threading model: `Apartment`
|
|
|
|
## Worker Rules
|
|
|
|
Each worker owns:
|
|
|
|
- one process,
|
|
- one MXAccess session,
|
|
- one dedicated STA thread,
|
|
- one MXAccess COM object,
|
|
- one inbound command queue,
|
|
- one outbound event queue.
|
|
|
|
All MXAccess operations must run on the STA. A plain blocking queue is not
|
|
enough for the STA; the STA loop must pump Windows/COM messages and service
|
|
queued commands.
|
|
|
|
Do not block the STA on pipe writes, gRPC calls, or slow consumers. Event
|
|
handlers should convert event args, enqueue outbound events, and return to
|
|
pumping messages.
|
|
|
|
On graceful shutdown, reject new commands, optionally clean up active MXAccess
|
|
handles, detach events, release the COM object, uninitialize COM, and exit. If
|
|
graceful shutdown exceeds the configured timeout, the gateway may kill the
|
|
worker.
|
|
|
|
## IPC Rules
|
|
|
|
Default pipe name shape:
|
|
|
|
```text
|
|
mxaccess-gateway-{gatewayProcessId}-{sessionId}
|
|
```
|
|
|
|
Frame messages as:
|
|
|
|
```text
|
|
uint32 little-endian payload_length
|
|
payload_length bytes protobuf WorkerEnvelope
|
|
```
|
|
|
|
Every envelope should include protocol version, session id, monotonic sender
|
|
sequence, correlation id, and a typed body. Protocol version mismatch should
|
|
fail session creation.
|
|
|
|
Pipe security should be local-machine only, with ACLs restricted to the gateway
|
|
identity and launched worker identity. Prefer a per-session nonce handshake.
|
|
|
|
## Gateway Rules
|
|
|
|
The gateway is responsible for:
|
|
|
|
- public TCP/gRPC API,
|
|
- Blazor Server dashboard using Bootstrap CSS/JS only,
|
|
- authn/authz when needed,
|
|
- session creation and teardown,
|
|
- worker launch and lifecycle management,
|
|
- command routing,
|
|
- event streaming,
|
|
- leases, heartbeats, timeouts, and quotas,
|
|
- worker kill/restart policy,
|
|
- metrics and structured logs.
|
|
|
|
The gRPC layer should stay thin: validate request, find session, call the
|
|
session worker client, map worker replies to public replies, and stream events.
|
|
Keep MXAccess-specific translation logic testable outside the gRPC handlers.
|
|
|
|
Dashboard code should also stay thin and read-only for v1. Use a snapshot
|
|
service over session/worker/metrics state; do not let Razor components mutate
|
|
gateway sessions or workers directly. Do not use MudBlazor or other Blazor UI
|
|
component libraries.
|
|
|
|
Gateway restart should not try to reattach old workers in the first version.
|
|
Terminate orphaned workers on startup if that behavior is implemented.
|
|
|
|
## Command, Timeout, And Cancellation Semantics
|
|
|
|
Command lifecycle:
|
|
|
|
```text
|
|
client gRPC command
|
|
gateway validates session and payload
|
|
gateway assigns correlation id
|
|
gateway writes WorkerCommand to pipe
|
|
worker queues command to STA
|
|
STA executes MXAccess method
|
|
worker captures return/out/status/HRESULT
|
|
worker sends WorkerCommandReply
|
|
gateway completes gRPC response
|
|
```
|
|
|
|
Canceling a gRPC call should stop waiting in the gateway, but it cannot safely
|
|
abort an in-flight COM call on the STA. Hard cancellation means killing the
|
|
worker process.
|
|
|
|
If a command wedges the STA beyond a configured grace period, the gateway should
|
|
kill the worker and fail the session.
|
|
|
|
## Backpressure Policy
|
|
|
|
Worker outbound events must use a bounded queue. For parity testing, prefer
|
|
fail-fast behavior over silent drops. Production coalescing or drop policies
|
|
must be explicit and observable.
|
|
|
|
The gateway should preserve per-session event order, apply backpressure from
|
|
slow gRPC streams, and disconnect or coalesce only according to an explicit
|
|
policy.
|
|
|
|
## Security And Logging
|
|
|
|
Use TLS for remote gRPC when crossing machine boundaries. Authentication may be
|
|
Windows auth, mTLS, or a deployment-specific token.
|
|
|
|
Commands that write, authenticate users, or alter runtime state need explicit
|
|
authorization design.
|
|
|
|
Never log passwords or raw credential values for `AuthenticateUser`,
|
|
`WriteSecured`, or related secured operations. Do not log full values by
|
|
default; make value logging opt-in and redacted.
|
|
|
|
## Testing Expectations
|
|
|
|
Use focused tests for:
|
|
|
|
- contract/protobuf compatibility,
|
|
- gateway session state and worker lifecycle,
|
|
- gateway behavior with a fake worker,
|
|
- worker value/status conversion,
|
|
- STA queue and message-pump behavior.
|
|
|
|
Live MXAccess integration tests are optional but should be isolated because they
|
|
depend on installed COM components and provider behavior.
|
|
|
|
Parity tests should compare direct MXAccess behavior against the gateway:
|
|
|
|
- return values,
|
|
- HRESULTs and exceptions,
|
|
- event sequence,
|
|
- value projection,
|
|
- quality/status arrays,
|
|
- invalid handle behavior,
|
|
- cross-server handle behavior,
|
|
- cleanup behavior.
|
|
|
|
Known important parity areas:
|
|
|
|
- `WriteSecured` may fail before a value-bearing NMX body is emitted.
|
|
- `WriteSecured2` can succeed in observed native paths.
|
|
- `OperationComplete` is distinct from write completion.
|
|
- `OnBufferedDataChange` has a distinct public event shape.
|
|
- Invalid handles and cross-server handles have specific exception/status
|
|
behavior.
|
|
- STA message pumping is required for event delivery.
|
|
|
|
## Source Update Workflow
|
|
|
|
When source code changes, build the affected component before handing work
|
|
back. If the change crosses component boundaries, build each affected component
|
|
instead of relying on a single top-level build.
|
|
|
|
Use the native build and test command for each changed area:
|
|
|
|
| Changed area | Required verification |
|
|
|--------------|-----------------------|
|
|
| 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, or metrics | build the .NET 10 gateway project and run affected gateway or fake-worker tests |
|
|
| Worker IPC, STA, MXAccess, or conversion code | build the .NET Framework 4.8 x86 worker project and run affected worker tests |
|
|
| Shared test infrastructure | run every test suite that consumes the changed helpers |
|
|
| .NET client | build the .NET client library, CLI, and tests |
|
|
| Go client | run Go formatting, build, and tests for the Go module |
|
|
| Rust client | run Rust formatting, build or check, and tests for the Rust crate |
|
|
| Python client | run Python formatting or linting if configured, package/build checks, and tests |
|
|
| Java client | build the Java client library, CLI, and tests |
|
|
| Integration tests | run them only when the required MXAccess COM component, provider state, and external services are available; otherwise document why they were skipped |
|
|
|
|
Update affected documentation in the same change as the source update. This
|
|
includes `gateway.md`, component design docs under `docs/`, client docs, API
|
|
contract notes, test instructions, and operational guidance. Documentation must
|
|
follow `StyleGuide.md`: write technical present-tense prose, explain the reason
|
|
for non-obvious choices, use exact code names, specify languages on code
|
|
blocks, use relative links for internal docs, and avoid stale temporary notes.
|
|
Source code and contract changes must also follow the relevant language guide
|
|
from the Style Guides section.
|
|
|
|
Do not leave documentation describing old behavior after changing public APIs,
|
|
contracts, configuration, build steps, security behavior, event shapes, value
|
|
conversion, status mapping, lifecycle rules, or client semantics.
|
|
|
|
## Implementation Priority
|
|
|
|
Build the smallest end-to-end slice first:
|
|
|
|
1. .NET 10 gateway starts.
|
|
2. Client calls `OpenSession`.
|
|
3. Gateway launches .NET Framework 4.8 x86 worker.
|
|
4. Worker creates STA and MXAccess COM object.
|
|
5. Client calls `Register`.
|
|
6. Client calls `AddItem`.
|
|
7. Client calls `Advise`.
|
|
8. Worker forwards one `OnDataChange` event to the gateway.
|
|
9. Gateway streams the event to the client.
|
|
10. Client calls `CloseSession`.
|
|
11. Gateway shuts down the worker.
|
|
|
|
That slice proves the high-risk requirements: process isolation, STA ownership,
|
|
message pumping, command routing, and event streaming.
|