feat!: mux-py on DongLoRa Protocol v2 with tag-aware dispatch
The 1.0 Python mux, mirroring the Rust donglora-mux 1.0 one-for-one and implementing the full §13 multi-client semantics on the DongLoRa Protocol v2 wire format.
* Structural rewrite. donglora_mux/{daemon,intercept,session,tagmap}.py replace the 601-line __init__.py monolith.
* Tag-aware dispatch. TagMapper with skip-zero allocation and per-client bulk drop on disconnect. Multiple clients can have commands in flight concurrently; the single-oneshot response slot is gone.
* Full §13.2 multi-client SET_CONFIG semantics. Tracks (client_id, modulation) lock owner and synthesizes OK(APPLIED/MINE), OK(ALREADY_MATCHED/OTHER), or OK(LOCKED_MISMATCH/OTHER).
* TX loopback fan-out (§13.4). On TX_DONE(TRANSMITTED) the mux synthesizes an RX frame with origin = LOCAL_LOOPBACK for every connected client except the transmitter.
* Async ERR(EFRAME) fan-out: tag-0 framing errors are broadcast to all clients.
* Per-client TX cache on ClientSession so the loopback fan-out does not need the sender to re-buffer.
* Socket and serial fcntl.flock to prevent multiple mux instances from fighting over the same dongle or socket path.
* 500 ms keepalive matching PROTOCOL.md §3.4 (2x margin on the 1 s inactivity timer), replacing the 5 s cadence from 0.x.
* Clean SIGINT / SIGTERM shutdown: per-client handler tasks are tracked and cancelled, so blocked reader.read() calls no longer pin the event loop open.
This commit is contained in:
12 files changed
+1693
-694
No files matched your search
@@ -1,16 +1,27 @@
|
||||
# DongLoRa Mux (Python)
|
||||
|
||||
USB multiplexer daemon — lets multiple applications share one DongLoRa
|
||||
dongle simultaneously.
|
||||
dongle simultaneously. Speaks the **DongLoRa Protocol v2** wire protocol
|
||||
(see [`PROTOCOL.md`](https://github.com/donglora/firmware/blob/main/PROTOCOL.md)).
|
||||
|
||||
## What It Does
|
||||
|
||||
- Owns the USB serial connection exclusively
|
||||
- Exposes a Unix domain socket (and optional TCP) speaking the same
|
||||
COBS-framed protocol
|
||||
- RxPacket frames broadcast to all connected clients
|
||||
- StartRx/StopRx reference-counted across clients
|
||||
- SetConfig locked once set (single client can change freely)
|
||||
- Owns the USB serial connection exclusively (flock-protected).
|
||||
- Exposes a Unix domain socket and optional TCP listener speaking the
|
||||
DongLoRa Protocol v2 frame format.
|
||||
- **Tag-correlated solicited responses** — concurrent commands from
|
||||
multiple clients are routed back to the originator via a mux-allocated
|
||||
device-tag map (`PROTOCOL.md §13.6`).
|
||||
- **`RX` fan-out** — every over-the-air RX is broadcast to all clients.
|
||||
- **TX loopback** (`§13.4`) — a successful TX from one client is
|
||||
delivered to every other client as an `RX` event with
|
||||
`origin = LOCAL_LOOPBACK`.
|
||||
- **Smart `SET_CONFIG` arbitration** (`§13.2`) — the first successful
|
||||
call locks the config; cross-client calls get `ALREADY_MATCHED` /
|
||||
`LOCKED_MISMATCH` with the lock owner's params echoed. A single
|
||||
connected client can still retune freely (scanner mode).
|
||||
- **`RX_START` / `RX_STOP` ref-counting** — the radio stays in RX as
|
||||
long as any client wants it.
|
||||
|
||||
## Running
|
||||
|
||||
@@ -22,5 +33,23 @@ just run --tcp 5741 --port /dev/ttyACM0 # with options
|
||||
|
||||
## Depends On
|
||||
|
||||
- [`donglora`](https://github.com/donglora/client-py) — the Python client library
|
||||
(protocol encoding, device discovery)
|
||||
- [`donglora`](https://github.com/donglora/client-py) (>= 1.0) — the
|
||||
Python client library. Provides the wire-level frame codec, event
|
||||
parsers, and USB discovery helpers this mux is built on.
|
||||
|
||||
## Relationship to `donglora-mux` (Rust)
|
||||
|
||||
This package is a Python port of the Rust
|
||||
[`donglora-mux`](https://crates.io/crates/donglora-mux) daemon. The two
|
||||
implementations are **functionally equivalent** — they speak the same
|
||||
DongLoRa Protocol v2 wire protocol, implement the same `§13.2` / `§13.4` multi-client
|
||||
semantics, and are drop-in interchangeable in front of the same dongle.
|
||||
Pick whichever fits your ops environment:
|
||||
|
||||
- **Rust (`cargo install donglora-mux`)** — single binary, no runtime
|
||||
deps, ~5 MB release build. Better for production / embedded Linux.
|
||||
- **Python (`pip install donglora-mux`)** — easy to hack on, integrates
|
||||
with a Python toolchain, useful when Rust isn't an option.
|
||||
|
||||
If you find a behavioural divergence between the two, that's a bug —
|
||||
please file it.
|
||||
Reference in new issue
Block a user