Commit d3f58c6e by PLN (Algolia)

fix(lcxl3): OLED home repaint — dedup on what was SENT, reassert every 20s

The footer froze at '?REC 00:10' on the glass during the first real take. No
code-level freeze reproduced; what WAS wrong: the ghost mirror recomputed state
instead of mirroring device writes (so diagnosis lied), and the 2s tick wrote
unconditionally with no dedup. Now: _home_cache records the string actually
sent, the tick writes only on change (repaint ≤2s, revert ≤2s after stale), and
the 20s label-reassert forces a full repaint so dedup can never mask a device-
side reset. Hardware verdict awaits the next recorded take.
parent 7e64736b
......@@ -705,6 +705,12 @@ class Driver:
self._last_touch = 0.0
self._oled_lines = ("", "", "")
self._oled_up_at = 0.0
# What is ACTUALLY on the STATIONARY target's fields right now, by
# field index — the redraw tick dedups against this (never against
# upstream state like the rec record) so an unchanged render costs
# no SysEx, and ghost_write mirrors it instead of recomputing a
# value that may already have drifted from what was sent.
self._home_cache: dict[int, str] = {}
# Who is drawing the screen right now. The whole bug was not knowing.
self._oled_owner = "driver"
self._labels: list[tuple[int, int, str, bool]] = []
......@@ -1061,6 +1067,10 @@ class Driver:
for field, line in enumerate((t, pmt, val)):
self.surface.set_text(L.TGT_STATIONARY, field, line)
self.surface.configure_display(L.TGT_STATIONARY, 0x7F)
# A full home paint (track load, save, reassert) is authoritative —
# stamp the dedup cache with what we just actually sent, so the next
# periodic tick compares against reality instead of a stale guess.
self._home_cache = {0: t, 1: pmt, 2: val}
# ------------------------------------------------- the per-control screen ----
......@@ -1165,9 +1175,13 @@ class Driver:
def ghost_write(self) -> None:
try:
age = time.time() - self._oled_up_at
t, pmt, val = self.lang.home_lines(self.track_name, self._home_detail(),
self._home_footer())
home = (t, pmt, val)
# Mirror what was actually SENT (the dedup cache), not a fresh
# recompute — a second independent read of the gig-log tail here
# used to mean the ghost could show a value one tick ahead of (or
# briefly disagreeing with) the real device write.
home = (self._home_cache.get(0, self.track_name.upper()[:16]),
self._home_cache.get(1, self._home_detail()),
self._home_cache.get(2, self._home_footer()))
cc = getattr(self, "_last_cc", None)
if age >= 1.2:
up, lines = "STATIONARY (home, ours)", home
......@@ -1372,11 +1386,21 @@ class Driver:
self.follow_tick()
last_follow = now
if self.lang and now - last_home > 2.0:
# tick the lap clock on home row 2, and the REC timer (if any)
# on row 3; a field write on the stationary target needs no
# re-summon
self.surface.set_text(L.TGT_STATIONARY, 1, self._home_detail())
self.surface.set_text(L.TGT_STATIONARY, 2, self._home_footer())
# Tick the lap clock on home row 2, and the REC timer (if any)
# on row 3. Always RECOMPUTE every 2s (that is how a fresh
# recording, or one going stale, gets noticed at all) but only
# WRITE when the rendered string actually changed — dedup on
# the final string, never on upstream state, so a steady
# "ParVagues" or a steady "REC h:mm:ss" (same second twice)
# costs no SysEx/ACK round trip. A field write on the
# stationary target needs no re-summon.
detail, footer = self._home_detail(), self._home_footer()
if detail != self._home_cache.get(1):
self.surface.set_text(L.TGT_STATIONARY, 1, detail)
self._home_cache[1] = detail
if footer != self._home_cache.get(2):
self.surface.set_text(L.TGT_STATIONARY, 2, footer)
self._home_cache[2] = footer
self.ghost_write()
last_home = now
if self.lang and now - last_labels > 20.0:
......@@ -1384,8 +1408,13 @@ class Driver:
# replug, Ardour grabbing the port, the LED endpoint stalling)
# drops everything we configured. Labels are cheap to restate —
# 32 SysEx every 20 s — and a screen that has silently reverted
# to "0" is indistinguishable from the bug we just fixed.
# to "0" is indistinguishable from the bug we just fixed. The
# home text dedup above is deliberately blind to a device-side
# reset for the same reason — it only knows what WE last sent,
# not what the glass shows — so ride this same 20s heartbeat
# to force a full, unconditional home repaint too.
self.oled_labels(quiet=True)
self.oled_home()
last_labels = now
time.sleep(0.001)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment