NODEDC_MISSION_CORE/apps/control-station
DCCONSTRUCTIONS 24a47318f2 feat(plugins): isolate device integrations 2026-07-17 19:29:32 +03:00
..
public chore: rename repository to NODEDC MISSION CORE 2026-07-16 12:10:05 +03:00
scripts feat(control-station): add atomic recorded-session playback 2026-07-17 17:51:24 +03:00
src feat(plugins): isolate device integrations 2026-07-17 19:29:32 +03:00
test feat(plugins): isolate device integrations 2026-07-17 19:29:32 +03:00
.env.example chore: rename repository to NODEDC MISSION CORE 2026-07-16 12:10:05 +03:00
.gitignore chore: rename repository to NODEDC MISSION CORE 2026-07-16 12:10:05 +03:00
README.md feat(plugins): isolate device integrations 2026-07-17 19:29:32 +03:00
index.html chore: rename repository to NODEDC MISSION CORE 2026-07-16 12:10:05 +03:00
package-lock.json feat(control-station): add atomic recorded-session playback 2026-07-17 17:51:24 +03:00
package.json feat(control-station): add atomic recorded-session playback 2026-07-17 17:51:24 +03:00
tsconfig.app.json feat(plugins): isolate device integrations 2026-07-17 19:29:32 +03:00
tsconfig.json chore: rename repository to NODEDC MISSION CORE 2026-07-16 12:10:05 +03:00
tsconfig.node.json chore: rename repository to NODEDC MISSION CORE 2026-07-16 12:10:05 +03:00
vite.config.ts feat(plugins): isolate device integrations 2026-07-17 19:29:32 +03:00

README.md

NODEDC MISSION CORE · Control Station

Универсальный браузерный пункт управления Mission Core на React 19, TypeScript и Vite. Приложение задаёт общую операторскую оболочку для аппаратов, сенсоров, наблюдения, миссий и записей. XGRIDS/LixelKity K1 является первым реальным device adapter, но структура интерфейса от него не зависит.

Внутри пространственной рабочей поверхности встроен открытый Rerun Web Viewer. Это self-hosted frontend-компонент из npm-пакета @rerun-io/web-viewer, а не переход во внешний облачный интерфейс. Для K1 live и adapter file-replay backend сам создаёт Rerun gRPC/proxy source. Saved observation sessions открывают проверенную digest-bound RRD generation через same-origin HTTP; ручной адрес нужен только для другого совместимого Rerun source.

Текущее состояние

Контур Состояние Что это означает
Mission Core fixed shell Реализован Header, навигация по разделам, рабочая поверхность, окна и инспекторы работают в одном приложении.
Device plugin registry Реализован, v1alpha1 + v1alpha2 До выбора модели provider остаётся inert и не делает I/O. v1alpha1 сохраняет одну модель; v1alpha2 допускает одну или несколько моделей и требует profile coverage каждой. Custom device.connection UI key и backend factory подключаются одним reviewed import в composition root.
Plugin-owned connection UI Реализован XGRIDS provisioning, acquisition/replay, diagnostics, metrics и scoped styles физически находятся в plugins/xgrids-k1/frontend; generic Control Station предоставляет только frontend SDK, model catalog и host slot.
Локальный control plane Реализован React получает состояние и выполняет операции через FastAPI REST и WebSocket на loopback.
K1 BLE → Wi-Fi Реализован Реальный BLE-поиск всех видимых устройств и одна подтверждённая provisioning-запись выбранному устройству.
K1 live/replay MQTT Реализован Read-only приём, raw-first сохранение, декодирование облака точек и позы, реальные метрики.
Автоматический MQTT → Rerun Реализован Первый live/adapter file-replay поднимает process-wide RecordingStream и gRPC/proxy на TCP 9876; следующие такие сессии переиспользуют его. Saved observation replay использует отдельный immutable HTTP RRD path.
Встроенный Rerun Viewer Реализован Self-hosted npm-компонент автоматически открывает текущий gRPC source внутри Control Station; внешний viewer не используется.
Контролы сцены → Rerun Реализованы для текущей геометрии Работают размер и видимость точек, атрибут цвета, палитра, окно накопления, траектория, сетка, host timeline и сохранение/восстановление spatial layout. Проекция и семантические слои ещё не подключены.
Сохранённые observation sessions Реализованы для point/pose Три последние сессии, background preparation, cache v7, generation-bound RRD, atomic admission, autoplay, play/pause/seek и controlled switching больших записей.
K1 camera preview и archive Live реализован; recorded contract реализован Обе RTSP/H.264 камеры физически приняты в live UI. Новые acquisition-owned fMP4 archives не зависят от browser windows; recorded player подключён, но реальная архивная K1 camera-session ещё не прошла physical acceptance.
Legacy Foxglove module Только regression Модуль и тесты сохранены для сравнения декодирования. Текущий live/replay runtime не запускает Foxglove WebSocket и не использует TCP 8765.
Карты и миссии Интерфейсный каркас Реальные map/mission backends и vehicle control ещё не подключены.

