补齐 Autocal 实时反馈与稳定窗口复核

显示实时力值、曲线及 UV 图层,并保存人工确认窗口和零载检查基准。定向测试、真实视频预览与已录会话复核闭环验证通过。
This commit is contained in:
ykcheungab
2026-10-11 18:08:09 +08:00
parent 04adf17f60
commit 34a1d9b1f1
12 changed files with 731 additions and 39 deletions
@@ -0,0 +1,85 @@
"""Live operator feedback must preserve sample validity and UV orientation."""
from dataclasses import replace
import numpy as np
from orisys_app_shared.acquisition.models import SdkPacket
class Worker:
is_running = True
def __init__(self):
self.sinks = []
def add_sink(self, sink):
self.sinks.append(sink)
def remove_sink(self, sink):
self.sinks.remove(sink)
def packet(**values):
image = np.full((64, 80, 3), (10, 20, 30), dtype=np.uint8)
base = SdkPacket(raw_frame=image.copy(), processed_frame=image,
timestamp=100, mono_t=1, fps=30, process_ms=1,
session_id=1, frame_id=1, normal_force_raw=12,
shear_x_raw=2, shear_y_raw=-3)
return replace(base, **values)
def test_force_feedback_consumes_every_sample_and_breaks_invalid_gaps(qt_app):
from orisys_autocal.ui.live_force_view import LiveForceView
worker = Worker()
view = LiveForceView(worker)
for item in (packet(), packet(frame_id=2, mono_t=1.1, stale_forces=True),
packet(frame_id=3, mono_t=1.2, normal_force_raw=20)):
worker.sinks[0](item)
view.refresh()
assert view.history.sample_count == 3
x, y = view.curves[0].getData()
np.testing.assert_allclose(x, [0, .1, .2])
assert y[0] == 12 and np.isnan(y[1]) and y[2] == 20
assert "20 raw" in view.readings.text()
assert "20.000 N" not in view.readings.text()
view.on_packet(packet(session_id=2, normal_force=4, force_calibrated=True,
calibrated_axes=("fn",)))
view.refresh()
assert view.history.sample_count == 1
assert "4.000 N" in view.readings.text()
assert "2.000 N" not in view.readings.text()
worker.is_running = False
view.refresh()
assert view.state.text()
view.shutdown()
assert not worker.sinks
def test_display_toggles_redraw_same_frame_without_mutating_measurements(qt_app, monkeypatch):
from orisys_autocal.ui import finger_source_panel
panel = finger_source_panel.FingerSourcePanel()
item = packet(flow=np.full((64, 80, 2), 2., dtype=float),
depth=np.full((64, 80), .5), valid_mask=np.ones((64, 80), bool),
flow_valid=True, flow_frame_id=1, depth_valid=True, depth_frame_id=1)
original = item.processed_frame.copy()
panel._last_packet = item
frames = []
monkeypatch.setattr(panel, "_show_frame", lambda frame: frames.append(frame.copy()))
panel._redraw()
np.testing.assert_array_equal(frames[-1], original)
panel.background_check.setChecked(False)
assert not np.array_equal(frames[-1], original)
solid = frames[-1]
panel.depth_check.setChecked(True)
assert not np.array_equal(frames[-1], solid)
depth = frames[-1]
panel.arrows_check.setChecked(True)
assert not np.array_equal(frames[-1], depth)
assert frames[-1].shape == original.shape
np.testing.assert_array_equal(item.processed_frame, original)
assert np.all(item.flow == 2)
panel._last_packet = replace(item, flow_valid=False, depth_frame_id=0)
panel._redraw()
assert panel.display_note.text()
np.testing.assert_array_equal(frames[-1], solid)
panel.shutdown()