feat: 为每个 Sensor 绑定只读 SensorContext
构造时一次绑定 SN、配置、资产和当前生效的标定:只接受显式给出的 SN,不做猜测;标定路径只取当前生效的标定,清除后为 None;换标定时整体替换。帧身份复用 stream-contract 的 FrameIdentity。load_config 改为复用 resolve_config_source,使记录的配置来源与实际读取的文件一致,并补一致性测试。已发布 Sensor 数值和资产包解析不变。回归 275 项、lint 通过。
This commit is contained in:
@@ -163,14 +163,54 @@ assert measurement.stamp == stamp
|
||||
|
||||
未知字段被拒绝,是为了让不同版本的记录不会静默错配。采用政策 A:字段变更(包括新增字段)一律升 `STREAM_CONTRACT_VERSION`,同时把新版本加入读取方的 `SUPPORTED_CONTRACT_VERSIONS`;同版本内不新增字段。
|
||||
|
||||
## Sensor 上下文(只读绑定)
|
||||
|
||||
`Sensor.context` 返回一个 frozen `SensorContext`(`from orisys.sensors import SensorContext`),在构造时为本实例绑定一次以下内容,并在显式换标定时整体替换:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `sensor_id` | 仅显式提供的设备 SN(`device_id`);未显式绑定为 `None`,绝不由枚举顺序、文件夹名或相机编号猜测 |
|
||||
| `product_family` / `processing_backend` | 由配置/资产包确定的产品家族与图像处理后端 |
|
||||
| `config_name` / `config_source` / `config_fingerprint` | 配置名、实际读取的 JSON 文件、加载后配置的规范化 SHA-256 |
|
||||
| `bundle_root` / `manifest_digest` / `assets` | 资产包根目录、manifest 内容摘要、manifest 声明的资产角色与包内相对路径(legacy manifest 与 descriptor manifest 都支持;运行时实际加载路径可能不同) |
|
||||
| `calibration_path` / `calibration_fingerprint` | 当前**正在生效**的力标定文件与内容指纹;未标定或 `set_calibration(None)` 后为 `None`/`"none"` |
|
||||
| `diagnostics` | 绑定过程中可容忍的资产/配置说明,不改变 Sensor 的构造结果 |
|
||||
|
||||
规则:
|
||||
|
||||
- 帧身份/时钟字段不在这里重复:`context.frame_identity(session_id=..., frame_id=..., source_frame_id=..., acquisition_id=..., sync_group_id=...)` 直接构造 T03 的 `FrameIdentity`。没有显式 SN 时它抛 `ValueError`,不伪造身份。
|
||||
- 每个 Sensor 实例独立绑定,没有模块级共享缓存;创建或使用另一个 Sensor 不会改变本实例的上下文。
|
||||
- 显式 `Sensor.set_calibration()` 换标定时原子替换上下文:SN/配置/资产绑定保持不变,标定路径与指纹更新;`set_calibration(None)` 后 `calibration_path` 立即为 `None`、指纹为 `"none"`,不保留构造时请求过的旧路径。
|
||||
- 绑定异常只写入 `diagnostics`,不把原本可构造的 Sensor 变成构造失败。
|
||||
- `assets`/`manifest_digest` 是 **manifest 声明的绑定**,不是运行时实际加载的路径:Finger 的导出目录由 `finger/exports.py` 在 cwd、打包目录、可编辑安装目录等候选中另行查找,实际加载路径由 T05 的图像块绑定;需要复现实验时以 T05/记录的身份为准。
|
||||
- 资产取应用包内相对路径,不复制、不上传、不按顺序推断;配置指纹取实际加载后的配置内容,而不是文件名。
|
||||
- 本任务(T04)不改变 Sensor 的数值与既有资产包解析行为,也不把上下文接入流入口;T05/T08 消费该上下文。
|
||||
|
||||
```python
|
||||
from orisys import configs
|
||||
from orisys.sensors import build_sensor_context
|
||||
from orisys.stream_contract import FrameIdentity
|
||||
|
||||
context = build_sensor_context(
|
||||
config_name="box",
|
||||
config=configs.load_config("box"),
|
||||
device_id="BOX-001",
|
||||
bundle_candidates=["box"],
|
||||
)
|
||||
identity: FrameIdentity = context.frame_identity(
|
||||
session_id="run-7", frame_id=12, source_frame_id=41,
|
||||
)
|
||||
assert identity.sensor_id == "BOX-001"
|
||||
```
|
||||
|
||||
## 不包含什么
|
||||
|
||||
- 不实现 SensorStream 入口、实时最新选帧、完整回放和背压(T08)。
|
||||
- 不实现图像块拆分、测量负载、ForceSession 重做、零点与输出滤波(T04–T07)。
|
||||
- 不实现图像块拆分、测量负载、ForceSession 重做、零点与输出滤波(T05–T07)。
|
||||
- 不实现批次、配组、多流调度(后续多流计划)。
|
||||
- 不实现录制/回放格式、Studio 迁移和离线重新滤波(T11、T13–T15)。
|
||||
- 不修改现有 `Sensor`、`StreamPipeline` 和当前 `ForceSession`;现有 `ForceSession.FrameStamp` 在 T06 重做时被本合同的帧头替换。
|
||||
|
||||
## 验证
|
||||
|
||||
`tests/test_stream_contract.py` 覆盖 T03 的四项验收:逐路身份与时钟、组字段可缺但不半组、同 PTS 帧可区分、`source_dt_s` 与事件 JSON 往返;同时覆盖 frozen 校验、严格版本/未知字段拒绝(政策 A)、`SUPPORTED_CONTRACT_VERSIONS`、事件可哈希、`kind` 的 lower_snake 格式和非法负载拒绝。类型本身不启动传感器、流水线或会话。
|
||||
`tests/test_stream_contract.py` 覆盖 T03 的四项验收:逐路身份与时钟、组字段可缺但不半组、同 PTS 帧可区分、`source_dt_s` 与事件 JSON 往返;同时覆盖 frozen 校验、严格版本/未知字段拒绝(政策 A)、`SUPPORTED_CONTRACT_VERSIONS`、事件可哈希、`kind` 的 lower_snake 格式和非法负载拒绝。`tests/test_sensor_context.py` 覆盖 T04 的只读 SensorContext:显式 SN/配置/资产/标定路径绑定、不猜 SN、实例隔离、换标定原子替换、绑定失败只报 diagnostics,以及 config source/资产包绑定。类型与上下文本身不启动传感器、流水线或会话。
|
||||
|
||||
Reference in New Issue
Block a user