polish(da12): auto-pull history on open; framer-cap HW note; seam tests (review)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-05 10:17:50 -04:00
parent a3e2407b0f
commit 6089ba1913
5 changed files with 23 additions and 1 deletions

View File

@@ -109,7 +109,7 @@ STALENESS = {
"danger": (Color.DANGER, Color.DANGER_FILL),
}
# Trend-chart series/element colors (DA-12 History dialog). 7-char #RRGGBB hex.
# Trend-chart series/element colors (used by module chart dialogs). 7-char #RRGGBB hex.
CHART = {
"average": "#0096FF", # filtered-average line (brand azure)
"current": "#7FB2D9", # instantaneous-current line (muted)

View File

@@ -61,6 +61,7 @@ class HistoryDialog(QDialog):
controller.historyChanged.connect(self._on_history_changed)
controller.limitsChanged.connect(self._draw_limit_lines)
self._reload()
self.pull_history() # backfill {J} once on open (no-op if not connected)
def closeEvent(self, event) -> None:
# Stop reacting to controller signals once closed (the controller outlives us).

View File

@@ -136,6 +136,12 @@ not been validated against a real DA-12.** Items to confirm:
several. Our decoder handles one frame = one batch; `MeasurementHistory.merge_backfill`
accumulates and deduplicates across multiple calls, so either way is handled, but
verify the frame count on hardware.
**Framer cap:** `StreamFramer` (`cim_suite/modules/da12/protocol/framing.py`) has a
4096-char incomplete-frame guard. A single `{J}` frame exceeding ~4096 chars
(roughly >290 records) split across serial reads would be truncated. If hardware
confirms one large `{J}` frame rather than multiple smaller ones, raise the
`max_buffer` argument passed to `StreamFramer` in
`cim_suite/modules/da12/domain/controller.py` accordingly.
2. **`{c}``{J}` round-trip** — send `request_history` for a sensor with known
buffered records; confirm a `{J}` reply arrives within a normal response window

View File

@@ -32,3 +32,10 @@ def test_noise_only_does_not_grow_unbounded():
f = StreamFramer(max_buffer=8)
assert f.feed("z" * 50) == []
assert len(f._buf) <= 8
def test_framer_reassembles_frame_split_across_two_feeds():
f = StreamFramer()
assert f.feed("{J00000002\t60D54E80\t01") == [] # no close yet
out = f.feed("36\t60D54EBC\t013B}")
assert out == ["J00000002\t60D54E80\t0136\t60D54EBC\t013B"]

View File

@@ -77,3 +77,11 @@ def test_dialog_disconnects_on_close(qtbot):
dlg.close()
ctrl._process("{C01\t0066}") # should NOT trigger a reload now
assert dlg.point_count() == before
def test_dialog_expected_interval_reads_setting_6(qtbot):
ctrl = StationController()
ctrl.settings.upsert(m.SettingLine(row=6, text="Update Interval\t30", type_code=0))
dlg = HistoryDialog(ctrl, _record(sid=1), None)
qtbot.addWidget(dlg)
assert dlg._expected_interval_ms() == 30000