"""A manually confirmed site produces one traceable trial directory.""" from __future__ import annotations import csv import json import time from types import SimpleNamespace import numpy as np import pytest from orisys_app_shared.acquisition.models import SdkPacket from orisys_autocal.trial import ManualTrial, load_path, site_at, uv_origin class Worker: def __init__(self) -> None: self.is_running = True self.sinks = [] self.packet = SdkPacket( raw_frame=np.zeros((32, 32, 3), dtype=np.uint8), processed_frame=np.zeros((32, 32, 3), dtype=np.uint8), timestamp=time.time(), fps=30, process_ms=1, session_id=2, frame_id=1, mono_t=1.0, capture_mono_ns=time.perf_counter_ns(), capture_utc_ns=time.time_ns(), source_frame_id=7, time_basis="host_camera", normal_force_raw=1.25, ) def latest(self): return self.packet def add_sink(self, sink): self.sinks.append(sink) def remove_sink(self, sink): self.sinks.remove(sink) def emit(self): for sink in self.sinks: sink(self.packet) def test_uv_coordinates_are_centered_and_masked(): mask = np.full((5, 7), -1, dtype=np.int32) mask[1:4, 2:5] = 2 flat = SimpleNamespace( width=7, height=5, face_index=mask, sample=lambda x, y: ( (np.array([x, y, 3.0]), np.array([0, 0, 1]), 2) if 0 <= x < 7 and 0 <= y < 5 and mask[y, x] >= 0 else None ), ) assert uv_origin(flat) == (3, 3) site = site_at(flat, -1, 2) assert site.raster_xy_px == (2, 1) assert site.position_sensor_mm == (2.0, 1.0, 3.0) with pytest.raises(ValueError): site_at(flat, -3, 0) def test_manual_trial_records_video_stamps_and_load_unload_events(tmp_dir): worker = Worker() trial = ManualTrial(worker) bundle = tmp_dir / "bundle" bundle.mkdir() (bundle / "mesh.npz").write_bytes(b"mesh") config = tmp_dir / "sensor.json" config.write_text("{}", encoding="utf-8") site = SimpleNamespace( u_px=0, v_px=1, position_sensor_mm=(1.0, 2.0, 3.0), face_index=2, raster_xy_px=(3, 2), uv_origin_raster_xy_px=(3, 3), ) assert load_path([5, 10]) == [ ("load", 5), ("load", 10), ("unload", 5), ("unload", 0), ] directory = trial.start( root=tmp_dir, site=site, levels=[5, 10], hold_s=20, sensor_sn="F-001", bundle_path=str(bundle), source={"kind": "camera", "value": 0}, config_ref=str(config), fps=30, ) worker.emit() for value in (5, 10, 5, 0): trial.confirm(value) trial.finish() report = trial.recorder.stop(wait=True) assert report.ok assert trial.poll() == "complete" assert len(list(directory.glob("raw_*.mp4"))) == 1 with (directory / "frames.csv").open(encoding="utf-8", newline="") as handle: row = next(csv.DictReader(handle)) assert row["t_host_mono_ns"] == str(worker.packet.capture_mono_ns) assert row["source_frame_id"] == "7" assert row["time_basis"] == "host_camera" assert row["capture_dropped"] == "0" with (directory / "events.csv").open(encoding="utf-8", newline="") as handle: events = list(csv.DictReader(handle)) reached = [event for event in events if event["kind"] == "step_reached"] assert len(reached) == 4 assert [json.loads(event["detail"])["branch"] for event in reached] == [ "load", "load", "unload", "unload", ] with (directory / "session.json").open(encoding="utf-8") as handle: session = json.load(handle) assert session["site"]["uv_xy_px"] == [0, 1] assert session["site"]["mesh_sha256"] def test_calibration_page_keeps_precise_uv_site_when_switching_to_live_view( make_window, ): from orisys_autocal.sensor.uv_flat import FlatSurface window = make_window() mask = np.full((5, 7), -1, dtype=np.int32) mask[1:4, 2:5] = 2 positions = np.zeros((5, 7, 3), dtype=np.float64) positions[1, 2] = (2, 1, 3) flat = FlatSurface( size_hw=(5, 7), image_rgb=np.zeros((5, 7, 3), dtype=np.uint8), positions_mm=positions, normals=np.zeros_like(positions), face_index=mask, uv_min=(0, 0), uv_max=(1, 1), ) panel = window.calibration_panel panel.set_flat(flat, "bundle") panel._picked(2, 1) assert (panel.u_spin.value(), panel.v_spin.value()) == (-1, 2) assert panel._site.raster_xy_px == (2, 1) assert "2.000" in panel.position_value.text() panel.views.setCurrentWidget(panel.source_panel) assert panel._site.raster_xy_px == (2, 1) window.close()