统一实时标定身份解析并规范行尾

控制器与两个对话框共用 CalibrationService.read_identity,解析失败与缺 SN 分开上报;
本轮改动文件统一 LF,SN 必填回归已锁定。
定向 73 项、校准 UI 8 项与 lint 通过;未跑全量门禁。
This commit is contained in:
ykcheungab
2026-10-19 18:42:13 +08:00
parent 70b4341fd0
commit 81063213f9
13 changed files with 1095 additions and 309 deletions
@@ -45,6 +45,42 @@ def _descriptor(profile_id: str, **overrides: object) -> dict[str, object]:
return data
def _coefficients(directory: Path, *, sensor_type: str, sn: str) -> Path:
"""A coefficient document the SDK's own loader accepts (a real export shape).
The live open/apply path reads a file's identity through ``CalibrationService``
— the same parser the device would use — so a Finger serial check needs a
document that really is a calibration file, not a ``meta`` block in any JSON.
"""
from orisys_app_shared.force.calibration_io import (
CalSample,
CalSessionStore,
build_export_model,
)
store = CalSessionStore()
store.aggregate("fn", CalSample(weight_n=0.0, raw_mean=0.0, n=10))
store.aggregate("fn", CalSample(weight_n=5.0, raw_mean=500.0, n=10))
model = build_export_model({key: store.channel(key) for key in ("fn", "fx", "fy")})
document = model.to_dict()
document["identity"] = {"sensor_type": sensor_type, "sn": sn}
document["pipeline"] = {"config_ref": "D:/lab/sensor.json"}
path = directory / f"{sensor_type}-{sn}.json"
path.write_text(json.dumps(document), encoding="utf-8")
return path
def _assert_unreadable(problem: str) -> str:
"""The problem is ``device_unreadable`` with the parser's reason, not "no SN"."""
template = t("video_analysis.device_unreadable", error="\x00")
prefix, _, suffix = template.partition("\x00")
assert problem != t("video_analysis.device_missing_sn")
assert problem.startswith(prefix) and problem.endswith(suffix), problem
detail = problem[len(prefix) : len(problem) - len(suffix)]
assert detail.strip(), "the parser's reason was dropped"
return detail
class _Host(QWidget):
"""Window stand-in with the live-calibration prefs API the dialog expects."""
@@ -233,11 +269,7 @@ def test_a_finger_file_with_a_mismatched_sn_is_not_a_valid_choice(
picker = _make_picker(qt_app, tmp_path, monkeypatch, build)
picker.set_sensor_type("finger")
host, dialog = _dialog_for(picker)
cal = tmp_path / "DD02-14.json"
cal.write_text(
json.dumps({"meta": {"sensor_type": "finger", "sn": "DD02-14"}}),
encoding="utf-8",
)
cal = _coefficients(tmp_path, sensor_type="finger", sn="DD02-14")
try:
dialog.select_calibration_file(cal)
# Offscreen dialogs are not shown; ``isHidden`` is the visibility flag.
@@ -255,13 +287,79 @@ def test_a_finger_file_with_a_mismatched_sn_is_not_a_valid_choice(
picker.deleteLater()
def test_window_resolves_finger_file_choice_for_open(make_window, tmp_path):
window = make_window()
def _finger_dialog(qt_app, tmp_path, monkeypatch):
def build(root: Path) -> None:
folder = root / "finger-lab"
_write(folder / "sensor.json", CONFIG)
_write(
folder / "manifest.json",
_descriptor(
"orisys.finger.lab-r1",
display_name="Finger lab",
product_family="finger",
processing_backend="uv_unroll",
computational_assets={},
),
)
picker = _make_picker(qt_app, tmp_path, monkeypatch, build)
picker.set_sensor_type("finger")
host, dialog = _dialog_for(picker)
return picker, host, dialog
def test_a_finger_file_the_sdk_rejects_is_reported_as_unreadable(
qt_app, tmp_path, monkeypatch
):
"""A ``meta`` SN is not an identity: the dialog precheck uses the strict parse."""
picker, _host, dialog = _finger_dialog(qt_app, tmp_path, monkeypatch)
cal = tmp_path / "DD02-14.json"
cal.write_text(
json.dumps({"meta": {"sensor_type": "finger", "sn": "DD02-14"}}),
encoding="utf-8",
)
try:
dialog.select_calibration_file(cal)
dialog.device_edit.setText("DD02-14")
_assert_unreadable(dialog.choice_problem())
finally:
dialog.deleteLater()
picker.deleteLater()
def test_a_finger_file_that_is_not_json_is_reported_as_unreadable(
qt_app, tmp_path, monkeypatch
):
picker, _host, dialog = _finger_dialog(qt_app, tmp_path, monkeypatch)
cal = tmp_path / "not-json.json"
cal.write_text("not a calibration document\n", encoding="utf-8")
try:
dialog.select_calibration_file(cal)
dialog.device_edit.setText("DD02-14")
_assert_unreadable(dialog.choice_problem())
finally:
dialog.deleteLater()
picker.deleteLater()
def test_a_finger_file_without_an_sn_is_reported_as_missing_sn(
qt_app, tmp_path, monkeypatch
):
"""Readable but nameless stays its own answer, not a parse failure."""
picker, _host, dialog = _finger_dialog(qt_app, tmp_path, monkeypatch)
cal = _coefficients(tmp_path, sensor_type="finger", sn="")
try:
dialog.select_calibration_file(cal)
dialog.device_edit.setText("DD02-14")
assert dialog.choice_problem() == t("video_analysis.device_missing_sn")
finally:
dialog.deleteLater()
picker.deleteLater()
def test_window_resolves_finger_file_choice_for_open(make_window, tmp_path):
window = make_window()
cal = _coefficients(tmp_path, sensor_type="finger", sn="DD02-14")
finger = next(
(
entry