7611d9e215
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>
89 lines
3.7 KiB
Rust
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,
|
|
};
|
|
}
|