Приложение не генерирует демонстрационное облако, траекторию, кадры или метрики. Если реальных данных нет, область сцены остаётся пустой, а числовые поля показывают .

Архитектура данных

K1 MQTT :1883, read-only
└── raw .k1mqtt + metadata + SHA-256 сохраняются первыми
    └── bounded latest-wins preview queue (32 сообщения)
        └── явно внедрённый K1 protobuf/LZ4 normalizer
            └── transport-neutral DecodedPointCloudView / DecodedPoseView
                └── Rerun Points3D + Transform3D + LineStrips3D
                    └── gRPC/proxy TCP 9876
                        └── rerun_grpc_url в REST/WebSocket state
                            └── встроенный @rerun-io/web-viewer
                                └── пространственная сцена Mission Core

Mission Core Control Station ←→ REST /api/v1/device-plugins/*
                         + plugin-scoped WebSocket events
                         FastAPI на 127.0.0.1:8000
                         CoreBluetooth + live/replay runtime

sealed/recovered observation session
└── SQLite catalog + bounded background preparation
    └── atomic RRD cache v7 + recorded-media manifest v2
        └── generation-bound same-origin HTTP
            └── aggregate admission
                └── Rerun native receiver + recorded fMP4 player

В момент готовности live/file-replay RerunBridge backend публикует адрес вида rerun+http://127.0.0.1:9876/proxy. Frontend автоматически назначает его сцене, если оператор не указал ручной source. После остановки приёма URL и встроенный viewer остаются активны, а следующая сессия сбрасывает session-local геометрию, траекторию и метрики и использует тот же listener. Он закрывается вместе с процессом k1link serve.

Поля foxglove_ws_url и foxglove_viewer_url пока остаются в API как совместимость со старым контрактом, но текущий runtime держит их пустыми.

Фиксированная оболочка

Shell собран из локальных NODE.DC UI packages и сохраняет одну структуру для всех функциональных модулей:

  1. AppHeader — марка NODE DC, выбор архитектурного раздела и состояние локального backend.
  2. AdminNavigationPanel — контекст аппарата и список рабочих поверхностей выбранного раздела.
  3. LandingStage — стартовая ситуационная поверхность и быстрые переходы.
  4. ApplicationPanel — единый контейнер активной рабочей поверхности.
  5. Window и Inspector — источник, отображение, слои и компоновка без раскрытия внутренних панелей визуального движка.

Встроенный Rerun Viewer работает как canvas внутри этой оболочки. Его верхняя, blueprint-, selection- и time-панели скрыты, чтобы продуктовые действия жили в Control Station. Размер точек, способ окрашивания и палитра, 12-секундное по умолчанию накопление, видимость облака и траектории и сетка связаны с backend и Rerun Blueprint. Host timeline управляет сохранённым session_time, а spatial layout сохраняет display settings, tool windows и source-window geometry через revisioned API. Смена 2D/3D/карты и семантические слои пока остаются интерфейсным контрактом.

Архитектурные разделы

Навигация описана данными в src/productModel.ts, а не зашита в разметку каждой страницы.

Раздел Назначение
Центр Оперативный обзор, состояние контура и активность оператора.
Парк Аппараты, текущее устройство, сенсоры и конфигурации борта.
Наблюдение Пространственная сцена, камеры, карта, объекты, телеметрия и время.
Миссии Планировщик, маршруты, сценарии и исполнение. Командный backend отключён.
Данные Сессии, потоки, сущности, playback и экспорт доказательств.
Система Модули, интеграции, сеть, аудит и настройки платформы.

Карточки возможностей имеют четыре честных уровня: работает сейчас, готово к источнику, интерфейсный контракт и последующий этап. Каталог не следует читать как утверждение, что для каждой карточки уже существует backend.

Установка, проверка, сборка и запуск

Требуются Node.js 20.19+ либо 22.12+, uv и соседний checkout NODEDC_DESIGN_GUIDELINE: зависимости @nodedc/* подключены к нему через локальные file: пути. Python устанавливается только в .venv репозитория.

Канонический путь от корня репозитория:

cd /Users/dcconstructions/Downloads/mnt/NODEDC/NODEDC_MISSION_CORE
uv sync --frozen --group dev

cd apps/control-station
npm ci
npm run test:unit
npm run typecheck
npm run build

cd ../..
uv run k1link serve

Открыть http://127.0.0.1:8000. Команда serve отдаёт собранный dist/ и локальный API. Она намеренно привязана только к 127.0.0.1; LAN bind не предусмотрен, потому что endpoint подключения кратковременно принимает пароль Wi-Fi.

Этот locked bootstrap повторяем в текущем workspace, но он ещё не является standalone release install. Локальные file: зависимости берутся из соседнего NODEDC_DESIGN_GUIDELINE, а lockfile не фиксирует Git revision или content hash этого checkout. До CI/portable packaging эти пакеты нужно опубликовать, завендорить либо проверять по неизменяемой donor revision.

Для разработки интерфейса после запуска backend:

cd apps/control-station
npm run dev

Для полного live-viewer контура Vite должен открыться на http://127.0.0.1:5173: только 5173, production preview 4173 и backend 8000 входят в текущий Rerun CORS allowlist. Если Vite сообщает fallback на 5174 или выше, освободите 5173 и перезапустите npm run dev; REST/WebSocket proxy на fallback-порту может работать, но прямой browser → Rerun 9876 будет отклонён. Vite проксирует весь /api (включая WebSocket) на http://127.0.0.1:8000. Другой локальный backend можно указать переменной VITE_API_TARGET. Preview production-сборки запускается командой npm run preview на http://127.0.0.1:4173.

Операторский путь для текущего K1 adapter

  1. Запустить uv run k1link serve и открыть Mission Core Control Station.
  2. Выбрать Парк → Локальное устройство.
  3. В каталоге моделей выбрать XGRIDS LixelKity K1. Только после этого активируется runtime и монтируется custom UI XGRIDS-плагина.
  4. Включить K1, дождаться стабильного индикатора, вручную сверить firmware 3.0.2 и direct-LAN топологию, затем подтвердить это в форме. Mission Core не читает firmware с устройства; такое подтверждение остаётся operator-attested, а не device-derived evidence.
  5. Нажать Показать все BLE-устройства. Интерфейс показывает полный результат шестисекундного поиска; метка совместимости является подсказкой, выбор делает оператор.
  6. Ввести SSID и пароль существующей сети и явно запустить подключение. Это одна reviewed provisioning-запись без автоматических повторов.
  7. Запустить live-приём по определённому адресу K1 либо replay локального .k1mqtt/проверенного TSV. Физическое сканирование K1 запускается и останавливается подтверждённым двойным нажатием кнопки устройства.
  8. Backend автоматически поднимет Rerun gRPC на TCP 9876 и опубликует адрес в state. Ручной source вводить не требуется.
  9. Открыть Наблюдение → Пространственная сцена. Реальные облако и траектория, частота, число точек, задержка и пропуски preview появятся после прихода сообщений K1.
  10. После нормального stop или recovery открыть Сохранённые сессии. Дождаться состояния Готово, выбрать запись и использовать host timeline. Evidence сохраняется автоматически; disk action сохраняет только workspace layout.

В проверочном live-сеансе через этот путь прошло 80 реальных MQTT-сообщений: 38 кадров lio_pcl, 42 кадра lio_pose, 2 775 точек в последнем облаке и 0 ошибок декодирования.

Источники Rerun

Live/replay runtime автоматически назначает первый URL из примера. В Наблюдение → Пространственная сцена → Источник можно оставить ручное поле пустым либо указать другой адрес, который понимает @rerun-io/web-viewer зафиксированной в package.json версии:

rerun+http://127.0.0.1:9876/proxy
http://127.0.0.1:8080/recording.rrd
https://example.internal/recording.rrd
  • rerun+http://…/proxy — живой Rerun gRPC source через доступный браузеру proxy;
  • http(s)://…/recording.rrd — RRD-запись по HTTP(S);
  • версия RRD должна быть совместима с версией Web Viewer;
  • ручной URL хранится только в состоянии текущей страницы и имеет приоритет над автоматическим source;
  • ошибка запуска показывается в viewport, без подстановки фиктивных данных.

После готовности Viewer выбор Rerun entity возвращает entityPath и имя view в оболочку. Backend принимает и применяет к Rerun размер точки 0.512.0, режимы цвета intensity, height, distance, rgb, class, палитры Turbo, Viridis, Plasma, grayscale и custom, накопление 0120 секунд, а также видимость облака, траектории и сетки. Значение по умолчанию — 12 секунд истории реальных кадров. Для текущего firmware-3 lio_pcl доказана только интенсивность из младшего байта rgbi; RGB используется лишь когда он действительно присутствует в декодированном формате. Custom/class сейчас означает выбранный сплошной цвет, а не готовую семантическую классификацию.

Для saved session host timeline вызывает Rerun play/pause/seek только после полного admission. Accumulation и display settings применяются через отдельный маленький blueprint RRD и не заменяют основную запись. Versioned observation.spatial layout сохраняется на host с optimistic revision; он не содержит sensor evidence. Переключатель 2D/3D/карты и семантические слои пока не вызывают готовый presentation backend.

Контракт локального API

Метод Route Тело / назначение
GET /api/health Проверка локального сервиса.
GET /api/v1/device-plugins Валидированные manifests установленных device plugins.
GET /api/v1/device-models Backend-каталог моделей из валидированных manifests; UI-каталог текущей сборки формируется отдельным static composition root.
POST /api/v1/device-plugins/{pluginId}/actions/{actionId} Namespaced действие через host allowlist; тело { "input": { ... } }.
WS /api/v1/device-plugins/{pluginId}/events Plugin-scoped snapshots с pluginId и монотонным sequence.
GET /api/v1/observation-sessions Последние cataloged sessions и authoritative preparation state.
POST /api/v1/observation-sessions/{id}/replay Verified replay launch либо HTTP 202 preparation handle.
GET /api/v1/observation-sessions/{id}/recording-preparation Exact-job polling с If-Match.
GET /api/v1/observation-sessions/{id}/recording.rrd Immutable generation-bound RRD; arbitrary chunked send_rrd не используется.
GET /api/v1/observation-sessions/{id}/media/{artifact}/... Manifest/init/segments записанных камер по opaque identifiers.
GET, PUT /api/v1/workspace-layouts/observation.spatial Versioned host layout с optimistic revision.

Frontend XGRIDS-плагина использует только v1alpha namespaced routes. Старые /api/state, /api/events, /api/ble/scan, /api/connect, /api/session/* и /api/viewer/settings сохранены как deprecated compatibility shims внутри XGRIDS backend contribution.

В v1alpha1/v1alpha2 backend composition загружается из manifests, а frontend plugins статически включаются в сборку через src/composition/devicePlugins.ts. Автоматической runtime-сверки двух installed sets пока нет: их соответствие — проверяемое требование сборки до появления подписанных plugin bundles и startup compatibility handshake. Поля manifest permissions, mutating и secretFields пока являются декларативными метаданными; generic host валидирует их форму и action allowlist, но ещё не реализует на их основе RBAC, подтверждения оператора или secret-vault substitution. Точный K1 compatibility profile остаётся неактивным, пока оператор явно не подтвердит firmware 3.0.2 и direct-LAN топологию. Runtime не получает версию firmware с устройства и явно помечает основание как operator-attested. Ошибки валидации namespaced action endpoint и deprecated /api/connect возвращаются как общий 422 без исходных значений запроса, поэтому отклонённое значение credential-поля не отражается клиенту.

Legacy /api/state и изменяющие состояние ответы могут вернуть snapshot напрямую или как { "state": { ... } }. v1alpha2 snapshot отдельно содержит device_ref, device_session, acquisition, operations, compatibility/calibration/sensor facts и старые presentation-поля phase, message, devices, selected_device_id, k1_ip, source_mode, metrics, rerun_grpc_url и viewer_settings. Legacy-поля foxglove_ws_url/foxglove_viewer_url остаются пустыми. OpenAPI доступен по /api/docs.

Карта исходников frontend

Файл Ответственность
src/App.tsx Fixed shell, выбор разделов и окна source/display/layers/layout.
src/productModel.ts Архитектурные разделы, рабочие поверхности и уровни готовности.
src/core/device-plugins/ Vendor-neutral manifest parser, registry, lifecycle, plugin host и public frontend SDK surface.
src/core/runtime/ Нормализованное состояние активного устройства и spatial source.
src/composition/devicePlugins.ts Единственный allowlist импортов конкретных device plugins.
src/workspaces/DeviceWorkspace.tsx Generic выбор модели и device.connection slot.
../../plugins/xgrids-k1/frontend/ Plugin-owned K1 BLE/Wi-Fi, acquisition/replay UI, API client, runtime mapper и scoped styles.
src/workspaces/Workspaces.tsx Оперативный обзор, spatial viewport и остальные продуктовые поверхности.
src/components/RerunViewport.tsx Live/recorded lifecycle WebViewer, native RRD open, atomic admission, playback и selection events.
src/components/ObservationSessionSelect.tsx Три последние сессии, состояния Готово / Обработка / Ошибка.
src/components/RecordedFmp4Player.tsx Проверенный generation-bound MSE playback архивных камер.
src/core/observation/sessionArchive.ts Строгий wire contract catalog/preparation/replay/media API.
src/core/observation/recordedSessionAdmission.ts Общий RRD/camera gate до публикации recorded workspace.
src/core/observation/workspaceLayout.ts Versioned spatial layout и optimistic revision.
src/sceneSettings.ts Типизированный UI-профиль пространственной сцены.
src/presentation.ts Vendor-neutral подписи lifecycle и форматирование нормализованных метрик.
src/styles.css, src/styles/* Компоновка shell и рабочих поверхностей.

Safety и чувствительные данные

  • Mission Core Control Station и credential endpoint доступны только на loopback.
  • Исключение: Rerun gRPC/proxy на TCP 9876 сейчас слушает все сетевые интерфейсы, хотя автоматически возвращаемый URL содержит 127.0.0.1. На нём нет connector-level authentication или TLS. Этот порт допустим только в доверенной лабораторной LAN; его нельзя пробрасывать в публичный Интернет или cellular WAN без аутентифицированного TLS reverse proxy. Listener намеренно остаётся активным между сессиями; для его закрытия нужно остановить k1link serve.
  • Пароль Wi-Fi находится только в React memory, передаётся в JSON POST body, очищается после успешного ответа и не сохраняется в URL/local storage.
  • BLE-подключение выполняет только отдельно рассмотренную provisioning-запись; случайные GATT writes и автоматические повторы запрещены.
  • MQTT live/replay не публикует команды устройству. Запуск и остановка физического сканирования остаются за кнопкой K1.
  • Live-сессии сначала сохраняют сырые сообщения и camera segments, затем формируют disposable preview. При перегрузке preview может быть отброшен, native evidence сохраняется. MQTT durability имеет bounded group-commit RPO, camera durability — текущий незавершённый fragment RPO; это не zero-loss claim.
  • Timeline capture_time сохраняет Unix-время приёма сообщения Mac, а окно накопления viewer использует session-local stream_time. capture_time — не доказанный timestamp сенсора K1 и не photon-to-screen latency.
  • Панорамный камерный поток в MQTT report topics отсутствует; отдельные left/right RTSP preview доступны через generic camera windows. LiDAR и camera сейчас синхронизированы только по host arrival, не по доказанным sensor clocks.
  • .runtime/, canonical evidence roots и legacy sessions/ игнорируются Git и могут содержать адреса, изображения, идентификаторы, траекторию и карту помещения. В репозиторий попадают только код, тесты, redacted manifests и безопасная документация.