Skip to content

fix(ticc): hard re-init on sustained wedge to recover a re-enumerated TICC (I-160351a) - #306

Open
bobvan wants to merge 1 commit into
mainfrom
delta/ticc-reinit-wedge
Open

fix(ticc): hard re-init on sustained wedge to recover a re-enumerated TICC (I-160351a)#306
bobvan wants to merge 1 commit into
mainfrom
delta/ticc-reinit-wedge

Conversation

@bobvan

@bobvan bobvan commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Review happens on the dayplan, not GitHub. Discussion/approval for this change goes through dayplan item I-160351a-delta — please don't review via GitHub comments.

Problem

The TICC reader reopens through a cached _SharedTiccPort, and acquire() only opens the serial when serial is None (the warm-reopen path — HUPCL is preserved so reopens don't reboot the Arduino). After the TICC USB re-enumerates — e.g. a power-cycle to clear a firmware hang — the old /dev node is gone but the cached fd is still non-None, so every reopen reuses the dead fd and never re-resolves the device path. The reader wedges: opens "succeed", trigger LEDs flash, zero events, indefinitely.

Observed 2026-07-24 on PiFace: ~2.6 h stale (Rb reference lost) → device USB-power-cycled on return → the running reader never re-latched; only an engine restart recovered it.

Fix

  • ticc.py: reset_shared_port(port, baud) closes + nulls the cached _SharedTiccPort so the next acquire() does a fresh _open_serial() that re-resolves /dev/ticcN.
  • peppar_fix_engine.py::ticc_reader(): after a sustained stale streak (REINIT_AFTER_REOPENS ≈ 30 s) escalate to reset_shared_port(), spaced so it keeps retrying while the device is absent. The warm reopen is left intact for transient hiccups (no needless Arduino reboots).

Tests

  • tests/test_ticc_reinit.py (5 new): reset closes+nulls; unknown / wrong-key no-op; acquire-after-reset re-opens (the load-bearing recovery); warm reopen without reset does NOT re-open (regression guarding the no-reboot path).
  • All 37 TICC-touching tests pass. Full suite: 2538 pass; the 21 failures are the pre-existing full-suite logging-capture pollution (cf. fdbe017) — they pass in isolation, unrelated to this change.

Remaining: hardware validation

Needs a lab window + a TICC to power-cycle under a running engine: unplug/replug the TICC, confirm within ~30–60 s the reader logs "forcing hard re-init" and ticc.csv resumes without an engine restart (the exact scenario that wedged Friday), and that normal-op transient reopens do not escalate. Tracked on I-160351a-delta.

🤖 Generated with Claude Code

… TICC (I-160351a)

The reader reopens through a cached _SharedTiccPort whose acquire() only
opens when serial is None (warm-reopen path, HUPCL preserved to avoid
Arduino reboots). After the TICC USB re-enumerates — e.g. a power-cycle to
clear a firmware hang — the old /dev node is gone but the cached fd is still
non-None, so every reopen reuses the dead fd and never re-resolves the
device path. The reader wedges: opens "succeed", LEDs flash, zero events,
forever (observed 2026-07-24 on PiFace; only an engine restart recovered it).

Add reset_shared_port() to close+null the cached port so the next acquire()
does a fresh open (re-resolving /dev/ticcN), and escalate to it from
ticc_reader() after a sustained stale streak (REINIT_AFTER_REOPENS, ~30 s),
spaced so it keeps retrying. The warm reopen is left intact for transient
hiccups (regression-tested).

Tests: tests/test_ticc_reinit.py (5 new). All 37 TICC-touching tests pass.
Full suite 2538 pass; the 21 unrelated failures are the known pre-existing
full-suite logging-capture pollution (cf. fdbe017) — they pass in isolation.
Hardware validation (recover a real re-enumerated TICC) pending a lab window.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant