24 KiB
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/replay backend сам
создаёт Rerun gRPC/proxy source; ручной адрес нужен только для другого Rerun
потока или совместимой RRD-записи.
Текущее состояние
| Контур | Состояние | Что это означает |
|---|---|---|
| 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. |
| Локальный control plane | Реализован | React получает состояние и выполняет операции через FastAPI REST и WebSocket на loopback. |
| K1 BLE → Wi-Fi | Реализован | Реальный BLE-поиск всех видимых устройств и одна подтверждённая provisioning-запись выбранному устройству. |
| K1 live/replay MQTT | Реализован | Read-only приём, raw-first сохранение, декодирование облака точек и позы, реальные метрики. |
| Автоматический MQTT → Rerun | Реализован | Первый live/replay поднимает process-wide RecordingStream и gRPC/proxy на TCP 9876; следующие сессии сбрасывают сцену и метрики и переиспользуют его. |
| Встроенный Rerun Viewer | Реализован | Self-hosted npm-компонент автоматически открывает текущий gRPC source внутри Control Station; внешний viewer не используется. |
| Контролы сцены → Rerun | Реализованы для текущей геометрии | Работают размер и видимость точек, атрибут цвета, палитра, окно накопления, траектория и сетка. Проекция, собственный timeline и сохранённые layout-профили ещё не подключены. |
| Legacy Foxglove module | Только regression | Модуль и тесты сохранены для сравнения декодирования. Текущий live/replay runtime не запускает Foxglove WebSocket и не использует TCP 8765. |
| Камеры, карты и миссии | Интерфейс готов | Серверная логика и реальные каналы для этих рабочих поверхностей ещё не подключены. |
Приложение не генерирует демонстрационное облако, траекторию, кадры или
метрики. Если реальных данных нет, область сцены остаётся пустой, а числовые поля
показывают —.
Архитектура данных
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
В момент готовности 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 и сохраняет одну структуру для всех функциональных модулей:
AppHeader— марка NODEDC MISSION CORE, выбор архитектурного раздела и состояние локального backend.AdminNavigationPanel— контекст аппарата и список рабочих поверхностей выбранного раздела.LandingStage— стартовая ситуационная поверхность и быстрые переходы.ApplicationPanel— единый контейнер активной рабочей поверхности.WindowиInspector— источник, отображение, слои и компоновка без раскрытия внутренних панелей визуального движка.
Встроенный Rerun Viewer работает как canvas внутри этой оболочки. Его верхняя, blueprint-, selection- и time-панели скрыты, чтобы продуктовые действия жили в Control Station. Размер точек, способ окрашивания и палитра, 12-секундное по умолчанию накопление, видимость облака и траектории и сетка связаны с backend и Rerun Blueprint. Кнопки собственного timeline, смена 2D/3D/карты и сохранение layout пока остаются интерфейсным контрактом.
Архитектурные разделы
Навигация описана данными в 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
Vite слушает http://127.0.0.1:5173 и проксирует весь /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
- Запустить
uv run k1link serveи открыть Mission Core Control Station. - Выбрать Парк → Локальное устройство.
- В каталоге моделей выбрать XGRIDS LixelKity K1. Только после этого активируется runtime и монтируется custom UI XGRIDS-плагина.
- Включить K1, дождаться стабильного индикатора, вручную сверить firmware
3.0.2и direct-LAN топологию, затем подтвердить это в форме. Mission Core не читает firmware с устройства; такое подтверждение остаётсяoperator-attested, а не device-derived evidence. - Нажать Показать все BLE-устройства. Интерфейс показывает полный результат шестисекундного поиска; метка совместимости является подсказкой, выбор делает оператор.
- Ввести SSID и пароль существующей сети и явно запустить подключение. Это одна reviewed provisioning-запись без автоматических повторов.
- Запустить live-приём по определённому адресу K1 либо replay локального
.k1mqtt/проверенного TSV. Физическое сканирование K1 запускается и останавливается подтверждённым двойным нажатием кнопки устройства. - Backend автоматически поднимет Rerun gRPC на TCP 9876 и опубликует адрес в state. Ручной source вводить не требуется.
- Открыть Наблюдение → Пространственная сцена. Реальные облако и траектория, частота, число точек, задержка и пропуски preview появятся после прихода сообщений K1.
В проверочном 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.5–12.0, режимы
цвета intensity, height, distance, rgb, class, палитры Turbo, Viridis,
Plasma, grayscale и custom, накопление 0–120 секунд, а также видимость облака,
траектории и сетки. Значение по умолчанию — 12 секунд истории реальных кадров.
Для текущего firmware-3 lio_pcl доказана только интенсивность из младшего
байта rgbi; RGB используется лишь когда он действительно присутствует в
декодированном формате. Custom/class сейчас означает выбранный сплошной цвет,
а не готовую семантическую классификацию.
Custom timeline, переключатель 2D/3D/карты, семантические слои и сохранение RBL пока не вызывают Blueprint/playback API. Черновик компоновки фиксируется только в памяти текущей страницы и не записывается на диск.
Контракт локального 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. |
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. |
src/core/runtime/ |
Нормализованное состояние активного устройства и spatial source. |
src/composition/devicePlugins.ts |
Единственный allowlist импортов конкретных device plugins. |
src/workspaces/DeviceWorkspace.tsx |
Generic выбор модели и device.connection slot. |
src/device-plugins/xgrids-k1/ |
Реальный K1 BLE/Wi-Fi/live/replay UI, client и compatibility mapper. |
src/workspaces/Workspaces.tsx |
Оперативный обзор, spatial viewport и остальные продуктовые поверхности. |
src/components/RerunViewport.tsx |
Жизненный цикл встроенного Rerun Web Viewer и selection events. |
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-сессии сначала сохраняют сырые сообщения, затем формируют preview. При перегрузке preview может быть отброшен, raw evidence сохраняется.
- Timeline
capture_timeсохраняет Unix-время приёма сообщения Mac, а окно накопления viewer использует session-localstream_time.capture_time— не доказанный timestamp сенсора K1 и не photon-to-screen latency. - Панорамный камерный поток в наблюдавшихся MQTT report topics отсутствует; текущая Rerun-сцена содержит только облако точек и позу/траекторию, а операционные метрики отображаются внешней оболочкой Mission Core.
sessions/игнорируется Git и может содержать адреса, идентификаторы, траекторию и карту помещения. В репозиторий попадают только redacted manifests и безопасная документация.