[M4] mxaccess: examples wave 3 — 7 example programs (M4 wave 3)
Replaces the M0 stub bodies in `crates/mxaccess/examples/` with real
consumer-facing demos against the M4 NMX `Session` surface. Each example
gates on `MX_LIVE` and prints a friendly bypass message when the live
env vars aren't populated, so `cargo build --workspace --all-targets`
stays green in CI without an AVEVA install.
Five examples target the proven NMX path (build + connect + demo +
shutdown):
- `connect-write-read` — `Session::write_value` + `read` round-trip; the
30-line consumer-experience target from `design/60-roadmap.md` M4 DoD.
- `subscribe` — single-tag `Subscription` stream; drains 5 updates or
10s timeout, then `unsubscribe` cleanly.
- `recovery` — `RecoveryPolicy { max_attempts: 3, delay: 250ms }`
+ spawned `recovery_events()` listener consuming the broadcast.
- `multi-tag` — per-tag `subscribe` loop merged via
`futures_util::stream::select_all`; matches the .NET cs:250-270 shape
(no atomic subscribe-many RPC on the wire).
- `secured-write` — `write_value_secured_at` exercising both single-user
(`current_user_id == verifier_user_id`) and two-person paths per
`wwtools/mxaccesscli/src/MxAccess.Cli/Commands/WriteCommand.cs:151-155,196-199`.
Two examples hold the place for downstream milestones:
- `subscribe-buffered` — pattern-matches on `Error::Unsupported` from
`Session::subscribe_buffered` (M6) and exits 0 with an explanation.
- `asb-subscribe` — same shape against `Session::connect` (M5 ASB).
All five live examples share an inline `LiveEnv::from_process` helper,
a dashed-hex `parse_guid`, and a `StaticResolver` that returns canned
metadata for the configured `MX_TEST_TAG`. The duplication is
intentional — Cargo examples are meant to be self-contained and read
top-to-bottom; consumers swap `StaticResolver` for a tiberius-backed
Galaxy resolver (followup F14) without touching any other example.
Test count delta: 524 → 524 (+0; examples are demos, not tests)
Open followups touched: F17 logged (Guid::parse_str helper to dedupe
the per-example dashed-hex parser).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -60,6 +60,12 @@ move to `## Resolved` with a date + commit hash.
|
||||
**Why deferred:** Wave-2 `Session::recover_connection` validates the policy and emits `RecoveryEvent::Started` + `RecoveryEvent::Recovered` on each call but does **NOT** actually tear down + re-establish the NMX transport / re-advise active subscriptions. The .NET reference's `RecoverConnectionCore` (`MxNativeSession.cs:442-474`) does all three: builds a replacement `ManagedNmxService2Client` via `CreateRegisteredService`, re-`Connect`s every `_publisherEndpoints` entry, re-`AdviseSupervisory`s every entry in `_subscriptions`, then atomically swaps the old service for the new one. Porting this to Rust requires (a) tracking the active subscriptions inside `SessionInner` (currently they're owned by the consumer's `Subscription` handles, with no central registry); (b) the long-lived connection task per R15 in `design/70-risks-and-open-questions.md` so swap-in-place is safe under concurrent operations; (c) a way to re-create the `CallbackExporter` (or keep the existing one bound while the underlying transport is replaced — needs design work).
|
||||
**Resolves when:** R15's long-lived connection task lands and `SessionInner` gains a subscription registry. At that point the recover loop becomes ~50 lines: for `attempt in 1..=max_attempts`, emit Started → drop+rebuild NmxClient → `register_engine_2` with the existing OBJREF → re-advise every registered correlation_id → emit Recovered (or Failed + sleep delay + continue, mirroring the `cs:407-440` shape exactly).
|
||||
|
||||
### F17 — `Guid::parse_str` helper (dashed-hex string parser)
|
||||
**Severity:** P3
|
||||
**Source:** M4 wave 3, `crates/mxaccess/examples/*.rs`
|
||||
**Why deferred:** Each of the five live-NMX examples (`connect-write-read`, `subscribe`, `recovery`, `multi-tag`, `secured-write`) duplicates a 15-line `parse_guid` helper that takes a `12345678-1234-1234-1234-123456789012` style string and produces the wire-byte `Guid([u8; 16])`. The helper belongs on `mxaccess_rpc::guid::Guid` itself (paired with its existing `Display` impl) so consumer code that wires up `MX_NMX_SERVICE_IPID` doesn't reimplement the LE-leading byte-swap convention. Deferred to keep this iteration's diff focused on examples; the parse path has no corresponding .NET reference helper to mirror so it's a Rust-side ergonomic addition rather than a parity port.
|
||||
**Resolves when:** Add `pub fn parse_str(s: &str) -> Result<Guid, RpcError>` on `mxaccess_rpc::guid::Guid` with a round-trip test against the existing `Display` fixture (`crates/mxaccess-rpc/src/guid.rs:111-119`). Update the five examples to call it. Single-iteration follow-up.
|
||||
|
||||
### F14 — `tiberius`-backed SQL implementation of `Resolver` + `UserResolver`
|
||||
**Severity:** P2
|
||||
**Source:** M3 stream A, `crates/mxaccess-galaxy/src/sql.rs` (constants present, no client wiring yet)
|
||||
|
||||
Reference in New Issue
Block a user