Files
mxaccess/rust
Joseph Doherty 2dc091d0be [M4] mxaccess: Session::read (read-as-subscribe pattern)
Now that Subscription impls Stream<Item = Result<DataChange, Error>>,
the read-as-subscribe pattern is a thin wrapper over subscribe +
timeout + best-effort unsubscribe.

New
- Session::read(reference, timeout) -> Result<DataChange, Error> —
  port of MxNativeSession.ReadAsync (cs:312-359). Validates timeout
  > 0, subscribes, awaits the first DataChange under
  tokio::time::timeout, then issues UnAdvise (best-effort, mirrors
  the .NET finally block at cs:351-358 which discards the
  unsubscribe return).

Error mapping
- timeout=0: Configuration::InvalidArgument ("Read timeout must be
  positive") matching ArgumentOutOfRangeException at cs:318-321.
- timeout elapsed: Error::Timeout(timeout).
- subscribe failure (resolver / transport): propagated unchanged.
- stream ends before any value: Connection::EngineNotRegistered
  (broadcast sender dropped during shutdown).
- unsubscribe failure: tracing::warn! with the error; doesn't
  override the read result.

Removed the placeholder stub in lib.rs that returned
Error::Unsupported.

Tests (4 new in mxaccess; total 44)
- read_returns_first_data_change_within_timeout: spawn read,
  inject a 0x33 DataUpdate via test_inject_sender (which fans out
  to all subscriptions), assert the DataChange comes back with the
  right value.
- read_returns_timeout_when_no_data_arrives: read times out cleanly
  with Error::Timeout when no callback fires.
- read_zero_timeout_returns_invalid_argument_without_subscribing:
  validates the early-reject path before any RPC is issued.
- read_propagates_resolver_not_found: subscribe-side error
  surfaces through read unchanged.

Test count delta: 516 -> 520 (+4). All four DoD gates green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 09:52:14 -04:00
..

mxaccess (Rust port)

Native Rust replacement for AVEVA / Wonderware MXAccess. See ../design/ for the architectural specification, ../src/ for the .NET reference (the executable spec), and ../CLAUDE.md for project-wide rules.

Status

M0 — Workspace skeleton. Stub types compile; nothing is implemented yet. See ../design/60-roadmap.md for the M0M6 milestone plan.

Layout

rust/
  Cargo.toml                 workspace root
  rust-toolchain.toml        1.85 stable
  crates/
    mxaccess-codec/          pure protocol codec, no I/O
    mxaccess-galaxy/         Galaxy SQL resolver (tiberius)
    mxaccess-rpc/            DCE/RPC + NTLMv2 + OXID + OBJREF
    mxaccess-callback/       INmxSvcCallback RPC server
    mxaccess-nmx/            INmxService2 client
    mxaccess-asb-nettcp/     net.tcp framing (MC-NMF + MC-NBFX/NBFS)
    mxaccess-asb/            IASBIDataV2 client
    mxaccess/                async session + Transport trait + public API
    mxaccess-compat/         LMXProxyServer-shaped facade

Build

cargo build --workspace
cargo test --workspace
cargo clippy --workspace -- -D warnings
cargo fmt --check

Live probes

. ..\tools\Setup-LiveProbeEnv.ps1
cargo test -p mxaccess --features live -- --ignored

The setup script fetches credentials from Infisical via wwtools/secrets/Get-Secret.ps1. Never inline plaintext credentials.

License

MIT — see ../LICENSE.