2dc091d0be
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>
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 M0–M6 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.