Files
mxaccess/rust
Joseph Doherty 9b57cf8f3b [F45] mxaccess: recovery replay re-issues RegisterReference for buffered subs
`Session::recover_connection_core` previously walked
`SessionInner::subscriptions` and replayed every entry via
`AdviseSupervisory`, which lost the `.property(buffer)` registration
on buffered subscriptions — silently downgrading buffered → plain on
transport rebuild.

Fix:

- New `pub(crate) enum SubscriptionMode { Plain, Buffered { ... } }`
  discriminator carried on each `SubscriptionEntry`. Buffered variant
  retains the un-suffixed reference + the rounded interval (so the
  re-issued buffered registration matches the original cadence) +
  the empty `item_context` / zero `item_handle` matching the wire
  send.
- `Session::subscribe` (plain path) records `SubscriptionMode::Plain`.
  `subscribe_buffered_nmx` records `SubscriptionMode::Buffered { ... }`.
- `recover_connection_core` matches on `entry.mode`. Plain branch
  unchanged. Buffered branch re-applies `.property(buffer)` via
  `to_buffered_item_definition` (idempotent), rebuilds the original
  `NmxReferenceRegistrationMessage` with the saved correlation id +
  `subscribe = true`, and dispatches `register_reference` (kind=
  ItemControl, inner command 0x10) against the replacement
  transport. Mirrors `MxNativeSession.ReAdviseSubscription`
  (`MxNativeSession.cs:538-569`).

New unit test `recover_connection_replays_buffered_subscription_via_
register_reference` synthesises a buffered registry entry, installs a
`RebuildFactory` pointing at a recording NMX server, drives
`recover_connection`, then asserts the recorded `TransferData` carries
inner command `0x10` (NOT `0x1f`) with the `.property(buffer)`-
suffixed item_definition + the saved correlation id + subscribe=true.

Side-finding worth filing separately: `Session::unsubscribe`
unconditionally calls `un_advise` for both plain and buffered
entries, but the .NET reference's `Unsubscribe`
(`MxNativeSession.cs:361-381`) skips `UnAdvise` for buffered
(`if (!subscription.IsBuffered)`). Out of scope for F45 (recovery-
only); will file as F47.

Public API unchanged. `SubscriptionMode` + `SubscriptionEntry` stay
`pub(crate)` — `cargo public-api -p mxaccess` baseline is unchanged.

Workspace 793 → 794 tests; clippy clean; rustdoc clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 05:54:30 -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.