Перейти к содержанию

Интерфейсы симулятора: 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.