Add TCP/modem transport (Sierra Wireless RV55/RX55 field units)

- minimateplus/transport.py: add TcpTransport — stdlib socket-based transport
  with same interface as SerialTransport. Overrides read_until_idle() with
  idle_gap=1.5s to absorb the modem's 1-second serial data forwarding buffer.
- minimateplus/client.py: make `port` param optional (default "") so
  MiniMateClient works cleanly when a pre-built transport is injected.
- minimateplus/__init__.py: export SerialTransport and TcpTransport.
- sfm/server.py: add `host` / `tcp_port` query params to all device endpoints.
  New _build_client() helper selects TCP or serial transport automatically.
  OSError (connection refused, timeout) now returns HTTP 502.
- docs/instantel_protocol_reference.md: add changelog entry and full §14
  (TCP/Modem Transport) documenting confirmed transparent passthrough, no ENQ
  on connect, modem forwarding delay, call-up vs ACH modes, and hardware note
  deprecating Raven X in favour of RV55/RX55.

Usage: GET /device/info?host=<modem_ip>&tcp_port=12345
This commit is contained in:
Brian Harrison
2026-03-31 00:44:50 -04:00
parent b8032e0578
commit 51d1aa917a
5 changed files with 402 additions and 53 deletions

View File

@@ -58,6 +58,7 @@
| 2026-03-12 | §11 | **RESOLVED — BAD CHK false positives on BW POLL frames:** Parser bug — BW frame terminator (`03 41`, ETX+ACK) was being included in the de-stuffed payload instead of being stripped as framing. BW frames end with bare `0x03` (not `10 03`). Fix: strip trailing `03 41` from BW payloads before checksum computation. |
| 2026-03-30 | §3, §5.1 | **CONFIRMED — BW→S3 two-step read offset is at payload[5], NOT payload[3:4].** All BW read-command frames have `payload[3] = 0x00` and `payload[4] = 0x00` unconditionally. The two-step offset byte lives at `payload[5]`: `0x00` for the length-probe step, `DATA_LEN` for the data-fetch step. Validated against all captured frames in `bridges/captures/3-11-26/raw_bw_*.bin` — every frame is an exact bit-for-bit match when built with offset at `[5]`. The `page_hi`/`page_lo` framing in the docstring was a misattribution from the S3-side response layout (where `[3]`/`[4]` ARE page bytes). |
| 2026-03-30 | §4, §5.2 | **CONFIRMED — S3 probe response page_key is always 0x0000.** The S3 response to a length-probe step does NOT carry the data length back in `page_hi`/`page_lo`. Both bytes are `0x00` in every observed probe response. Data lengths for each SUB are fixed constants (see §5.1 table). The `minimateplus` library now uses a hardcoded `DATA_LENGTHS` dict rather than trying to read the length from the probe response. |
| 2026-03-31 | §12 TCP Transport | **NEW SECTION — TCP/modem transport confirmed transparent from Blastware Operator Manual (714U0301 Rev 22).** Key facts confirmed: (1) Protocol bytes over TCP are bit-for-bit identical to RS-232 — no handshake framing. (2) No ENQ byte on TCP connect (`Enable ENQ on TCP Connect: 0-Disable` in Raven ACEmanager). (3) Raven modem `Data Forwarding Timeout = 1 second` — modem buffers serial bytes up to 1s before forwarding over TCP; `TcpTransport.read_until_idle` uses `idle_gap=1.5s` to compensate. (4) TCP port is user-configurable (12335 in manual example; user's install uses 12345). (5) Baud rate over serial link to modem is 38400,8N1 regardless of TCP path. (6) ACH (Auto Call Home) = INBOUND to server (unit calls home); "call up" = OUTBOUND from client (Blastware/SFM connects to modem IP). `TcpTransport` implements outbound (call-up) mode. |
---
@@ -889,6 +890,129 @@ Build in this order — each step is independently testable:
---
## 14. TCP / Modem Transport
> ✅ **CONFIRMED — 2026-03-31** from Blastware Operator Manual 714U0301 Rev 22 §4.4 and ACEmanager Raven modem configuration screenshots.
The MiniMate Plus protocol is **fully transport-agnostic at the byte level**. The same DLE-framed S3/BW frame stream that flows over RS-232 is transmitted unmodified over a TCP socket. No additional framing, handshake bytes, or session tokens are added at the application layer.
---
### 14.1 Two Usage Modes
**"Call Up" (Outbound TCP — SFM connects to modem)**
Blastware or SFM opens a TCP connection to the modem's static IP address on its device port. The modem bridges the TCP socket to its RS-232 serial port, which is wired directly to the MiniMate Plus. From the protocol perspective this is identical to a direct serial connection.
```
SFM ──TCP──► Raven modem ──RS-232──► MiniMate Plus
(static IP, port N) (38400,8N1)
```
This is the mode implemented by `TcpTransport(host, port)`. Typical call:
```
GET /device/info?host=203.0.113.5&tcp_port=12345
```
**"Call Home" / ACH (Inbound TCP — unit calls the server)**
The MiniMate Plus is configured with an IP address and port. On an event trigger or scheduled time it powers up its modem, which establishes a TCP connection outbound to the server. Blastware (or a future SFM ACH listener) accepts the incoming connection. After the unit connects, the PC has a configurable "Wait for Connection" window to send the first command before the unit times out and hangs up.
```
MiniMate Plus ──RS-232──► Raven modem ──TCP──► ACH server (listening)
(static office IP, port N)
```
`TcpTransport` is a **client** (outbound connect only). A separate `AchServer` listener component is needed for this mode — not yet implemented.
---
### 14.2 No Application-Layer Handshake on TCP Connect
**Confirmed from ACEmanager configuration screenshot:**
```
Enable ENQ on TCP Connect: 0-Disable
```
When a TCP connection is established (in either direction), **no ENQ byte or other handshake marker is sent** by the modem before the protocol stream starts. The first byte from either side is a raw protocol byte — for SFM-initiated call-up, SFM sends POLL_PROBE immediately after `connect()`.
No banner, no "CONNECT" string, no Telnet negotiation preamble. The Raven modem's TCP dialog is configured with:
| ACEmanager Setting | Value | Meaning |
|---|---|---|
| TCP Auto Answer | 2 — Telnet Server | TCP mode (transparent pass-through, not actually Telnet) |
| Telnet Echo Mode | 0 — No Echo | No echo of received bytes |
| Enable ENQ on TCP Connect | 0 — Disable | No ENQ byte on connect |
| TCP Connect Response Delay | 0 | No delay before first byte |
| TCP Idle Timeout | 0 | No modem-level idle disconnect |
---
### 14.3 Modem Serial Port Configuration
> **Hardware note:** The Raven X modem shown in the Blastware manual is 3G-only and no longer operational (3G network shutdown). The current field hardware is the **Sierra Wireless RV55** (and newer RX55). Both run ALEOS firmware and have an identical ACEmanager web UI — the settings below apply to all three generations.
The modem's RS-232 port (wired to the MiniMate Plus) must be configured as:
| ACEmanager Setting | Value |
|---|---|
| Configure Serial Port | **38400,8N1** |
| Flow Control | None |
| DB9 Serial Echo | OFF |
| Data Forwarding Timeout | **1 second** (S50=1) |
| Data Forwarding Character | 0 (disabled) |
The **Data Forwarding Timeout** is the most protocol-critical setting. The modem **accumulates bytes from the RS-232 port for up to 1 second** before forwarding them as a TCP segment. This means:
- A large S3 response frame may arrive as multiple TCP segments with up to 1-second gaps between them.
- A `read_until_idle` implementation with `idle_gap < 1.0 s` will **incorrectly declare the frame complete mid-stream**.
- `TcpTransport.read_until_idle` overrides the default `idle_gap=0.05 s` to `idle_gap=1.5 s` to compensate.
If connecting to a unit via a direct Ethernet connection (no serial modem in the path), the 1.5 s idle gap will still work but will feel slower. In that case you can pass `idle_gap=0.1` explicitly.
---
### 14.4 Connection Timeouts on the Unit Side
The MiniMate Plus firmware has two relevant timeouts configurable via Blastware's Call Home Setup dialog:
| Timeout | Description | Impact |
|---|---|---|
| **Wait for Connection** | Seconds after TCP connect during which the unit waits for the first BW frame. If nothing arrives, unit terminates the session. | SFM must send POLL_PROBE within this window after `connect()`. Default appears short (≈1530 s). |
| **Serial Idle Time** | Seconds of inactivity after which the unit terminates the connection. | SFM must complete its work and disconnect cleanly — or send periodic keep-alive frames — within this window. |
For our `TcpTransport` + `MiniMateProtocol` stack, both timeouts are satisfied automatically because `connect()` is immediately followed by `protocol.poll()` which sends POLL_PROBE, and the full session (POLL + read + disconnect) typically completes in < 30 seconds.
---
### 14.5 Port Numbers
The TCP port is **user-configurable** in both Blastware and the modem. There is no universally fixed port.
| Setting location | Value in manual example | Value in user's install |
|---|---|---|
| Blastware TCP Communication dialog | 12335 | 12345 |
| Raven ACEmanager Destination Port | 12349 (UDP example) | varies |
`TcpTransport` defaults to `DEFAULT_TCP_PORT = 12345` which matches the user's install. This can be overridden by the `port` argument or the `tcp_port` query parameter in the SFM server.
---
### 14.6 ACH Session Lifecycle (Call Home Mode — Future)
When the unit calls home under ACH, the session lifecycle from the unit's perspective is:
1. Unit triggers (event or scheduled time)
2. Unit powers up modem, dials / connects TCP to server IP:port
3. Unit waits for "Wait for Connection" window for first BW frame from server
4. Server sends POLL_PROBE → unit responds with POLL_RESPONSE (same as serial)
5. Server reads serial number, full config, events as needed
6. Server disconnects (or unit disconnects on Serial Idle Time expiry)
7. Unit powers modem down, returns to monitor mode
Step 4 onward is **identical to the serial/call-up protocol**. The only difference from our perspective is that we are the **listener** rather than the **connector**. A future `AchServer` class will accept the incoming TCP connection and hand the socket to `TcpTransport` for processing.
---
## Appendix A — s3_bridge Capture Format