Files
mxaccess/rust/crates/mxaccess-codec/src/lib.rs
T
Joseph Doherty 7611d9e215 [M5] mxaccess-codec: F24 ASB Variant + AsbStatus + RuntimeValue codec
Ports `Variant` (cs:1170-1241), `AsbStatus` (cs:1109-1167), `RuntimeValue`
(cs:741-791), `AsbVariantFactory.From*` (cs:1310-1429), and
`MxAsbDataClient.DecodeVariant` (cs:713-825) into `mxaccess-codec::asb_variant`.

Three layers per `docs/ASB-Variant-Wire-Format.md`:
1. `AsbVariant` — raw 2/4/4/payload header + bytes; round-trips byte-identical.
2. `DecodedVariant` — typed view with one variant per proven ASB scalar / array
   (`Bool`, `Int32`, `Float`, `Double`, `String`, `DateTime`, `Duration` plus
   array forms). Type ids outside the proven matrix surface as
   `Unsupported { type_id, payload }` — same fallback as .NET's `_ => payload`.
3. `from_*` factories — mirror `AsbVariantFactory.FromX` exactly, setting
   `length` to `payload.len()` per `cs:1431-1438`.

`AsbStatus` and `RuntimeValue` round-trip the wire layout verbatim.
Status-element walking (marker bit 7 = implicit zero, etc., per
`docs/ASB-Variant-Wire-Format.md:180-205`) is deferred to a follow-up; the
codec exposes the raw status payload bytes for now, matching .NET's
`AsbStatus.Payload = byte[]` shape.

The lib.rs `AsbVariant` / `AsbStatus` / `RuntimeValue` stubs are replaced by
the real types via `pub use`. 25 new unit tests cover the proven matrix:
scalar + array round-trip, byte layout (2/4/4/payload), `Unsupported`
fallback for declared-but-unproven types, short-frame rejection,
malformed `string[]` partial-decode preservation matching .NET behavior.

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

89 lines
3.7 KiB
Rust

//! `mxaccess-codec` — pure protocol codec for the AVEVA / Wonderware MXAccess
//! wire format. No I/O.
//!
//! M1 codec parity in progress. Implemented:
//! - Foundational types: `MxReferenceHandle` (CRC-16/IBM), `NmxTransferEnvelope`
//! (with `reserved6_10` preservation), `MxStatus` + `MxStatusCategory` +
//! `MxStatusSource` + `detail_text`, `MxValue` + `MxValueKind` + `MxDataType`.
//! - Message-body codecs: `NmxItemControlMessage` (advise/supervisory/unadvise),
//! `write_message` module (scalar + array, normal + timestamped Write),
//! `subscription_message` (DataUpdate `0x33` + SubscriptionStatus `0x32`),
//! `NmxReferenceRegistrationMessage` + Result, `NmxOperationStatusMessage`
//! (incl. the proven `00 00 50 80 00` 5-byte completion frame and the
//! `0x00`/`0x41`/`0xEF` 1-byte completion frames preserved verbatim),
//! `NmxMetadataQueryMessage` (observed pre-advise template),
//! `NmxTransferEnvelopeTemplate` (round-trip preserver).
//!
//! Remaining (wave 2): `NmxSecuredWrite2Message` (`0x38`),
//! `ObservedWriteBodyTemplate`. ASB Variant + AsbStatus + RuntimeValue
//! landed in the F24 sub-stream of M5 — see [`asb_variant`].
//!
//! Every wire shape here is grounded in `src/MxNativeCodec/*.cs` (the .NET
//! reference) and `captures/0NN-frida-*` (Frida ground truth).
#![forbid(unsafe_code)]
pub mod asb_variant;
pub mod envelope;
pub mod envelope_template;
pub mod error;
pub mod item_control;
pub mod metadata_query;
pub mod observed_frame;
pub mod observed_write_template;
pub mod operation_status;
pub mod reference_handle;
pub mod reference_registration;
pub mod secured_write;
pub mod status;
pub mod subscription_message;
pub mod value;
pub mod write_message;
pub use envelope::{ENVELOPE_HEADER_LEN, NmxTransferEnvelope, NmxTransferMessageKind};
pub use envelope_template::NmxTransferEnvelopeTemplate;
pub use error::CodecError;
pub use item_control::{NmxItemControlCommand, NmxItemControlMessage};
pub use metadata_query::NmxMetadataQueryMessage;
pub use observed_frame::{NmxObservedEnvelope, NmxObservedMessage, NmxObservedString};
pub use observed_write_template::ObservedWriteBodyTemplate;
pub use operation_status::{NmxOperationStatusFormat, NmxOperationStatusMessage};
pub use reference_handle::{MxReferenceHandle, compute_name_signature, update_crc16_ibm};
pub use reference_registration::{
NmxReferenceRegistrationMessage, NmxReferenceRegistrationResultMessage,
};
pub use secured_write::DecodedSecuredWrite;
pub use status::{MxStatus, MxStatusCategory, MxStatusSource, detail_text};
pub use subscription_message::{NmxSubscriptionMessage, NmxSubscriptionRecord};
pub use value::{MxDataType, MxValue, MxValueKind};
// `NmxWriteMessage` and `NmxSecuredWrite2Message` are not single struct types
// in the Rust port — encoding/decoding live as functions in the
// `write_message` and `secured_write` modules. Keep stubs as short type
// aliases so existing references compile; consumers should call the module
// functions directly.
#[derive(Debug, Clone)]
pub struct NmxWriteMessage;
#[derive(Debug, Clone)]
pub struct NmxSecuredWrite2Message;
// ---- ASB types (M5, F24) -------------------------------------------------
pub use asb_variant::{
AsbDataType, AsbStatus, AsbVariant, DecodedVariant, RuntimeValue, decode_variant,
};
// ---- Convenience prelude -------------------------------------------------
pub mod prelude {
pub use super::{
CodecError, MxDataType, MxReferenceHandle, MxStatus, MxStatusCategory, MxStatusSource,
MxValue, MxValueKind, NmxItemControlCommand, NmxItemControlMessage,
NmxOperationStatusMessage, NmxReferenceRegistrationMessage,
NmxReferenceRegistrationResultMessage, NmxSubscriptionMessage, NmxTransferEnvelope,
NmxTransferEnvelopeTemplate, NmxTransferMessageKind,
};
}