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:
1 parent
66cb804102
commit
bb933ff748
13 files changed
+422
-83
No files matched your search
@@ -28,6 +28,7 @@ DEFAULT_BUCKET = "sensorbox"
|
||||
LOOKBACK = "-30d"
|
||||
FRESH_MIN = 15 # like SEEN_OK_MIN in the service system
|
||||
FUTURE_TOLERANCE_MIN = 10 # newer than now + this: the box clock is ahead
|
||||
SINCE_SLACK_S = 60 # clock difference box / laptop when counting "since the test started"
|
||||
TIMEOUT_S = 20
|
||||
|
||||
# Sensor groups that every box must upload: name -> measurement prefixes.
|
||||
@@ -122,13 +123,17 @@ def _ago(now: dt.datetime, t: dt.datetime) -> str:
|
||||
return f"{minutes / 1440:.0f} days ago"
|
||||
|
||||
|
||||
def classify(latest: dict[str, dt.datetime], now: dt.datetime, fresh_min: int = FRESH_MIN) -> InfluxCheck:
|
||||
def classify(latest: dict[str, dt.datetime], now: dt.datetime, fresh_min: int = FRESH_MIN,
|
||||
since: dt.datetime | None = None) -> InfluxCheck:
|
||||
"""`since`: only data newer than this counts (the box was restarted then)."""
|
||||
def newest(prefixes: tuple[str, ...]) -> dt.datetime | None:
|
||||
times = [t for m, t in latest.items() if m.startswith(prefixes)]
|
||||
return max(times) if times else None
|
||||
|
||||
def fresh(t: dt.datetime | None) -> bool:
|
||||
return t is not None and (now - t).total_seconds() <= fresh_min * 60
|
||||
if t is None or (now - t).total_seconds() > fresh_min * 60:
|
||||
return False
|
||||
return since is None or (since - t).total_seconds() <= SINCE_SLACK_S
|
||||
|
||||
details = []
|
||||
for label, prefixes in (("Device status", (STATUS,)), ("Microphone", (MIC,)),
|
||||
@@ -155,9 +160,11 @@ def classify(latest: dict[str, dt.datetime], now: dt.datetime, fresh_min: int =
|
||||
+ ", ".join(missing), latest, missing, details)
|
||||
if lagging:
|
||||
# Offline now, but the history already shows the fault.
|
||||
return InfluxCheck(SENSORS_MISSING, f"Box is offline now (last status {_ago(now, status_t)}); "
|
||||
"before that its status arrived without sensor data: " + ", ".join(missing),
|
||||
return InfluxCheck(SENSORS_MISSING, f"Nothing new in InfluxDB (last status {_ago(now, status_t)}); "
|
||||
"before that the status arrived without sensor data: " + ", ".join(missing),
|
||||
latest, missing, details)
|
||||
if since is not None:
|
||||
return InfluxCheck(SILENT, "No new data in InfluxDB since the test started", latest, missing, details)
|
||||
return InfluxCheck(SILENT, f"No recent data in InfluxDB (nothing in the last {fresh_min} min)",
|
||||
latest, missing, details)
|
||||
|
||||
@@ -173,7 +180,8 @@ def query_latest(cfg: InfluxConfig, mac: str) -> dict[str, dt.datetime]:
|
||||
return parse_csv(resp.read().decode("utf-8"))
|
||||
|
||||
|
||||
def check_device(cfg: InfluxConfig | None, mac: str, now: dt.datetime | None = None) -> InfluxCheck:
|
||||
def check_device(cfg: InfluxConfig | None, mac: str, now: dt.datetime | None = None,
|
||||
since: dt.datetime | None = None) -> InfluxCheck:
|
||||
"""Blocking; run it off the UI thread. Never raises."""
|
||||
mac = "".join(c for c in (mac or "") if c.isalnum()).upper()
|
||||
if cfg is None or not cfg.token:
|
||||
@@ -191,4 +199,4 @@ def check_device(cfg: InfluxConfig | None, mac: str, now: dt.datetime | None = N
|
||||
return InfluxCheck(UNAVAILABLE, f"InfluxDB not reachable from this computer ({reason})")
|
||||
except (ValueError, csv.Error) as exc:
|
||||
return InfluxCheck(UNAVAILABLE, f"Unexpected InfluxDB answer ({exc})")
|
||||
return classify(latest, now or dt.datetime.now(dt.timezone.utc))
|
||||
return classify(latest, now or dt.datetime.now(dt.timezone.utc), since=since)
|
||||
Reference in new issue
Block a user