fe2a6db786
rust / build / test / clippy / fmt (push) Has been cancelled
Layout:
- src/ .NET 10 x64 reference: MxNativeCodec, MxNativeClient,
MxAsbClient, probes, tests, harnesses. Executable spec.
- design/ Architectural plan for the Rust port (M0–M6), error
model, protocol invariants, risks (R1–R16), adversarial
review log (review.md).
- rust/ Rust workspace. M0 skeleton + M1 codec parity.
mxaccess-codec: 215 unit tests + 2 cross-implementation
parity tests (byte-identical against .NET reference).
Other crates are M0 stubs awaiting M2+.
- captures/ Frida + netsh + pcap evidence per CLAUDE.md
("captures are evidence, not throwaway logs").
- analysis/ Decompiled C# (frida/proxy/decompiled-*),
Ghidra exports for native DLLs (`exports/` only —
working state at `projects/` and AVEVA's input
binaries at `input/` are gitignored).
- docs/ Reverse-engineering reference docs.
- tools/ Setup-LiveProbeEnv.ps1 (Infisical credential fetcher),
Compute-Crc.ps1 (.NET parity helper).
- .github/workflows/ Rust CI: fmt + build + test + clippy on Windows.
- LICENSE MIT (Joseph Doherty, 2026).
Verified:
- cargo test --workspace → 217 passed (215 unit + 2 .NET parity), 0 failed
- cargo clippy --workspace -- -D warnings → clean
- cargo fmt --all -- --check → clean
- cargo publish --dry-run -p mxaccess-codec → packages cleanly
Excluded from history (see .gitignore):
- **/bin, **/obj, **/target — build artifacts
- analysis/ghidra/projects/ — Ghidra working state (regenerable)
- analysis/ghidra/input/ — AVEVA proprietary DLLs (vendor IP)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
499 lines
32 KiB
Markdown
499 lines
32 KiB
Markdown
# Current Sprint State
|
|
|
|
Last updated: 2026-04-26 local VM time.
|
|
|
|
## Build Status
|
|
|
|
- `MxNativeCodec.Tests`: passed.
|
|
- `MxNativeClient.Tests`: passed, including compatibility surface and recovery
|
|
lifecycle event checks.
|
|
- `MxTraceHarness`: Release build passed.
|
|
- `MxNativeClient.Probe`: Release build passed, including
|
|
`--probe-session-recover-multi`.
|
|
- `MxDataConsumerProbe`: Release build passed. It is an x86 .NET Framework
|
|
probe for `aaMxDataConsumer.dll` / `IDataConsumer`.
|
|
- `AsbProxyProbe`: Release x64 .NET Framework build passed. It directly loads
|
|
the MSIL ASB IData proxy/contract assemblies and proves x64 managed
|
|
discovery, connection, register, read, write, readback, and published write
|
|
completion against the live MxDataProvider. It also has an unregister compare
|
|
probe for installed-proxy parity.
|
|
- `MxAsbClient.Probe`: Release .NET 10 x64 build passed. It is a pure managed
|
|
ASB client slice with no AVEVA assembly references. It reads the ASB solution
|
|
shared secret through DPAPI, performs the ASB system-auth handshake, opens the
|
|
net.tcp `IASBIDataV2` channel, and live validates register, read, write,
|
|
readback, published write completion, unregister parity, and multi-item
|
|
register/read for `TestChildObject.TestInt` plus
|
|
`TestMachine_001.TestHistoryValue`.
|
|
- `MxAsbClient.Probe`: clean Release build with zero warnings after adding an
|
|
explicit `System.Security.Cryptography.Xml` `10.0.7` package reference and
|
|
replacing the obsolete PBKDF2 constructor with `Rfc2898DeriveBytes.Pbkdf2`.
|
|
- `MxAsbClient.Tests`: passed. The test project covers ASB `Variant` factory
|
|
and decode/display round trips for bool, int, float, double, string,
|
|
datetime, duration, and the matching bool/int/float/double/string/
|
|
datetime/duration array wire payloads, `MonitoredItemValue` binary
|
|
serializer round trips for publish responses, and ASB status payload decoding
|
|
for publish quality/status mapping. It also covers the stable ASB connection
|
|
options overload, endpoint validation, and friend-only debug payload helper
|
|
visibility, plus write-completion option validation and cancellation-token
|
|
surface, subscription/monitored-item option overloads, and publish result
|
|
summary helpers. Latest focused non-live shaping tests also assert exact
|
|
defaults and validation boundaries for `AsbConnectionOptions`,
|
|
`AsbWriteOptions`, `AsbWriteCompletionOptions`, `AsbSubscriptionOptions`,
|
|
and `AsbMonitoredItemOptions`, DTO shape for write-completion/readback
|
|
results, null collection argument guards, empty/non-empty `AsbPublishResult`
|
|
helpers, derived status-summary helpers, item-status array mapping, and both
|
|
compatibility `Advise` overload signatures.
|
|
- Credential scan over `analysis`, `docs`, `src`, `captures`, and `README.md`:
|
|
no matches for the protected password patterns.
|
|
|
|
## Implemented Surface
|
|
|
|
- Public MXAccess method surface is accounted for: `Register`, `Unregister`,
|
|
`AddItem`, `RemoveItem`, `Advise`, `UnAdvise`, `Write`, `WriteSecured`,
|
|
`AuthenticateUser`, `ArchestrAUserToId`, `AddItem2`, `Write2`,
|
|
`WriteSecured2`, `Suspend`, `Activate`, `AdviseSupervisory`,
|
|
`AddBufferedItem`, and `SetBufferedUpdateInterval`.
|
|
- Public event families are represented: `OnDataChange`, `OnWriteComplete`,
|
|
`OperationComplete`, and `OnBufferedDataChange`.
|
|
- `WriteSecured2` is implemented and live validated from .NET 10 x64 for the
|
|
observed authenticated secured-write body shape.
|
|
- `WriteSecured` remains intentionally unsupported because installed MXAccess
|
|
returns `0x80004021` for the observed secured/verified paths before emitting a
|
|
value-bearing NMX body.
|
|
- Object/VARIANT timestamp overloads exist for `Write2` and `WriteSecured2`,
|
|
matching the installed interop assembly's public API shape.
|
|
- `SetHeartbeatSendInterval` is exposed and live validated through
|
|
`MxNativeClient.Probe --probe-session-heartbeat --heartbeat-ticks=5
|
|
--heartbeat-max-missed=3`.
|
|
- Explicit recovery is implemented as `MxNativeSession.RecoverConnection()` and
|
|
`MxNativeCompatibilityServer.RecoverConnection(serverHandle)`. The probe
|
|
`--probe-session-recover --tag=TestChildObject.TestInt --value=321` live
|
|
validated subscribe, recover, preserved subscription count, and write-through
|
|
against local NmxSvc.
|
|
- `RecoverConnectionAsync` adds a caller-controlled retry loop via
|
|
`MxNativeRecoveryPolicy`. Automatic write retry remains intentionally absent
|
|
because it can duplicate writes.
|
|
- Recovery lifecycle is observable through `RecoveryAttemptStarted`,
|
|
`RecoveryAttemptFailed`, and `RecoveryCompleted` on both `MxNativeSession`
|
|
and the managed compatibility facade. A live recovery probe with
|
|
`--recover-attempts=2 --recover-delay-ms=100 --value=323` observed one
|
|
started event, zero failed events, and one completed event.
|
|
- Callback, operation-status, reference-registration, unparsed-callback,
|
|
compatibility data-change, buffered-data-change, and write-complete payloads
|
|
now carry `IsDuringRecovery`. The current policy is pass-through with an
|
|
explicit marker rather than suppression or buffering.
|
|
- `MxNativeClient.Probe --probe-session-recover-multi` replays several active
|
|
subscriptions and counts recovery-window callbacks by callback family.
|
|
|
|
## Runtime Evidence Added In Latest Sprints
|
|
|
|
- GR identified `TestMachine_001.TestHistoryValue` as a deployed, historized
|
|
integer dynamic attribute.
|
|
- Captures `121` and `122` proved context-bearing buffered registration/result
|
|
bodies for `TestHistoryValue.property(buffer)` in context `TestMachine_001`.
|
|
- Both supervisory and plain advise buffered captures still produced no native
|
|
`Fire_OnBufferedDataChange` entry on this VM.
|
|
- Reflection over `ArchestrA.MXAccess.dll` confirmed the 18-method public
|
|
method surface, four event families, `MxStatus` layout, `MxStatusCategory`,
|
|
`MxStatusSource`, and full `MxDataType` enum values.
|
|
- Installed `Lmx.aaDCT` detail text has been folded into `MxStatusDetails` for
|
|
details `16`-`61`, `541`, `542`, and `8017`.
|
|
- Explicit recovery event reporting is implemented and live validated. The
|
|
events report attempt number, maximum attempts, exception, and retry intent
|
|
for failed attempts.
|
|
- A live multi-subscription recovery probe replayed four subscriptions and
|
|
preserved all four. It observed two data callbacks and four unparsed callbacks
|
|
overall, but zero callbacks with `IsDuringRecovery=true` on this VM.
|
|
- A follow-up churn run with `--recover-concurrent-writes` wrote
|
|
`TestChildObject.TestInt` values `330`-`334` from a separate managed session
|
|
during recovery. It preserved all four subscriptions and observed four data
|
|
callbacks overall, including two with `IsDuringRecovery=true`. This validates
|
|
the pass-through-with-marker callback policy under live traffic.
|
|
- `aaMxDataConsumer.dll` was imported with `tlbimp` into
|
|
`analysis\interop\Interop.aaMxDataConsumer.dll` and decompiled under
|
|
`analysis\decompiled-interop\Interop.aaMxDataConsumer`. Registry CLSID
|
|
`{85209FB2-0BA1-4594-BBC4-59D3DDAB823D}` maps to `MxDataConsumer Class`.
|
|
- `MxDataConsumerProbe` can instantiate `DataConsumerClass`, register
|
|
`IDataConsumerCallback`, resolve namespace strings to namespace ID `1`, and
|
|
call `ResolveReference`, `subscribe`, `ActivateSuspend`, and
|
|
`ProcessActivateSuspend2`. However, `IsConnected(namespaceId)` remains `0`
|
|
for tested namespace strings (`Galaxy`, `ArchestrA`, `Lmx`, `localhost`,
|
|
`ZB`), no registration/subscription responses are produced, and
|
|
`ProcessActivateSuspend2` returns `0x8007139F` (`ERROR_INVALID_STATE`).
|
|
- `DataClientClass` appears in the imported `aaMxDataConsumer` type library,
|
|
but its CLSID `{73BC4121-FF89-4762-901C-206E2BD9FE87}` is not registered on
|
|
this node; `MxDataConsumerProbe --probe-dataclient` reports
|
|
`0x80040154` (`REGDB_E_CLASSNOTREG`) before endpoint tests can run.
|
|
- Headless/ILSpy analysis of `aaMxDataConsumer.dll` shows the COM consumer is a
|
|
mixed-mode wrapper around managed ASB data proxies. `CDataClientCLI` creates a
|
|
`DataClientProxy`, stores the namespace string, starts an auto-connect
|
|
worker, and `DataClientProxy.Initialize` passes that same string as
|
|
`AccessName` to `IDataProxySelector.SelectProxyForLatestEndpoint`.
|
|
- `ASBIDataV2Adapter.dll` from the GAC contains `IDataProxySelector`. It first
|
|
calls `ASBDataV2Proxy.FindIDataEndpoint(accessName, DiscoveryScope.Global)`;
|
|
if that returns endpoints it constructs `ASBDataV2Proxy`, otherwise it falls
|
|
back to V1. The discovery scope is
|
|
`domainname/<accessName>/global`.
|
|
- `AsbProxyProbe` live x64 discovery found one `IASBIDataV2` endpoint for
|
|
access name `ZB`:
|
|
`net.tcp://desktop-6jl3kko/ASBService/Default_ZB_MxDataProvider/IDataV2`.
|
|
It found no endpoint for `Default_ZB_MxDataProvider`, `Galaxy`,
|
|
`localhost`, or `ZB2`.
|
|
- `AsbProxyProbe --access=ZB --connect` opened the ASB data proxy from an x64
|
|
managed process. `ASBDataV2Proxy.Connect` returned `true`, channel state was
|
|
`Opened`, and `PublishWriteComplete` returned `0x00000000` with no pending
|
|
writes. This proves a managed x64 ASB route to LMX/NMX data services exists
|
|
on this node.
|
|
- `AsbProxyProbe --access=ZB --connect --register --read --tag=TestChildObject.TestInt`
|
|
registered and read the test integer through `IASBIDataV2`. `RegisterItems`
|
|
and `Read` both returned `0x00000000`; the item id was
|
|
`18446462598732840961`; the read value was ASB `DataType.TypeInt32` (`4`)
|
|
with payload `4E010000`, decoded as `334`.
|
|
- `AsbProxyProbe --access=ZB --connect --register --read --write-int=401 --tag=TestChildObject.TestInt`
|
|
accepted a basic ASB write and proved readback. The immediate `Write` result
|
|
was globally successful, the per-item synchronous status was `0x0000001F`
|
|
(`ArchestrAError.OperationWouldBlock`), the next read returned integer value
|
|
`401`, and `PublishWriteComplete` then returned `0x00000020`
|
|
(`ArchestrAError.PublishComplete`) with the original write handle
|
|
`0xA5B20001` and per-item completion status `0x00000000`. This is the first
|
|
direct evidence that ASB write completion polling can supply the missing
|
|
operation-completion source without the unstable DataConsumer COM wrapper.
|
|
- Forcing the x86 `MxDataConsumerProbe` to `--namespace=ZB` hung in the COM
|
|
wrapper path and was stopped. The direct ASB route is currently the cleaner
|
|
path for x64 managed implementation and for any future `OperationComplete`
|
|
trigger testing.
|
|
- The pure .NET 10 ASB port in `src\MxAsbClient` now proves the live data path
|
|
without loading AVEVA assemblies. Key auth details recovered during the port:
|
|
this VM's ASB solution registry key is `Archestra_DESKTOP-6JL3KKO`,
|
|
`HashAlgorthim=None`, `keySize=256`, and the solution overrides the DH prime
|
|
with the installed 768-bit value. The remaining compatibility fixes were the
|
|
AVEVA CLR data-contract namespaces/field ordering for `ConnectionValidator`,
|
|
`WriteValue`, `ItemStatus`, `ItemIdentity`, and `ItemWriteComplete`, plus
|
|
mutable-struct-safe binary decode for nested `Variant` and `ASBStatus`
|
|
values.
|
|
- `MxAsbClient.Probe --tag=TestChildObject.TestInt --write-int=412` completed
|
|
from .NET 10 x64. The run read the previous value `411`, accepted write
|
|
`412` with immediate per-item `0x0000001F` (`OperationWouldBlock`), read back
|
|
`412`, and decoded `PublishWriteComplete` result `0x00000020`, count `1`,
|
|
handle `0xA5B21001`, and final per-item status `0x00000000`.
|
|
- `RegisterItems` parity is resolved for the observed startup race. ASB
|
|
`AuthenticateMe` is a one-way WCF call; the first immediate `RegisterItems`
|
|
can arrive before `ASBIDataV2Shim.AuthenticateMe` adds the connection
|
|
implementation. The client now matches installed proxy signing for normal
|
|
data calls and retries `RegisterItems` briefly when the immediate result is
|
|
`0x00000001` (`InvalidConnectionId`). Stage21 evidence shows first register
|
|
message `2` returned `InvalidConnectionId`, retry message `3` returned
|
|
`0x00000000` with item id `18446462598732840961`, and later calls on the
|
|
same connection succeeded.
|
|
- `UnregisterItems` is implemented in the pure .NET 10 ASB port and compared
|
|
against the installed AVEVA `ASBDataV2Proxy`. Both clients return global
|
|
success `0x00000000` and the same per-item `0x0000000B`
|
|
(`OperationFailed`) for the registered `TestChildObject.TestInt` identity on
|
|
this provider. Evidence:
|
|
`analysis\proxy\mxasbclient-probe-stage23-unregister-id.txt` and
|
|
`analysis\proxy\asbproxyprobe-unregister-compare.txt`.
|
|
- Multi-item ASB register/read is implemented and live validated in the pure
|
|
.NET 10 port. `analysis\proxy\mxasbclient-probe-stage24-multi-read.txt`
|
|
registers and reads `TestChildObject.TestInt` and
|
|
`TestMachine_001.TestHistoryValue` in single two-item requests. Both register
|
|
and read return global `0x00000000` and per-item `0x00000000`; the read values
|
|
decode as ASB `TypeInt32` with previews `412` and `303`.
|
|
- The pure .NET 10 ASB port now has non-live ASB `Variant` construction and
|
|
decode/display coverage for the core scalar and array type matrix:
|
|
`TypeBool`, `TypeInt32`, `TypeFloat`, `TypeDouble`, `TypeString`,
|
|
`TypeDateTime`, `TypeDuration`, and their matching array forms. The public
|
|
client also exposes a generic `Write(tag, Variant, writeHandle)` path so the
|
|
next live write probes can reuse the same encoder instead of being hard-coded
|
|
to `Int32`.
|
|
- A live ASB register/read probe validated the deployed scalar and array read
|
|
type matrix from .NET 10 x64:
|
|
`TestChildObject.TestBool` -> `TypeBool` (`17`),
|
|
`TestChildObject.TestInt` -> `TypeInt32` (`4`),
|
|
`TestChildObject.TestFloat` -> `TypeFloat` (`8`),
|
|
`TestChildObject.TestDouble` -> `TypeDouble` (`9`),
|
|
`TestChildObject.TestString` -> `TypeString` (`10`),
|
|
`TestChildObject.TestDateTime` -> `TypeDateTime` (`11`), and the matching
|
|
`TestBoolArray[]`, `TestIntArray[]`, `TestFloatArray[]`,
|
|
`TestDoubleArray[]`, `TestStringArray[]`, and `TestDateTimeArray[]` returning
|
|
ASB array types `57`, `44`, `48`, `49`, `50`, and `51`. All register/read
|
|
per-item statuses were `0x00000000`, and previews decoded correctly.
|
|
- `MxAsbClient.Probe` now keeps SOAP envelopes and custom serializer byte dumps
|
|
opt-in through `--dump-messages`; normal traced runs still show stages,
|
|
statuses, type IDs, payload lengths, timestamps, and decoded previews without
|
|
dumping request/response bodies.
|
|
- Pure .NET 10 ASB scalar writes are live validated beyond `Int32`:
|
|
`TestBool` accepted a `TypeBool` write and later read back `False` then
|
|
`True`; `TestFloat` wrote/read `13.25`; `TestDouble` wrote/read `13.75`;
|
|
`TestString` accepted `asb-scalar-write` and read it back on a delayed
|
|
follow-up read; `TestDateTime` wrote/read `2026-04-26T14:33:00Z`.
|
|
These runs confirm the scalar factory payloads are accepted by the live
|
|
provider. Immediate `PublishWriteComplete` often returned count `0`, and
|
|
bool/string immediate readback could be stale or time out, so completion
|
|
polling/readback timing remains part of the production timeout policy gap.
|
|
- Pure .NET 10 ASB array writes are live validated for the deployed dynamic
|
|
array tags. `TestIntArray[]` accepted/read back `11`-`20`;
|
|
`TestBoolArray[]` accepted/read back alternating false/true values;
|
|
`TestFloatArray[]` and `TestDoubleArray[]` accepted/read back
|
|
`11.1`-`20.1`; `TestStringArray[]` accepted/read back `K11`-`T20`; and
|
|
`TestDateTimeArray[]` accepted/read back ten UTC timestamps from
|
|
`2026-04-26T14:40:00Z` through `2026-04-26T14:49:00Z`. Int, bool, and string
|
|
array readback needed a delayed follow-up read, matching the scalar timing
|
|
behavior noted above.
|
|
- Production cleanup removed the active ASB build warnings: the vulnerable
|
|
transitive `System.Security.Cryptography.Xml` `10.0.0` package is overridden
|
|
with `10.0.7`, `dotnet list package --include-transitive --vulnerable` now
|
|
reports no vulnerable packages for `MxAsbClient`, and the system-auth
|
|
PBKDF2 derivation uses the non-obsolete static API while preserving the
|
|
installed proxy's 1000-iteration SHA1 derivation inputs. A live ASB read of
|
|
`TestChildObject.TestInt` still succeeds after the authenticator change.
|
|
- Pure .NET 10 ASB subscription/publish is now live-proven for the basic
|
|
lifecycle. `MxAsbClient.Probe --subscribe --publish-count=3` created
|
|
subscription id `3`, added monitored items for `TestChildObject.TestInt` and
|
|
`TestChildObject.TestString` with per-item status `0x00000000`, received both
|
|
values on the second publish poll (`TypeInt32` preview `412` and `TypeString`
|
|
preview `asb-scalar-write`), deleted the subscription with global success,
|
|
and then completed the existing unregister cleanup path. Publish responses
|
|
returned global `0x00000020`, matching the previously observed completion
|
|
queue success signal, so exact result/status mapping remains open.
|
|
- The ASB publish path now maps raw `MonitoredItemValue` responses into
|
|
callback-ready values with item-id-to-tag-name resolution, decoded value,
|
|
UTC timestamp, `MxQuality`, and parsed ASB status elements. A live publish
|
|
run for `TestChildObject.TestInt` and `TestChildObject.TestString` decoded
|
|
quality `0x00C0` and status elements
|
|
`OpcUaStatus:0|OpcUaVendorStatus:0|MxStatusCategory:0|MxStatusDetail:0|MxQuality:192`
|
|
for both values. The same run added `DeleteMonitoredItems`, returned global
|
|
success and per-item `0x00000000` for both monitored identities, then deleted
|
|
the subscription successfully.
|
|
- `MxAsbDataClient` now exposes `PublishedValueReceived` plus `PublishValues`.
|
|
`PublishValues` keeps the raw `PublishResponse`, maps values through the
|
|
client's monitored-item id cache, and raises one event per mapped value. A
|
|
live probe observed `published_event[0]` for `TestChildObject.TestInt` and
|
|
`published_event[1]` for `TestChildObject.TestString`, both with quality
|
|
`0x00C0` and expected previews.
|
|
- `MxAsbCompatibilityServer` now adapts the pure ASB stream into an MXAccess-like
|
|
server/item-handle data-change facade. A live
|
|
`MxAsbClient.Probe --compat-subscribe --publish-count=3` run registered ASB
|
|
server handle `1`, added/advised item handles `1` and `2`, polled publish,
|
|
raised two `compat_data_change` events with values `412` and
|
|
`asb-scalar-write`, quality `0x00C0`, decoded ASB status elements, removed
|
|
both monitored items, and unregistered cleanly.
|
|
- The compatibility facade now has an `Advise` overload that accepts
|
|
`AsbSubscriptionOptions` plus `AsbMonitoredItemOptions`, matching the public
|
|
ASB subscription shaping instead of forcing positional primitives. The probe
|
|
compatibility subscribe path now exercises this route, and a live
|
|
compatibility subscribe run through the options overload registered/advised
|
|
item handles, published mapped `compat_data_change` events, removed monitored
|
|
items, and unregistered cleanly.
|
|
- Initial non-success ASB item-status mapping is implemented. A live probe for
|
|
`DefinitelyMissingObject.DefinitelyMissingAttribute` showed global register
|
|
and read success but per-item read error `0x000A`
|
|
(`InvalidMonitoredItems`), an empty value with value-status payload decoded
|
|
as `OpcUaStatus:32905|OpcUaVendorStatus:0|MxStatusCategory:750|MxStatusDetail:0|MxQuality:0`,
|
|
and unregister per-item error `0x000B` (`OperationFailed`). Unknown payloads
|
|
are still preserved rather than guessed.
|
|
- `MxAsbClient.Probe --probe-error-cases` now keeps broader live ASB
|
|
non-success evidence opt-in without changing production client behavior.
|
|
Evidence in `analysis\proxy\mxasbclient-probe-error-cases-v2.txt` covers:
|
|
invalid read target global success with per-item `0x000A`
|
|
(`InvalidMonitoredItems`) plus bad value status; invalid-target async write
|
|
immediate per-item `0x001F` (`OperationWouldBlock`) followed by
|
|
`PublishWriteComplete` global `0x0020` (`PublishComplete`) and completion
|
|
per-item `0x0006` (`MonitoredItemsNotFound`) with status payload
|
|
`OpcUaStatus:6|OpcUaVendorStatus:0|MxStatusCategory:4|MxStatusDetail:6`;
|
|
wrong-type string write to `TestChildObject.TestInt` immediate per-item
|
|
`0x001F`, completion per-item `0x0013`
|
|
(`WriteFailedBadTypeMismatch`) with status payload
|
|
`OpcUaStatus:19|OpcUaVendorStatus:0|MxStatusCategory:4|MxStatusDetail:8001`,
|
|
and unchanged readback value `412`; invalid monitored-item cleanup global
|
|
success with per-item `0x000B` (`OperationFailed`); invalid subscription
|
|
delete global `0x000C`, status `0x0020`, specific `0x80020000`; and empty
|
|
subscription publish returning global `0x0020` with no values.
|
|
- ASB result/status summaries now name the full installed `ArchestrAError`
|
|
code set, classify publish `0x0020` (`PublishComplete`) as success-like for
|
|
publish polling, summarize `MxQuality` as good/uncertain/bad/unknown, and map
|
|
known MX status details `16` and `17` to `RequestTimedOut` and
|
|
`BadNoCommunication` while preserving raw category/detail/quality values.
|
|
- Async ASB write completion now has a production-oriented polling helper.
|
|
`WaitForWriteComplete` polls `PublishWriteComplete` for a target write
|
|
handle until completion or timeout, preserving every raw poll response and
|
|
completion record. `WaitForWriteCompleteAndRead` adds optional delayed
|
|
readback without retrying or duplicating the write. A live probe using
|
|
`--write-int=412 --wait-write-complete --write-complete-timeout-ms=5000
|
|
--write-complete-poll-ms=250 --write-readback-delay-ms=500` saw the first
|
|
completion poll return count `0`, the second poll return the matching
|
|
handle `0xA5B21001`, and readback return `412`.
|
|
- ASB client disposal now has explicit cleanup reporting. `Dispose()` delegates
|
|
to idempotent `Cleanup()`, sends signed `Disconnect` best-effort, closes the
|
|
channel and factory independently, aborts safely after close/fault failures,
|
|
and records `LastCleanupResult` with `Completed`, `Succeeded`,
|
|
`UsedAbortFallback`, and `RequiresNewConnection`. A live read probe ended
|
|
with `asb.stage=cleanup`, `asb.stage=disconnect`, and
|
|
`asb.cleanup.succeeded=True`.
|
|
- ASB reconnect now has an explicit opt-in API. `MxAsbDataClient.Reconnect`
|
|
validates caller retry options, optionally cleans up the current connection,
|
|
returns a new client on success, and preserves attempt plus cleanup evidence
|
|
without silently replaying reads or writes. A live
|
|
`MxAsbClient.Probe --probe-reconnect --reconnect-attempts=2
|
|
--reconnect-delay-ms=250 --cleanup-disconnect-timeout-ms=5000
|
|
--cleanup-close-timeout-ms=5000` run read `TestChildObject.TestInt` as
|
|
`412`, cleaned up and disconnected the original connection with caller
|
|
cleanup timeouts, connected successfully on attempt `1`, re-registered the
|
|
tag on the new client, read back `412`, and completed final cleanup
|
|
successfully.
|
|
- ASB cleanup partial-failure behavior is now covered in focused local tests
|
|
without forcing live provider faults. The close-or-abort policy is factored
|
|
into `AsbCommunicationCleanup`, and tests cover already-closed objects,
|
|
faulted objects that skip close and abort directly, close failure followed by
|
|
abort fallback, abort failure reporting, and propagation of the configured
|
|
close timeout.
|
|
- ASB cleanup cancellation now has explicit policy. `AsbClientCleanupOptions`
|
|
accepts a `CancellationToken`; when cancellation is already requested, cleanup
|
|
skips graceful disconnect/close attempts and uses local abort cleanup for
|
|
non-closed channel/factory objects. Existing disconnect and close timeouts
|
|
still bound synchronous WCF calls that have already started. A live
|
|
`MxAsbClient.Probe --probe-canceled-cleanup` run connected, registered, and
|
|
read `TestChildObject.TestInt` as `412`, then skipped disconnect and aborted
|
|
both opened communication objects locally. The cleanup result completed with
|
|
`succeeded=False`, `abort=True`, `requires_new=True`, and an
|
|
`OperationCanceledException` disconnect marker.
|
|
- Safe connection-failure evidence is now captured without destabilizing the
|
|
live ASB provider. `MxAsbClient.Probe --probe-connect-failure
|
|
--endpoint=net.tcp://localhost:1/ASBService/Default_ZB_MxDataProvider/IDataV2`
|
|
reports `connect_failure_observed=True`, an `EndpointNotFoundException`, and
|
|
inner `SocketException` `10061` for a refused local TCP endpoint. `Connect`
|
|
now also closes or aborts any WCF channel/factory objects created before a
|
|
connection setup failure; the refused-endpoint probe reports
|
|
`asb.stage=connect-cleanup` before surfacing the exception.
|
|
- OperationComplete candidate evidence was narrowed on the direct ASB route.
|
|
The `IASBIDataV2` contract exposed by the live endpoint has no activate,
|
|
suspend, or generic operation-complete operation; only `PublishWriteComplete`
|
|
exists, and it is write-specific. A live
|
|
`MxAsbClient.Probe --probe-operation-complete-candidates` run against
|
|
`TestChildObject.TestInt` and `TestChildObject.TestString` polled
|
|
`PublishWriteComplete` before and after create-subscription, add-monitored,
|
|
publish, delete-monitored, and delete-subscription operations. Every poll
|
|
returned count `0`, while the non-write operations themselves succeeded.
|
|
- ASB buffered-subscription probing is now explicit. `MxAsbDataClient` exposes
|
|
the `MonitoredItem.Buffered` flag through `AddMonitoredItems`, and
|
|
`MxAsbClient.Probe --subscribe-buffered` sets it. A live buffered ASB publish
|
|
probe against the historized `TestMachine_001.TestHistoryValue` succeeded,
|
|
but returned the same single current `MonitoredItemValue` shape as the normal
|
|
publish path: value `303`, quality `0x00C0`, timestamp
|
|
`2026-04-26T02:33:45.4260000Z`, and no multi-sample buffered batch.
|
|
- ASB status mapping now includes installed MX detail `33`
|
|
(`WriteAccessDenied`) and maps it to `WriteFailedAccessDenied`. Tests cover
|
|
success, invalid monitored item, wrong-type write completion, invalid cleanup
|
|
evidence inputs, request timeout detail `16`, platform/no-communication
|
|
detail `17`, and access-denied detail `33`.
|
|
- Safe access-denied/no-communication candidate probes did not produce those
|
|
source conditions on this VM. Same-value direct ASB writes to secured
|
|
`TestMachine_001.ProtectedValue` and verified
|
|
`TestMachine_001.ProtectedValue1` both completed successfully and read back
|
|
unchanged, so direct system-auth ASB writes do not reproduce the MXAccess
|
|
public secured-write rejection path here. A read-only sweep of
|
|
`TestMachine_001.ProtectedValue` through `TestMachine_020.ProtectedValue`
|
|
registered and read all 20 values with good quality `0x00C0`; no
|
|
no-communication status was observed.
|
|
- Native NMX completion-only operation-status handling is now stricter. One-byte
|
|
completion-only frames remain preserved as raw, unpromoted completion statuses
|
|
even when the byte is `0x00`; only the observed 5-byte status-word frame
|
|
`00 00 50 80 00` maps to `MxStatus.WriteCompleteOk` and can raise the
|
|
MXAccess-compatible write-complete event. Tests cover completion-only
|
|
`0x00`, `0x41`, and `0xEF`, plus the promoted `0x8050/0x00` status-word
|
|
frame. A search of the available Ghidra/decompiled outputs did not expose a
|
|
completion-byte-to-`MXSTATUS_PROXY[]` mapping table beyond this public-event
|
|
evidence.
|
|
- Public ASB connection shaping has started. `AsbConnectionOptions` is now the
|
|
stable options object for `MxAsbDataClient.Connect`, the previous string
|
|
overload delegates to it, and `MxAsbCompatibilityServer.Register` has a
|
|
matching options overload. Endpoint validation now happens before registry or
|
|
WCF setup work. `AsbPayloadDebug` is no longer part of the public surface and
|
|
is friend-only for the test/probe assemblies. A clean Release probe build,
|
|
non-live ASB contract test run, and live read of `TestChildObject.TestInt`
|
|
all passed after this change.
|
|
- ASB write-completion options now validate their own polling/readback policy
|
|
and expose a `CancellationToken`. Cancellation is checked before each
|
|
`PublishWriteComplete` poll and during poll/readback delays; already-running
|
|
synchronous ASB calls remain bounded by the existing WCF operation behavior.
|
|
A live same-value write-completion probe still completed on the second poll
|
|
for handle `0xA5B21001` and read back `412`.
|
|
- ASB subscription API shaping now has `AsbSubscriptionOptions` and
|
|
`AsbMonitoredItemOptions` overloads while preserving the existing positional
|
|
overloads. A live subscribe/publish probe created subscription `15`, added
|
|
monitored items for `TestChildObject.TestInt` and
|
|
`TestChildObject.TestString`, published both mapped values/events, deleted
|
|
the monitored items, deleted the subscription, and cleaned up the ASB
|
|
connection successfully.
|
|
- `AsbPublishResult` now exposes a derived `Result` summary and `HasValues`
|
|
helper so callers do not have to remap raw `PublishResponse.Result` on every
|
|
publish poll. The existing raw response and mapped values remain preserved.
|
|
- Public API/request-shaping tests were expanded without live ASB dependency.
|
|
They now pin defaults, validation, overload visibility, and helper DTO shape
|
|
for connection, write completion/readback, subscription, monitored-item,
|
|
compatibility `Advise`, and publish-result surfaces.
|
|
- Basic writes now also have an `AsbWriteOptions` overload carrying the write
|
|
handle and optional comment. The existing positional write overload and
|
|
`WriteInt32` path delegate through it, preserving behavior; a live same-value
|
|
write-completion probe still completed on the second poll and read back
|
|
`412`.
|
|
- Public multi-item APIs now explicitly reject null collection arguments before
|
|
touching client state: `RegisterMany`, `UnregisterMany`, `ReadMany`,
|
|
option-based `AddMonitoredItems`, and `DeleteMonitoredItems`. Empty
|
|
collections and tag/item contents keep their prior behavior.
|
|
- Published values and compatibility data-change events now expose derived
|
|
`StatusSummary` helpers, and `AsbResultMapper.ToItemSummaries` maps nullable
|
|
item-status arrays into raw-preserving summaries with null/empty as an empty
|
|
result.
|
|
- `docs\ASB-Variant-Wire-Format.md` now captures the supported ASB `Variant`
|
|
wire format: type IDs, scalar and array payload layouts, timestamp/duration
|
|
handling, string encoding, runtime value wrapping, quality/status payload
|
|
parsing, and intentionally raw-preserved unsupported types.
|
|
- `docs\ASB-Native-Integration-Decision.md` now records the recommended
|
|
integration boundary: prefer `MxAsbClient` for the regular tag data plane
|
|
behind a higher-level compatibility routing facade, keep `MxNativeClient` for
|
|
native callback-only semantics and not-yet-proven MXAccess behaviors, and
|
|
avoid merging ASB WCF lifecycle internals into the NMX DCE/RPC session.
|
|
|
|
## Remaining Highest-Value Gaps
|
|
|
|
1. Additional live faulted-channel and source-condition evidence that can be
|
|
gathered safely. Current safe secured/verified-write and protected-value
|
|
read sweeps did not produce access-denied or no-communication statuses.
|
|
2. Broaden pure .NET 10 ASB only where safe source conditions expose new
|
|
behavior. Local ASB `Variant` encode/decode, live reads/writes,
|
|
subscription/create/add/publish/delete-monitored/delete-subscription,
|
|
mapped publish events, compatibility data-change facade, options-shaped
|
|
connection/write/subscription/advise/write-completion APIs, cleanup, and
|
|
reconnect are now covered. Remaining work is mainly external failure modes
|
|
such as access denied or no-communication when a safe source condition is
|
|
available.
|
|
3. A native runtime trigger for `OperationComplete`. The direct ASB path now
|
|
proves a write-completion queue (`PublishWriteComplete`) carrying write
|
|
handles and per-item statuses. Direct `IASBIDataV2` does not expose
|
|
activate/suspend or a generic operation-complete contract, and subscription
|
|
create/add/publish/delete operations did not enqueue write-completion
|
|
records. Map to MXAccess event ID `3` only if a separate native trigger is
|
|
found.
|
|
4. A native runtime/source condition that emits `OnBufferedDataChange` sample
|
|
batches. Direct ASB buffered monitored items are accepted by this provider,
|
|
but the observed publish response still carries one current value rather
|
|
than a native MXAccess buffered sample batch.
|
|
5. Exact mapping for completion-only operation-status bytes into
|
|
`MXSTATUS_PROXY[]`; current code now preserves all completion-only bytes as
|
|
unpromoted raw statuses instead of guessing, including completion-only
|
|
`0x00`.
|
|
6. Cleanup behavior around live faulted/partial failure cases when a safe
|
|
provider condition is available.
|
|
7. Less-common ASB variant types only when real deployed tags expose them; the
|
|
currently supported/proven variant layout is documented and unsupported
|
|
declared types remain raw-preserved.
|
|
|
|
## Recommended Next Sprint
|
|
|
|
Treat the public ASB API-shaping sprint as closed unless a concrete caller gap
|
|
appears. The remaining local documentation gaps are also closed. Return to exact
|
|
completion-only operation-status mapping into `MXSTATUS_PROXY[]` only if a new
|
|
safe native public-event capture or deeper targeted decompile can provide
|
|
evidence. Keep live faulted-channel, access-denied, and source
|
|
no-communication probes opportunistic unless a safe provider condition appears.
|