"""Pretend to be a Micromate on a serial port: log what arrives, reply to POLL. Proves the modem's return path (serial -> TCP) independently of the real unit. """ import os, select, sys, termios, time path, baud = sys.argv[1], int(sys.argv[2]) if len(sys.argv) > 2 else 115200 B = {9600: termios.B9600, 38400: termios.B38400, 115200: termios.B115200}[baud] fd = os.open(path, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK) a = termios.tcgetattr(fd) a[0] = a[1] = a[3] = 0 a[2] = termios.CS8 | termios.CREAD | termios.CLOCAL a[4] = a[5] = B a[6] = list(a[6]); a[6][termios.VMIN] = 0; a[6][termios.VTIME] = 0 termios.tcsetattr(fd, termios.TCSANOW, a) termios.tcflush(fd, termios.TCIOFLUSH) # A real POLL probe reply, captured from UM12947 on 2026-09-24. REPLY = bytes.fromhex("0200c5a4000000000000300000000000000099") + b"\x03" print(f"fake unit on {path} @ {baud}; will answer any inbound frame", flush=True) while True: r, _, _ = select.select([fd], [], [], 1.0) if not r: continue data = os.read(fd, 4096) if not data: continue ts = time.strftime("%H:%M:%S") print(f"{ts} IN {len(data):3} B {data.hex(' ')}", flush=True) time.sleep(0.02) os.write(fd, REPLY) print(f"{ts} OUT {len(REPLY):3} B {REPLY.hex(' ')} <- canned POLL reply", flush=True)