拆分收尾:标定归属规则移入 CalibrationService(app_window 2340→2304 行)
- What: `apps/studio/src/orisys_studio/calibration_service.py` 新增三条**无状态**规则, 它们原来长在窗口里:`identity_of(path)`(这份系数文件声称属于哪台设备——用 SDK 自己的 解析器读,与设备将要加载的是同一份文档;读不出来就什么都不声称)、`same_path(a, b)` (同一个配置路径的不同写法不算不一致;两个空值也不算)、 `target_mismatch(path, *, sensor_type, config_ref)`(在确认框之前就说清楚"这份标定 看起来不属于这台设备"的理由)。服务本来就是这个文件工作的所有者,规则回到了它这里, 并且不需要窗口就能测。 - What: 窗口保留 `calibration_identity` / `calibration_target_mismatch` 两个转发 (tests 与 tools/ui_check.py 通过窗口驱动它们),删除 `_same_path`。 `_on_calibration_apply` 仍留在窗口:它要弹确认框、要把路径交给采集 worker,那是交互 与会话,不是关于文件的规则;`_calibration_summary`、`_drain_calibration_events`、 `_drain_reset_events`、`_on_calibration_point/cancelled/exported` 同理(面板与状态栏 的胶水)。 - What: 新增 apps/studio/tests/test_calibration_service.py:4 个用例——身份读出 sensor_type/sn/config_ref、读不出来的文件既无身份也无"不匹配"指控(不能把"不知道" 当成"不一致")、另一台设备/另一份配置会被逐条报出而同一台不会、同一路径的不同写法 不算不一致而非路径值算。 - Why: 这是 app_window 拆分计划里最后一块「有独立规则可搬」的部分。搬完之后 "这份标定是谁的"只有一处实现,窗口只剩对话与转发。 - Verification: pytest shared/tests apps/studio/tests(offscreen)636 passed / 0 failed / 15 skipped(原 632 + 新增 4);apps/studio/tools/ui_check.py 587 项检查 0 失败; .repro/locale_check.py 0 缺失。行为等价(同一份实现 + 一层转发)。 - Not in this change: 预览(_start_preview/_stop_preview/_on_preview_tick)与 2D 渲染 (_render_run_image/_show_image_frame/_depth_range)仍留在窗口——它们是面板与相机的 胶水,不属于任何一个已拆出的变更驱动;已知缺陷(本次未修,另行处理):worker 的 on_reset_applied 从未接到 recording.note_reset,真机上按"清零"不会记录确认事件。
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
"""Acceptance for the calibration service's rules about *whose* a file is.
|
||||
|
||||
The window used to own these: reading a coefficient file's claimed identity and deciding
|
||||
whether it obviously belongs to the device on the bench. They are pure rules about files,
|
||||
so they now live with the rest of the calibration file work — and can be tested without
|
||||
building a window. The end-to-end apply path is still covered by ``test_ui_smoke``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from orisys_app_shared.force.calibration_io import (
|
||||
CalSample,
|
||||
CalSessionStore,
|
||||
build_export_model,
|
||||
)
|
||||
from orisys_studio.calibration_service import CalibrationService
|
||||
|
||||
|
||||
def _calibration_file(directory: Path, *, sensor_type: str, config_ref: str) -> Path:
|
||||
"""A coefficient document that names the device it was measured on."""
|
||||
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-A"}
|
||||
document["pipeline"] = {"config_ref": config_ref}
|
||||
path = directory / f"{sensor_type}_calibration.json"
|
||||
path.write_text(json.dumps(document), encoding="utf-8")
|
||||
return path
|
||||
|
||||
|
||||
def test_the_identity_names_the_sensor_and_the_configuration(tmp_path):
|
||||
path = _calibration_file(tmp_path, sensor_type="box", config_ref="D:/lab/a/sensor.json")
|
||||
|
||||
identity = CalibrationService.identity_of(path)
|
||||
assert identity["sensor_type"] == "box"
|
||||
assert identity["sn"] == "SN-A"
|
||||
assert identity["config_ref"] == "D:/lab/a/sensor.json"
|
||||
|
||||
|
||||
def test_a_file_that_cannot_be_read_claims_nothing(tmp_path):
|
||||
"""No identity must mean "no mismatch claim", not "mismatch"."""
|
||||
broken = tmp_path / "not-a-calibration.json"
|
||||
broken.write_text("this is not a coefficient document\n", encoding="utf-8")
|
||||
|
||||
assert CalibrationService.identity_of(broken) == {}
|
||||
assert CalibrationService.target_mismatch(
|
||||
broken, sensor_type="finger", config_ref="D:/lab/b/sensor.json"
|
||||
) == []
|
||||
|
||||
|
||||
def test_another_devices_calibration_is_reported_with_its_reasons(tmp_path):
|
||||
path = _calibration_file(tmp_path, sensor_type="box", config_ref="D:/lab/a/sensor.json")
|
||||
|
||||
reasons = CalibrationService.target_mismatch(
|
||||
path, sensor_type="finger", config_ref="D:/lab/b/sensor.json"
|
||||
)
|
||||
assert any("sensor_type" in reason for reason in reasons), reasons
|
||||
assert any("config_ref" in reason for reason in reasons), reasons
|
||||
|
||||
# The same device and configuration is not a mismatch.
|
||||
assert (
|
||||
CalibrationService.target_mismatch(
|
||||
path, sensor_type="box", config_ref="D:/lab/a/sensor.json"
|
||||
)
|
||||
== []
|
||||
)
|
||||
|
||||
|
||||
def test_the_same_configuration_named_differently_is_not_a_mismatch(tmp_path):
|
||||
"""A path is compared as a path: the apply path must not cry wolf over spelling."""
|
||||
assert CalibrationService.same_path("", "D:/lab/a/sensor.json") is True
|
||||
assert CalibrationService.same_path("D:/lab/a/sensor.json", "") is True
|
||||
assert CalibrationService.same_path("D:/lab/a/sensor.json", "D:/lab/./a/sensor.json")
|
||||
|
||||
# ...but it does have to be a path: a non-path value simply differs.
|
||||
assert CalibrationService.same_path("D:/lab/a/sensor.json", "not a path\x00") is False
|
||||
Reference in New Issue
Block a user