a
A Rust library that implements the protocol layer of China MEE HJ 212 (ASCII messages).
This crate focuses on reusable building blocks:
- Receiving: extract frames from a TCP/serial byte stream (sticky packets) → parse a frame → get a structured
Hj212Packet - Sending: build
CP=&&...&&and payload via builders → wrap into an HJ212 frame (##{LEN}{PAYLOAD}{CRC})
Scope
Included:
- Framing:
##{LEN}{PAYLOAD}{CRC16_HEX}(HJ 212—2025 ANSI CRC16) - Parse/build:
parse_frame/build_frame - Streaming framer:
Framer - Builders:
CpBuilder/PayloadBuilder - Helpers: CRC functions, DataTime parsing
- Standard-alignment helpers:
build_frame_standard(uppercase CRC + trailing\r\n)parse_frame_strict(requires 4-digit length + CRC +\r\n)
- Appendix C helpers:
PNUM/PNOsupport and common ACK payload builders (build_qn_rtn,build_exe_rtn,build_data_ack,build_notify_ack) - Appendix A/H helpers (optional): encryption helpers under
crypto(see below)
Intentionally NOT included:
- Network I/O (Tokio/Actix/TCP server), serial port reading/writing
- Database/storage, platform-specific mapping, UI
- HTTPS upload logic (Appendix H multimedia upload)
Quick start
1) Parse a single complete frame
use ;
let payload = "QN=1;ST=22;CN=2011;PW=123456;MN=ABC;Flag=7;CP=&&DataTime=20250101010101;a21026-Rtd=12.3&&";
let frame = build_frame;
assert!;
let pkt = parse_frame.unwrap;
assert_eq!;
assert_eq!;
2) Extract frames from a byte stream
use ;
let mut framer = new;
framer.push;
while let Some = framer.next_frame
# Ok::
3) Send-side: builders
use ;
let mut cp = new;
cp.data_time
.rtd_flag
.kv;
let frame = new
.st
.cn
.flag
.frame;
let pkt = parse_frame.unwrap;
assert_eq!;
Optional: SM4 auth encryption (Appendix H)
The standard’s multimedia upload appendix specifies:
Authorizationplaintext isusername:password- SM4-ECB + PKCS7Padding
- then Base64
Enable feature sm4:
= { = "0.1", = ["sm4"] }
And use a::crypto::sm4_auth.
Notes on standard vs compat frames
build_frame(...)emits standard frames: 4-digit LEN + uppercase CRC + trailing\r\n.- If you must interop with a legacy variant without CRLF, use
build_frame_compat(...).
CRC16 and LEN (important)
LENis the ASCII byte length of the payload (the bytes between{LEN}and{CRC}), i.e.payload.as_bytes().len(). It does not include the##prefix, theLENdigits, the 4-byte CRC text, or the trailing\r\n.- CRC is computed over the payload bytes only using the HJ 212—2025 “ANSI CRC16” algorithm (poly
0xA001, init0xFFFF, and the update stepcrc = (crc >> 8) ^ byte). - For historical interop, this crate also exposes the CRC16/Modbus variant via
crc16_modbus_hex_upper/lower.
Standard example from the text (CRC = 2200):
##0087QN=20240601085857223;ST=32;CN=1011;PW=123456;MN=010000A8900016F000169DC0;Flag=9;CP=
&&&&2200\r\n
Notes on CP parsing
parse_frame*parsesCP=&&...&&intopkt.cpby splitting only on;intok=vpairs.- Some platform payloads group fields with commas (e.g.
a34006-Avg=... ,a34006-Flag=N). Those commas are intentionally left for the business layer to interpret.