feat!: mux on DongLoRa Protocol v2 with tag-aware dispatch

The 1.0 mux, running on the Protocol v2 wire format and full §13 multi-client semantics.

* Tag-aware dispatch. Each inbound client frame is re-tagged with a mux-allocated device tag; responses are routed back to the original client using the tag mapping. The single-oneshot response slot is gone; multiple clients can have commands in flight concurrently.

* Full §13.2 multi-client SET_CONFIG semantics. Tracks the (client, modulation) lock owner and synthesizes OK(APPLIED/MINE), OK(ALREADY_MATCHED/OTHER), or OK(LOCKED_MISMATCH/OTHER) based on lock ownership and parameter match. Lock releases automatically on client disconnect.

* TX loopback fan-out (§13.4). On TX_DONE(TRANSMITTED) the mux synthesizes an RX frame with origin = LocalLoopback and sends it to every connected client except the transmitter.

* Async ERR(EFRAME) fan-out. If the dongle emits async framing errors (tag = 0), they are broadcast to all clients rather than swallowed.

* TagMapper with skip-zero allocation and per-client bulk drop on disconnect.

* Per-client TX cache on ClientSession so the loopback fan-out does not need the sender to re-buffer.

* 500 ms keepalive cadence matching PROTOCOL.md §3.4 inactivity window (replacing the old 5 s loop).
This commit is contained in:
swaits committed 2026-04-22 09:50:52 -06:00
1 parent 90a306853d
commit 7274293a04
7 files changed
+1081 -522

No files matched your search

+7 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "donglora-mux"
version = "0.2.2"
version = "1.0.0-alpha.1"
edition = "2024"
rust-version = "1.93"
authors = ["Stephen Waits <steve@waits.net>"]
@@ -13,7 +13,11 @@ categories = ["hardware-support"]
exclude = [".jj/", "deny.toml", "justfile", "mise.toml", "rustfmt.toml"]
[dependencies]
donglora-client = "0.2"
# Mux speaks raw DongLoRa Protocol frames — pull the wire codec directly from the
# protocol crate rather than routing through the high-level Dongle API.
donglora-protocol = { path = "../protocol", default-features = false, features = ["std"] }
# Still used for USB discovery helpers (`find_port`, `wait_for_device`).
donglora-client = { path = "../client-rs", version = "1.0.0-alpha.1" }
anyhow = "1"
clap = { version = "4", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
@@ -22,6 +26,7 @@ tokio-util = "0.7"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
fd-lock = "4"
heapless = "0.9"
[lints.clippy]
unwrap_used = "deny"