NODEDC_MISSION_CORE/apps/control-station
DCCONSTRUCTIONS 57b2208cae feat(k1): add local connection matrix 2026-07-19 09:52:22 +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(archive): complete recorded session lifecycle 2026-07-19 01:07:21 +03:00
test feat(k1): add local connection matrix 2026-07-19 09:52:22 +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(k1): wire canonical control session to UI 2026-07-18 17:53:55 +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 device UI Реализован XGRIDS provisioning, project/acquisition/replay, diagnostics, metrics, optional spatial controls и scoped styles физически находятся в plugins/xgrids-k1/frontend; generic Control Station предоставляет frontend SDK, model catalog и host slots.
Локальный control plane Реализован React получает состояние и выполняет операции через FastAPI REST и WebSocket на loopback.
K1 BLE → Wi-Fi Реализован Реальный BLE-поиск всех видимых устройств и одна подтверждённая provisioning-запись выбранному устройству.
K1 live/replay MQTT Реализован Read-only приём, обязательное локальное имя проекта, raw-first сохранение, декодирование облака/позы и device-reported scan time/distance/speed.
Автоматический 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/device metrics Три последние сессии, background preparation, capture-clock-bound cache v9, 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 подключён, но point-cloud + one-camera archived playback ещё не прошёл 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 + durable clock origin сохраняются первыми
    ├── injected ModelingReport observer -> device scan time/distance/speed
    └── bounded latest-wins visual preview queue (4 сообщения)
        └── явно внедрённый K1 protobuf/LZ4 point/pose 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
    └── capture-clock-bound atomic RRD cache v9 + 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. Ввести обязательное название проекта. Оно NFKC-нормализуется, trim-ится, проверяется на control/surrogate characters и лимит 96 Unicode characters, сохраняется как display metadata и не используется в filesystem path.
  8. Запустить локальный live-приём по определённому адресу K1 либо replay локального .k1mqtt/проверенного TSV. Физическое сканирование K1 запускается и останавливается подтверждённым двойным нажатием кнопки устройства.
  9. Backend автоматически поднимет Rerun gRPC на TCP 9876 и опубликует адрес в state. Ручной source вводить не требуется.
  10. Открыть Наблюдение → Пространственная сцена. Plugin-owned spatial block показывает local acquisition phase, stop local reception и полученные от K1 scan time/distance/speed; облако, траектория и preview metrics появляются только после реальных сообщений.
  11. Физически остановить K1, дождаться steady green, затем остановить локальный приём из device workflow или spatial block, чтобы запечатать evidence.
  12. После нормального 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;
  • nonterminal acquisition fail-closed блокирует manual source apply/reset, saved session switch и persisted replay reattach до завершения текущего приёма; automatic-source action очищает старый source и открывает сцену только после successful start, failed start сохраняет текущую сцену, а guard не останавливает устройство;
  • ошибка запуска показывается в 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 и fail-closed source-switch guard.
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, project/acquisition/replay UI, optional spatial controls, 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 data live/replay остаётся subscribe-only. Команды изолированы в plugin-owned canonical control session и доступны только через отдельные operator-present UI checkpoints.
  • Exact start/stop codec, response correlator и device-status state machine не владеют transport. Plugin v0.5.0 связывает их отдельным single-owner MQTT контуром без wall-clock переходов, reconnect или automatic retry; после STOP обязательны live READY и подтверждённый постоянный зелёный индикатор.
  • Live-сессии сначала сохраняют сырые сообщения и camera segments, затем формируют disposable preview. При перегрузке preview может быть отброшен, native evidence сохраняется. MQTT durability имеет bounded group-commit RPO, camera durability — текущий незавершённый fragment RPO; это не zero-loss claim.
  • Новая session summary hash-bind-ит durable clock origin и атомарно переключает pointer с provisional mqtt.timeline.json на content-addressed mqtt.timeline.session-<sha256>.json: только owner-sealed session scope может рекламировать combined media replay. Cache v9 создаёт реальные origin/end RRD rows; provisional transport scope fails closed.
  • 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. Физический archived playback point cloud + одна выбранная камера ещё не принят.
  • .runtime/, canonical evidence roots и legacy sessions/ игнорируются Git и могут содержать адреса, изображения, идентификаторы, траекторию и карту помещения. В репозиторий попадают только код, тесты, redacted manifests и безопасная документация.