Files
mxaccessgw/AGENTS.md
T
2026-04-26 15:19:17 -04:00

16 KiB

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:

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:

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:

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:

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:

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:

mxaccess-gateway-{gatewayProcessId}-{sessionId}

Frame messages as:

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:

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.