Serial monitor redesign: Test box button, InfluxDB row in the checklist

- Test box: restart + start-up capture + online InfluxDB check (data newer
  than the restart), with a status strip; checklist and findings are the
  main view, live log / guided troubleshooting / box details are tabs,
  rarely used actions in a More menu
- light palette on all systems (macOS dark mode made the colours unreadable)
- new rule influx.invalid_value: -inf from a silent microphone makes
  InfluxDB reject every upload (HTTP 400); guide end names it
- exclusive serial port on macOS/Linux: two tool windows no longer split
  the log between them
- InfluxDB check: `since` filter, neutral wording when nothing new arrives

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
KrzyszurandClaude Opus 5.5 committed 2026-10-01 00:06:48 +02:00
1 parent 66cb804102
commit bb933ff748
13 files changed
+422 -83

No files matched your search

+13 -4
View File
@@ -51,6 +51,7 @@ class SerialManager:
self._thread: threading.Thread | None = None
self._stop = threading.Event()
self._target: _Target | None = None
self._open_error = ""
self.state = State.DISCONNECTED
# ---- public API (any thread) -------------------------------------
@@ -89,9 +90,14 @@ class SerialManager:
info = find_port(self._target.serial_number, self._target.device)
if info is None:
return None
posix = sys.platform != "win32"
# serial_for_url also accepts test URLs such as "loop://".
# exclusive: a second tool window must not read the same port (on
# macOS/Linux both would get half of the lines). COM ports on Windows
# are always exclusive.
ser = serial.serial_for_url(
info.device, baudrate=self._baud, timeout=READ_TIMEOUT_S, do_not_open=True
info.device, baudrate=self._baud, timeout=READ_TIMEOUT_S, do_not_open=True,
**({"exclusive": True} if posix else {}),
)
# On USB-Serial-JTAG, RTS=1/DTR=0 holds the chip in reset and DTR=1
# during reset selects download mode.
@@ -100,7 +106,6 @@ class SerialManager:
# macOS/Linux: the OS asserts both lines on open and pyserial then
# writes DTR before RTS, passing through RTS=1/DTR=0 and resetting
# the box on every connect. Drop RTS first, then DTR.
posix = sys.platform != "win32"
if not posix:
ser.dtr = False
ser.rts = False
@@ -108,8 +113,12 @@ class SerialManager:
ser.open()
if posix:
ser.dtr = False
except (serial.SerialException, OSError):
except (serial.SerialException, OSError) as exc:
busy = "exclusive" in str(exc).lower() or "busy" in str(exc).lower() or "access" in str(exc).lower()
self._open_error = ("The port is used by another program (another Technician Tool window?)"
if busy else "")
return None
self._open_error = ""
self._target.device = info.device # COM number / node may change
return ser
@@ -143,7 +152,7 @@ class SerialManager:
ser = self._open()
if ser is None:
if not announced_wait:
self._set_state(State.WAITING, "Plug in / power on the sensorbox")
self._set_state(State.WAITING, self._open_error or "Plug in / power on the sensorbox")
announced_wait = True
self._drain_cmds(None)
time.sleep(REOPEN_POLL_S)