feat(node): push USB changes and preserve sensor setup across sessions

This commit is contained in:
DCCONSTRUCTIONS
2026-09-06 00:24:48 +03:00
parent a8647c4d87
commit 22d69107ba
24 changed files with 636 additions and 60 deletions
+23
View File
@@ -0,0 +1,23 @@
import asyncio
import threading
from k1link.fleet.events import FleetEvents
def test_registry_events_cross_thread_coalesce_and_unsubscribe():
async def check():
events = FleetEvents()
queue, close = events.subscribe()
thread = threading.Thread(target=lambda: [events.notify() for _ in range(20)])
thread.start()
thread.join()
await asyncio.sleep(0)
assert queue.qsize() == 1
await queue.get()
close()
events.notify()
await asyncio.sleep(0)
assert queue.empty()
assert not events.listeners
asyncio.run(check())
+83
View File
@@ -0,0 +1,83 @@
"""Record finalization must leave the board inventory responsive."""
import importlib.util
import threading
import types
from pathlib import Path
import pytest
def test_hashing_keeps_inventory_lock_free_and_rejects_new_capture(monkeypatch, tmp_path):
monkeypatch.setitem(__import__("sys").modules, "pyrealsense2", types.ModuleType("pyrealsense2"))
path = Path(__file__).parents[2] / "apps/node-agent/sensors/device.py"
spec = importlib.util.spec_from_file_location("recording_device", path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
device = module.Device("synthetic-camera", tmp_path, {})
ident = "capture_" + "a" * 32
root = device.root / "recordings" / ident
root.mkdir(parents=True)
(root / "source.db3").write_bytes(b"synthetic record bytes")
device.record = {"id": ident, "state": "recording"}
entered, release = threading.Event(), threading.Event()
real = module.hashlib.sha256
class SlowHash:
def __init__(self):
self.digest = real()
def update(self, value):
entered.set()
assert release.wait(3)
self.digest.update(value)
def hexdigest(self):
return self.digest.hexdigest()
monkeypatch.setattr(module.hashlib, "sha256", SlowHash)
worker = threading.Thread(target=device.stop)
worker.start()
try:
assert entered.wait(2)
assert device.lock.acquire(timeout=0.2)
try:
assert device.acquisition == "stopping"
with pytest.raises(ValueError):
device.start()
finally:
device.lock.release()
finally:
release.set()
worker.join(3)
assert not worker.is_alive()
assert device.acquisition == "idle"
assert device.record is None
assert module.json.loads((root / "manifest.json").read_text())["state"] == "complete"
def test_disconnect_finalizes_failed_record_and_clears_live_frames(monkeypatch, tmp_path):
monkeypatch.setitem(__import__("sys").modules, "pyrealsense2", types.ModuleType("pyrealsense2"))
path = Path(__file__).parents[2] / "apps/node-agent/sensors/device.py"
spec = importlib.util.spec_from_file_location("disconnect_device", path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
device = module.Device("synthetic-camera", tmp_path, {})
ident = "capture_" + "b" * 32
root = device.root / "recordings" / ident
root.mkdir(parents=True)
(root / "source.db3").write_bytes(b"frames before physical disconnect")
device.record = {"id": ident, "state": "recording"}
device.images = {"color": b"old frame"}
device.verified_this_process = True
stopped = []
device.pipeline = types.SimpleNamespace(stop=lambda: stopped.append(True))
device.disconnect()
assert stopped == [True]
assert not device.online and not device.verified_this_process
assert not device.images and device.pipeline is None
assert device.acquisition == "failed"
value = module.json.loads((root / "manifest.json").read_text())
assert value["state"] == "failed" and value["sha256"]
device.disconnect()
assert stopped == [True]