修复回放标定选择与曲线显示数据保留

让标定选择进入处理和缓存,避免显示设置清空曲线。102 项针对性检查通过,全量门禁仍有停止时序、模型尺寸与原生崩溃问题。
This commit is contained in:
ykcheungab
2026-10-19 16:30:36 +08:00
parent 768f560820
commit 9d4563e929
12 changed files with 478 additions and 97 deletions
+22 -16
View File
@@ -1,9 +1,8 @@
# Replay panel contract
The replay panel plays one recording — or one video — against a single cursor. Each
capability states its requirement, data source and a falsifying check. The result format and
playback semantics are owned by Part A ([replay-analysis.md](replay-analysis.md)) and Part B
([replay-analysis-part-b.md](replay-analysis-part-b.md)).
The replay panel plays one recording or video against one cursor. Part A
([replay-analysis.md](replay-analysis.md)) and Part B
([replay-analysis-part-b.md](replay-analysis-part-b.md)) own format and playback semantics.
Status: Parts A and B shipped at `cf49b08`, the depth channel at `9e20fd0`, the result-aware mode
plan at `1aa4543`. The cache root and the lifetime sweep are implemented; the display-control
@@ -11,12 +10,10 @@ separation (Part C) is not.
## 1. Load a video
Selecting a video always gives a playable timeline, and the panel says what the file is.
`load_video` calls a folder holding `forces.csv` beside the video a recording; anything else
opens as a bare video (`VideoOnlySource`) and plays, with `playback.video_only_opened` on the
rail and `playback.video_only_notice` in the notes. A file that cannot be decoded is refused
with its reason. What a bare video lacks is force rows *measured by a device*, and
`session.json` metadata.
`load_video` treats video beside `forces.csv` as a recording; otherwise it opens a bare video
(`VideoOnlySource`), with `playback.video_only_opened` on the rail and
`playback.video_only_notice` in the notes. Undecodable files are refused with their reason.
Bare video lacks device-measured force rows and `session.json` metadata.
**A bare video can be analysed, and is asked how.** The two files the pass reads are *generated*
for the video in an app-owned workspace and every later step is the existing recording path; the
@@ -72,13 +69,13 @@ One cursor drives all three.
| View | Data source |
|---|---|
| Charts | the recorded `forces.csv` (`RecordingCurveSource`), or — over a bare video — the result's own recomputed trace (`AnalysisCurveSource`) |
| Charts | recorded `forces.csv` (`RecordingCurveSource`), or a recomputed trace for bare videos and reconfigured recordings (`AnalysisCurveSource`) |
| 2D canvas | the cached per-frame samples and depth, drawn at display time |
| 3D pane | the packet built from the cached frame (`AnalysisSession.packet` → `Scene3DView.submit_packet`) |
The charts read one of two sources, and the header names which: `chart.mode_replay` for a
recording's own `forces.csv` (*the* measurement — a device was in the loop) and
`chart.mode_recomputed` for the trace the pass recomputed from a bare video
`chart.mode_recomputed` for the recomputed trace
(`FRAME_FLAG_FORCE`, 28 B/frame; `provenance.sources["forces"]` says which). With neither —
nothing analysed yet, or a profile whose chain produced no force for this footage — the header
states why (`chart.mode_no_trace`) rather than leaving an empty plot.
@@ -98,6 +95,19 @@ sampled on the pass's own grid, so the frame carries that policy (`ImageFrame.ar
Verify: `test_replay_analysis.py::test_the_3d_pane_is_fed_the_same_frame_the_canvas_draws`,
`::test_a_display_change_that_moves_the_grid_is_refused_not_served`.
### Calibration choices and curve retention
Recording setups scope calibration, confirmed device, unloaded and tracker references to one
directory; the profile stays locked. References use recorded IDs/times. Chosen calibration content and force-channel choice enter
the cache key. A complete `sources.forces="recomputed"` result replaces the displayed CSV trace,
labelled `chart.mode_recomputed`; original files remain unchanged. Failed/cancelled passes
replace no trace. Without an override, recorded force retains priority.
Display controls preserve measurements, session time and partial axes, including FN-only.
New live runs remain independent; manual graph reset may clear the graph. Storage remains
bounded and reports drops. See the [SDK calibration contract](../../../../orisys-sdk/docs/calibration-model-contract.zh.md).
Verify: `test_recording_recalibration.py`, `test_curve_retention.py`, `test_force_history.py`.
## 5. What the 2D view can show
The modes come from the frame's own fields and from whether a result exists (`plan_image`,
@@ -171,7 +181,3 @@ wipe-on-exit preference and result export (≈70 MB per ten minutes).
* The render cost (≈1.4 ms) and the PNG estimate (180–540 MB per ten minutes) are the owner's
bench; Part B §14's snapshots (4.2–4.9 kB each) suggest the estimate is high.
* A chosen calibration can be applied to the **box** chain only (`Sensor(cal_path=…)`). The
finger runtime opens its sensor without one, so a non-default curve for a finger profile is
refused rather than silently ignored — the SDK has no entry point for it (reported, not
worked around).