[F51] live ASB type-matrix: provision UDAs + capture wire fixtures + round-trip tests
Provisioned 7 new UDAs on $TestMachine via wwtools/graccesscli
object uda add (then deployed to TestMachine_001):
TestFloat MxFloat scalar
TestFloatArray MxFloat array (4)
TestDouble MxDouble scalar
TestDoubleArray MxDouble array (4)
TestDateTime MxTime scalar
TestDuration MxElapsedTime scalar
TestDurationArray MxElapsedTime array (4)
New crates/mxaccess/examples/asb-type-matrix.rs reads all 14 tags
(7 pre-existing + 7 new) in a single batch and dumps the live
AsbVariant bytes per tag when MX_ASB_DUMP_FIXTURES=<dir> is set.
Single-attempt register (no retry — F31 InvalidConnectionId
cool-down re-arms on every retry, making backoff
counter-productive; if the cool-down is engaged, wait 60+ seconds
without ASB activity then re-run).
Captured live evidence (single cold-start run, all 14 register
calls returned error_code=0x0000):
TestChangingInt type_id=4 (Int32) length=4 payload=4
TestAlarm001 type_id=17 (Boolean) length=1 payload=1
MachineCode type_id=10 (String) length=30 payload=30
TestFloat type_id=8 (Float) length=4 payload=4
TestDouble type_id=9 (Double) length=8 payload=8
TestDateTime type_id=11 (DateTime) length=8 payload=8
TestDuration type_id=12 (ElapsedTime) length=8 payload=8
TestIntArray, TestBoolArray, TestStringArray, TestDateTimeArray,
TestFloatArray, TestDoubleArray, TestDurationArray
type_id=0 length=0 payload=0
(provisioned but no value written yet)
Per-tag fixture .bin files saved under
crates/mxaccess-codec/tests/fixtures/f51-type-matrix/ — full
14-byte to 40-byte AsbVariant byte sequences (i32 type_id LE +
i32 length LE + payload bytes).
crates/mxaccess-codec/tests/f51_type_matrix_parity.rs round-trips
each scalar fixture: decode -> re-encode -> assert byte-equal +
type_id / length pin. Tests skip with [skip] message when fixtures
are absent (so the suite passes on a fresh checkout without live
captures). 7 scalar tests pass against the captured fixtures.
Array tags excluded from round-trip pinning because the live
engine returns empty payloads for unwritten arrays. Codec-side
array round-trip is covered by asb_variant's existing synthetic-
payload unit tests.
docs/galaxy-test-fixtures.md inventories all $TestMachine UDAs
(pre-existing + F51-provisioned), the graccesscli provisioning
recipe, the fixture-regeneration pattern, and the F31 cool-down
caveat.
design/followups.md F51 marked resolved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
//! F51 — round-trip parity for the live ASB type matrix.
|
||||
//!
|
||||
//! Captured live by `cargo run -p mxaccess --example asb-type-matrix`
|
||||
//! with `MX_ASB_DUMP_FIXTURES=...` set. Each fixture file holds the
|
||||
//! full `AsbVariant` byte sequence (i32 type_id LE + i32 length LE +
|
||||
//! payload bytes) for one tag's read response. The tests below decode
|
||||
//! each fixture, re-encode, and assert byte-identical round-trip plus
|
||||
//! the expected type_id / length tuple.
|
||||
//!
|
||||
//! Live-evidence row from the original capture:
|
||||
//!
|
||||
//! | Tag | type_id | length | payload bytes |
|
||||
//! |---|---|---|---|
|
||||
//! | TestChangingInt | 4 (Int32) | 4 | 4 |
|
||||
//! | TestAlarm001 | 17 (Boolean) | 1 | 1 |
|
||||
//! | MachineCode | 10 (String) | 30 | 30 |
|
||||
//! | TestFloat | 8 (Float) | 4 | 4 |
|
||||
//! | TestDouble | 9 (Double) | 8 | 8 |
|
||||
//! | TestDateTime | 11 (DateTime) | 8 | 8 |
|
||||
//! | TestDuration | 12 (ElapsedTime)| 8 | 8 |
|
||||
//!
|
||||
//! Array tags read empty on the live install (no value written yet)
|
||||
//! so they're not in this fixture set; their codec round-trip is
|
||||
//! covered by `asb_variant`'s existing unit tests with synthetic
|
||||
//! payloads.
|
||||
|
||||
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
|
||||
|
||||
use mxaccess_codec::AsbVariant;
|
||||
|
||||
const FIXTURE_DIR: &str = "tests/fixtures/f51-type-matrix";
|
||||
|
||||
fn load_fixture(name: &str) -> Option<Vec<u8>> {
|
||||
let path = format!("{FIXTURE_DIR}/{name}.bin");
|
||||
match std::fs::read(&path) {
|
||||
Ok(bytes) => Some(bytes),
|
||||
Err(_) => {
|
||||
eprintln!(
|
||||
"[skip] fixture {name} not present at {path} — \
|
||||
capture via `cargo run -p mxaccess --example asb-type-matrix` \
|
||||
with `MX_ASB_DUMP_FIXTURES={FIXTURE_DIR}` set",
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_round_trip(fixture: &str, expected_type_id: u16, expected_length: i32) {
|
||||
let Some(bytes) = load_fixture(fixture) else {
|
||||
return;
|
||||
};
|
||||
let (decoded, consumed) = AsbVariant::decode(&bytes)
|
||||
.unwrap_or_else(|e| panic!("decode {fixture}: {e:?}"));
|
||||
assert_eq!(consumed, bytes.len(), "{fixture}: decode consumed != bytes.len()");
|
||||
assert_eq!(decoded.type_id, expected_type_id, "{fixture}: type_id");
|
||||
assert_eq!(decoded.length, expected_length, "{fixture}: length");
|
||||
let re_encoded = decoded.encode();
|
||||
assert_eq!(re_encoded, bytes, "{fixture}: round-trip not byte-identical");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int32_scalar_round_trip() {
|
||||
assert_round_trip("TestMachine_001_TestChangingInt", 4, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean_scalar_round_trip() {
|
||||
assert_round_trip("TestMachine_001_TestAlarm001", 17, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_scalar_round_trip() {
|
||||
// length 30 = "TestMachine_001" (15 chars × 2 bytes UTF-16) on a
|
||||
// typical install. Different fixture captures may shift the
|
||||
// length if the MachineCode value differs.
|
||||
let Some(bytes) = load_fixture("TestMachine_001_MachineCode") else {
|
||||
return;
|
||||
};
|
||||
let (decoded, consumed) = AsbVariant::decode(&bytes)
|
||||
.unwrap_or_else(|e| panic!("decode: {e:?}"));
|
||||
assert_eq!(consumed, bytes.len());
|
||||
assert_eq!(decoded.type_id, 10);
|
||||
let re_encoded = decoded.encode();
|
||||
assert_eq!(re_encoded, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_scalar_round_trip() {
|
||||
assert_round_trip("TestMachine_001_TestFloat", 8, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_scalar_round_trip() {
|
||||
assert_round_trip("TestMachine_001_TestDouble", 9, 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_time_scalar_round_trip() {
|
||||
assert_round_trip("TestMachine_001_TestDateTime", 11, 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn elapsed_time_scalar_round_trip() {
|
||||
assert_round_trip("TestMachine_001_TestDuration", 12, 8);
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Reference in New Issue
Block a user