统一实时标定身份解析并规范行尾
控制器与两个对话框共用 CalibrationService.read_identity,解析失败与缺 SN 分开上报; 本轮改动文件统一 LF,SN 必填回归已锁定。 定向 73 项、校准 UI 8 项与 lint 通过;未跑全量门禁。
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user