Skip to content

Add legacy libmbus normalized XML output format - #108

Merged
maebli merged 7 commits into
mainfrom
feat/libmbus-xml-output
Jul 23, 2026
Merged

Add legacy libmbus normalized XML output format#108
maebli merged 7 commits into
mainfrom
feat/libmbus-xml-output

Conversation

@maebli

@maebli maebli commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Summary

Adds an xml output format that reproduces libmbus's (rSCADA)
mbus_frame_data_xml_normalized() output byte for byte, so consumers of the
legacy normalized XML can use this parser as a drop-in, and so the parser can
be diffed against the reference .norm.xml corpus in tests/rscada.

The format is available everywhere serialize_mbus_data is:

  • Library: serialize_mbus_data(hex, "xml", None)
  • CLI: m-bus-parser-cli parse -d "..." -t xml
  • Python bindings: parse(data, "xml")
  • Web app: a new Parse to XML button

What's included

  • src/rscada_xml.rs — renders the SlaveInformation header and
    normalized DataRecords: VIF unit/quantity normalization table, medium and
    product-name lookups, BCD/integer/real/date/string value decoding, and XML
    escaping. Matches libmbus semantics including the pre-LSB-first binary
    formatting and 20xx-century date handling used when the reference corpus was
    generated.
  • Parity test (tests/rscada_xml.rs) diffing the xml output against the
    reference files. Frames that don't yet match are tracked in
    KNOWN_MISMATCHES; the test fails if one starts matching, so the list can
    only shrink.
  • Two parser fixes surfaced by the parity work:
    • Walk the VIFE extension chain from the correct offset when the plaintext
      VIF precedes the VIFE bytes (plaintext-before-extension); records that
      previously aborted with InvalidValueInformation now parse.
    • Decode a 6-byte date/time VIF (0x6D) as type I (CP48) instead of type F,
      per EN 13757-3.
  • Docs website — new button plus a rebuilt wasm bundle so the browser
    parser knows the format.

Parity

Byte-for-byte matches against the reference corpus:

  • 61/73 with std alone
  • 69/73 with std,plaintext-before-extension

The remaining 4 mismatches diverge because the parser rejects BCD data with
non-decimal nibbles (the Fh sign marker and manufacturer error markers like
DDh) and aborts the record stream, whereas libmbus decodes them leniently.

Verification

  • cargo fmt --check, cargo clippy -D warnings, and cargo test all pass
    across no-features / std / plaintext-before-extension.
  • CLI produces output byte-identical to the reference .norm.xml.
  • The rebuilt wasm's m_bus_parse(frame, "xml") (the function the web button
    calls) also returns byte-identical output, verified via a Node-target build.

maebli added 7 commits July 23, 2026 22:02
- Walk the VIFE extension chain from the correct offset when the plaintext
  VIF precedes the VIFE bytes (plaintext-before-extension). Records that
  previously aborted with InvalidValueInformation now parse.
- Decode a 6-byte data field carrying a date/time VIF (0x6D) as a type I
  (CP48) timestamp instead of type F, matching EN 13757-3.
Add an "xml" format to serialize_mbus_data that reproduces libmbus's
mbus_frame_data_xml_normalized() output byte for byte, as a drop-in for
consumers of the rSCADA/libmbus normalized XML.

- New src/rscada_xml.rs renders the SlaveInformation header and normalized
  DataRecords (VIF unit/quantity normalization table, medium and product
  name lookups, BCD/integer/real/date/string value decoding, XML escaping),
  matching libmbus semantics including the pre-LSB-first binary formatting
  and 20xx-century date handling used to generate the reference corpus.
- Wire the format into serialize_mbus_data and expose it through the CLI
  (-t xml) and the Python bindings' accepted-format list. The wasm binding
  passes the format through unchanged.
Diff the "xml" output against the reference .norm.xml files in
tests/rscada/test-frames. Frames that do not yet match libmbus are listed
in KNOWN_MISMATCHES; the test fails if one starts matching so the list can
only shrink. 61/73 frames match with std alone, 69/73 with
plaintext-before-extension enabled.
- README: list xml among the output formats, add CLI and library usage
  examples, and add it to the format reference table and CLI help block.
- CHANGELOG: note the new xml format and the two parser fixes it surfaced.
- Add a "Parse to XML" button to the web app, wired to the xml format with
  xml syntax highlighting and an .xml download.
- Rebuild the bundled wasm artifacts (wasm-pack --target web) so the parser
  in the browser knows the new xml format, and bump the cache-buster query
  strings to 0.2.0 so browsers load the refreshed build.
# Conflicts:
#	docs/m_bus_parser_wasm_pack_bg.wasm
xml is now a supported format, so the unsupported-format test uses a
genuinely invalid name and the render test asserts xml produces the
libmbus <MBusData> document.
@maebli
maebli merged commit a207f2b into main Jul 23, 2026
13 checks passed
@maebli
maebli deleted the feat/libmbus-xml-output branch July 23, 2026 20:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant