a 0.1.1

HJ 212 ASCII protocol: framing, parsing, CRC, and builders.
Documentation
# 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/PNO` support 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


```rust
use a::{build_frame, parse_frame};

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(payload);
assert!(frame.ends_with("\r\n"));

let pkt = parse_frame(&frame).unwrap();
assert_eq!(pkt.mn.as_deref(), Some("ABC"));
assert_eq!(pkt.cp.get("a21026-Rtd").map(String::as_str), Some("12.3"));
```

### 2) Extract frames from a byte stream


```rust,no_run
use a::{Framer, parse_frame};

let mut framer = Framer::new();
framer.push(b"##0025QN=1;ST=22;CN=2011;CP=&&&&");

while let Some(frame_bytes) = framer.next_frame() {
    let frame_str = String::from_utf8(frame_bytes).expect("HJ212 is ASCII");
    let _pkt = parse_frame(&frame_str)?;
}

# Ok::<(), a::Hj212Error>(())

```

### 3) Send-side: builders


```rust
use a::{CpBuilder, PayloadBuilder, parse_frame};

let mut cp = CpBuilder::new();
cp.data_time("20250101010101")
  .rtd_flag("a21026", "12.3", "N")
  .kv("a21026-Zs", "0.0");

let frame = PayloadBuilder::new(
    "20251225123000001", // QN
    "123456",            // PW
    "ABC",               // MN
    cp.build(),
)
.st("22")
.cn("2011")
.flag("7")
.frame();

let pkt = parse_frame(&frame).unwrap();
assert_eq!(pkt.mn.as_deref(), Some("ABC"));
```

## Optional: SM4 auth encryption (Appendix H)


The standard’s multimedia upload appendix specifies:

- `Authorization` plaintext is `username:password`
- SM4-ECB + PKCS7Padding
- then Base64

Enable feature `sm4`:

```toml
a = { version = "0.1", features = ["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)


- `LEN` is 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, the `LEN` digits, 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`, init `0xFFFF`, and the update step `crc = (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*` parses `CP=&&...&&` into `pkt.cp` by splitting **only on `;`** into `k=v` pairs.
- 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.