Интерфейсы симулятора: HTTP API, CLI, журнал и отчёты¶
Раздел для тех, кто подключает симулятор к планировщику geoscan, гоняет регрессии в CI или читает результаты прогона. Жюри и заказчик — блоки «зачем» и диаграммы потоков. Разработчик — ссылки на http-api.md и cli.md с проверенными командами. Оператор — пошаговый запуск sim run / sim serve и что смотреть при failed.
Связанные разделы: бизнес-контекст симулятора, архитектура, эксплуатация (в т.ч. стенд https://aerosim.ff), REST планировщика geoscan — docs/task5/91-doc/06-api/README.md.
| Документ | Содержание |
|---|---|
| http-api.md | Все HTTP-ручки, аутентификация, ошибки, Prometheus, живые примеры |
| cli.md | Команды sim, коды выхода, headless-прогон |
| journal-reports.md | Файлы журнала, JSON Schema, fact_report.json |
Исходники API: simulate/src/geoscan_sim/api/. Контракты: simulate/contracts/ (error_codes.yaml, log_schema.json). Машиночитаемая схема HTTP: GET /api/v1/openapi.json (simulate/src/geoscan_sim/api/app.py:49).
Где сняты примеры в этом разделе: сессия 2026-09-17, в основном локально http://127.0.0.1:18081 (sim serve --no-auth, пустой --runs-dir). Публичные ручки стенда https://aerosim.ff проверены отдельно (см. эксплуатация §4); бизнес-ручки /api/* на стенде требуют ключ из creds-store.
Роль интерфейсов в контуре хакатона¶
Симулятор принимает план полёта (тот же JSON, что выдаёт планировщик), прогоняет его в выбранном мире (рельеф, зоны, ветер) и отдаёт факт: телеметрию, события, кадры съёмки и отчёт «план vs факт» для валидатора метрик.
flowchart LR
subgraph clients [Клиенты]
CLI[sim CLI]
HTTP[HTTP API]
WEB[web/dist UI]
end
subgraph sim [geoscan-sim]
API[FastAPI]
POOL[ProcessPoolExecutor]
ENG[Headless integrator]
end
PLAN[(plan.json)]
JOURNAL[(run_manifest + JSONL)]
FACT[(fact_report.json)]
CLI --> ENG
HTTP --> API --> POOL --> ENG
WEB --> API
ENG --> JOURNAL
ENG --> FACT
PLAN --> ENG
Вывод: один и тот же движок пишет журнал и при sim run, и при POST /api/v1/runs; HTTP добавляет очередь, каталог прогонов и раздачу артефактов без копирования каталога вручную.
Асинхронный прогон по HTTP¶
POST /api/v1/runs отвечает 202 Accepted сразу после постановки задачи в пул воркеров (simulate/src/geoscan_sim/api/routes/runs.py:49-94). Клиент опрашивает GET /api/v1/runs/{run_id} до status: "done" или "failed".
sequenceDiagram
participant C as Клиент
participant API as FastAPI
participant RS as RunService
participant W as subprocess worker
C->>API: POST /api/v1/runs {plan|plan_ref, world_id, seed}
API->>RS: enqueue_run()
RS->>RS: api_meta.json status=running
API-->>C: 202 {run_id, status: queued} + Location
loop poll
C->>API: GET /api/v1/runs/{run_id}
API-->>C: status running|done|failed
end
RS->>W: execute_run_in_subprocess
W->>W: run_plan_headless → journal + fact_report
W-->>RS: ok / error
RS->>RS: api_meta status=done|failed
C->>API: GET .../telemetry, /events, /frames
API-->>C: JSON / JSONL records
На демо-плане s01 с миром plane_seed_42 полный цикл занял ~18 с опроса (10× по 2 с) на 127.0.0.1:18081 в сессии документирования 2026-09-17.
Статусы прогона (HTTP)¶
Состояние хранится в api_meta.json в каталоге прогона (simulate/src/geoscan_sim/api/run_store.py:143-211). Тело ответа POST /runs может содержать status: "queued", тогда как мета уже переводится в running (run_store.py:167-170) — при первом GET обычно видно running.
stateDiagram-v2
[*] --> queued: POST /runs (тело ответа)
queued --> running: enqueue_run пишет meta
running --> done: worker ok
running --> failed: worker error / exception
done --> [*]
failed --> [*]
status |
Смысл | Что читать дальше |
|---|---|---|
running |
Воркер выполняет run_plan_headless |
Ждать, опрашивать GET /runs/{id} |
done |
fact_report.json и run_manifest.json на диске |
fact_report, /telemetry, /frames |
failed |
Поле error с code (часто INTERNAL_ERROR) |
Логи сервера, каталог прогона |
Быстрый старт (оператор)¶
cd simulate
uv sync
uv run sim run fixtures/plans/s01.json --seed 42 --output /tmp/sim-out --world m0-synthetic-flat
Пример stdout (фрагмент, прогон 2026-09-17):
{
"fact_report": "/tmp/sim-out/fact_report.json",
"manifest": "/tmp/sim-out/run_manifest.json",
"telemetry": "/tmp/sim-out/telemetry.jsonl",
"telemetry_sha256": "e11273715f65affb7987923aeb444d85ded8c8919264af05a2cf446cf4b62371",
"frames_written": 380,
"preflight_codes": []
}
HTTP (без ключа, только dev): sim serve держите в отдельном терминале (или запустите в фоне с & и подождите 1–2 с), затем во втором терминале вызывайте curl.
# терминал 1
uv run sim serve --host 127.0.0.1 --port 8080 --no-auth
# терминал 2
curl -sS http://127.0.0.1:8080/healthz
Поле version в /healthz — это engine_version() (короткий git-sha сборки); на другом коммите или на образе без метаданных git суффикс будет иным (например 0.1.0+unknown на aerosim.ff).
Подробности — в cli.md и http-api.md.
Ограничения, зафиксированные в коде¶
| Тема | Факт |
|---|---|
| Вердикт PASS/FAIL | GET .../verdict возвращает UNRESOLVED с блокерами (см. живой пример в http-api.md) |
Покрытие в fact_report |
Для s01 метрика coverage_fraction: actual: null, status: NOT_COMPUTED (тест tests/test_fact_report.py:46-52) |
| Swagger UI | Отключён (docs_url=None, redoc_url=None в app.py:50-51) |
| OpenAPI JSON | Доступен на /api/v1/openapi.json |
| Статический UI | Если собран simulate/web/dist, монтируется на / (app.py:80-82) |
Обработка ошибок (обзор)¶
Единый JSON-объект: code, severity, path, message (simulate/src/geoscan_sim/contracts/loader.py:161-174). Реестр кодов — simulate/contracts/error_codes.yaml.
flowchart TD
REQ[HTTP запрос] --> AUTH{Путь /api/* ?}
AUTH -->|нет ключа на стенде| EKEY[401 API_KEY_INVALID]
AUTH -->|ok или публичный путь| HANDLER[Маршрут]
PUB[/healthz /readyz /metrics/] --> HANDLER
HANDLER --> VAL{Валидация тела}
VAL -->|422| PLAN[PLAN_SCHEMA_VERSION_UNSUPPORTED + др.]
HANDLER --> RUN{POST /runs}
RUN -->|400| NOP[нет plan/plan_ref]
RUN -->|409| COLL[RUN_ID_COLLISION]
RUN -->|429| QUEUE[RUN_QUEUE_FULL]
HANDLER --> NF[404 RUN_NOT_FOUND / WORLD_NOT_FOUND]
HANDLER -->|500| INT[INTERNAL_ERROR]
Таблица HTTP-кодов по ручкам — http-api.md.