feat(plugins): isolate device integrations

This commit is contained in:
DCCONSTRUCTIONS
2026-07-17 19:29:32 +03:00
parent f9ffb7bd1c
commit 24a47318f2
122 changed files with 3304 additions and 1892 deletions
+9 -14
View File
@@ -22,10 +22,7 @@ from k1link.sessions import (
SessionRecordingMaterializer,
SessionRecordingPreparationManager,
SessionStore,
recover_stale_active_session_marker,
resolve_missioncore_evidence_dir,
)
from k1link.web.camera_archive import recover_incomplete_camera_archives
from k1link.web.device_plugin_composition import load_installed_device_plugins
from k1link.web.plugin_catalog import DevicePluginCatalog, PluginCatalogError
from k1link.web.plugin_runtime import (
@@ -42,13 +39,14 @@ REPOSITORY_ROOT = Path(__file__).resolve().parents[3]
INVALID_REQUEST_DETAIL = "Некорректные параметры запроса."
legacy_observation_sessions_root = REPOSITORY_ROOT / "sessions"
observation_sessions_root = resolve_missioncore_evidence_dir(REPOSITORY_ROOT)
plugin_environment = load_installed_device_plugins(REPOSITORY_ROOT)
plugin_catalog: DevicePluginCatalog = plugin_environment.catalog
plugin_dispatcher: DevicePluginDispatcher = plugin_environment.dispatcher
session_store = SessionStore(REPOSITORY_ROOT)
session_recording_materializer = SessionRecordingMaterializer(session_store.data_dir)
session_recording_materializer = SessionRecordingMaterializer(
session_store.data_dir,
exporters=plugin_environment.recording_exporters,
)
session_recorded_media_inspector = RecordedMediaInspector(
session_store.data_dir / "recorded-media-preparations"
)
@@ -76,8 +74,9 @@ def refresh_observation_catalog() -> tuple[str, ...]:
"""Discover completed or recoverable local evidence without copying payloads."""
imported = [
*session_store.import_legacy_viewer_live(legacy_observation_sessions_root),
*session_store.import_legacy_viewer_live(observation_sessions_root),
session_id
for archive in plugin_environment.observation_archives
for session_id in session_store.reconcile_archive(archive)
]
return tuple(dict.fromkeys(imported))
@@ -138,12 +137,8 @@ async def app_lifespan(_: FastAPI) -> AsyncIterator[None]:
# Recovery is intentionally a one-shot startup phase. The archive
# helper owns a cross-process lease, while ordinary catalog requests
# only perform discovery and therefore never touch a live writer.
await asyncio.to_thread(recover_stale_active_session_marker, observation_sessions_root)
for sessions_root in (
legacy_observation_sessions_root,
observation_sessions_root,
):
await asyncio.to_thread(recover_incomplete_camera_archives, sessions_root)
for archive in plugin_environment.observation_archives:
await asyncio.to_thread(archive.recover, archive.root)
# Full evidence discovery, hashing and RRD queue reconciliation can be
# expensive on field captures. Start it immediately in the background
# instead of holding the ASGI startup gate.