feat: added raw capture pipeline. added simple windows gui.

This commit is contained in:
serversdwn
2026-03-02 19:30:02 -05:00
parent 967a5b2dad
commit 9db55ffcee
6 changed files with 440 additions and 14 deletions

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""
s3_bridge.py — S3 <-> Blastware serial bridge with raw binary capture + DLE-aware text framing
Version: v0.5.0
Version: v0.5.1
Whats new vs v0.4.0:
- .bin is now a TRUE raw capture stream with direction + timestamps (record container format).
@@ -10,6 +10,8 @@ Whats new vs v0.4.0:
- frame end = 0x10 0x03 (DLE ETX)
(No longer splits on bare 0x03.)
- Marks/Info are stored as proper record types in .bin (no unsafe sentinel bytes).
- Optional raw taps: use --raw-bw / --raw-s3 to also dump byte-for-byte traffic per direction
with no headers (for tools that just need a flat stream).
BIN record format (little-endian):
[type:1][ts_us:8][len:4][payload:len]
@@ -84,12 +86,15 @@ def pack_u64_le(n: int) -> bytes:
class SessionLogger:
def __init__(self, path: str, bin_path: str):
def __init__(self, path: str, bin_path: str, raw_bw_path: Optional[str] = None, raw_s3_path: Optional[str] = None):
self.path = path
self.bin_path = bin_path
self._fh = open(path, "a", buffering=1, encoding="utf-8", errors="replace")
self._bin_fh = open(bin_path, "ab", buffering=0)
self._lock = threading.Lock()
# Optional pure-byte taps (no headers). BW=Blastware tx, S3=device tx.
self._raw_bw = open(raw_bw_path, "ab", buffering=0) if raw_bw_path else None
self._raw_s3 = open(raw_s3_path, "ab", buffering=0) if raw_s3_path else None
def log_line(self, line: str) -> None:
with self._lock:
@@ -103,6 +108,11 @@ class SessionLogger:
self._bin_fh.write(header)
if payload:
self._bin_fh.write(payload)
# Raw taps: write only the payload bytes (no headers)
if rec_type == REC_BW and self._raw_bw:
self._raw_bw.write(payload)
if rec_type == REC_S3 and self._raw_s3:
self._raw_s3.write(payload)
def log_mark(self, label: str) -> None:
ts = now_ts()
@@ -122,6 +132,10 @@ class SessionLogger:
finally:
self._fh.close()
self._bin_fh.close()
if self._raw_bw:
self._raw_bw.close()
if self._raw_s3:
self._raw_s3.close()
class DLEFrameSniffer:
@@ -311,6 +325,8 @@ def main() -> int:
ap.add_argument("--s3", default="COM5", help="S3-side COM port (default: COM5)")
ap.add_argument("--baud", type=int, default=38400, help="Baud rate (default: 38400)")
ap.add_argument("--logdir", default=".", help="Directory to write session logs into (default: .)")
ap.add_argument("--raw-bw", default=None, help="Optional file to append raw bytes sent from BW->S3 (no headers)")
ap.add_argument("--raw-s3", default=None, help="Optional file to append raw bytes sent from S3->BW (no headers)")
ap.add_argument("--quiet", action="store_true", help="No console heartbeat output")
ap.add_argument("--status-every", type=float, default=0.0, help="Seconds between console heartbeat lines (default: 0 = off)")
args = ap.parse_args()
@@ -329,10 +345,14 @@ def main() -> int:
ts = _dt.datetime.now().strftime("%Y%m%d_%H%M%S")
log_path = os.path.join(args.logdir, f"s3_session_{ts}.log")
bin_path = os.path.join(args.logdir, f"s3_session_{ts}.bin")
logger = SessionLogger(log_path, bin_path)
logger = SessionLogger(log_path, bin_path, raw_bw_path=args.raw_bw, raw_s3_path=args.raw_s3)
print(f"[LOG] Writing hex log to {log_path}")
print(f"[LOG] Writing binary log to {bin_path}")
if args.raw_bw:
print(f"[LOG] Raw tap BW->S3 -> {args.raw_bw}")
if args.raw_s3:
print(f"[LOG] Raw tap S3->BW -> {args.raw_s3}")
logger.log_info(f"s3_bridge {VERSION} start")
logger.log_info(f"BW={args.bw} S3={args.s3} baud={args.baud}")
@@ -396,4 +416,4 @@ def main() -> int:
if __name__ == "__main__":
raise SystemExit(main())
raise SystemExit(main())