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

gRPC, JSON Schema и форматы выгрузки

gRPC: общие типы (common.proto)

Пакет geoscan.common.v1 (src/backend/proto/geoscan/common.proto):

Тип Назначение
Position lon_deg, lat_deg, alt_m
Geometry / Ring Упрощённая геометрия для proto
SolverStatus OPTIMAL, FEASIBLE, INFEASIBLE, TIMEOUT
ExportFormat GEOJSON, KML, PLAN, WAYPOINTS
Violation, Warning, Unassigned Результаты расчёта и валидации

Единицы в именах полей proto согласуются с whitelist суффиксов в src/backend/schema/units.json.


PlannerService

Файл: src/backend/proto/geoscan/planner.proto.

rpc Plan(PlanRequest) returns (stream PlanEvent)

PlanRequest

Поле Тип Описание
scenario_json bytes Сериализованный сценарий (REST JSON, UTF-8)
criterion string Критерий оптимизации
time_limit_s double Лимит времени солвера, с
seed int64 Детерминизм

PlanEvent (oneof):

  • PlanProgressstage, progress_frac (0…1), objective_value, elapsed_s, message
  • PlanResultplan_json, solver_status, solve_time_s, PlanMetrics, violations, unassigned, warnings

Сервер: src/backend/planner/planner/rpc/server.py:46-69 — аутентификация x-service-api-key, ошибки INVALID_ARGUMENT, FAILED_PRECONDITION, DEADLINE_EXCEEDED.

Gateway-клиент: src/backend/gateway/src/common/grpc/planner.client.ts — стриминг в runPlan, дедлайн из диспетчера.

rpc ExportPlan(ExportPlanRequest) returns (ExportPlanResponse)

Поле запроса Описание
plan_json Документ плана
format enum ExportFormat
uav_id Опциональный фильтр миссии
scenario_json Для слоёв сценария в KML/GeoJSON

Ответ: content, filename, content_type.


ValidatorService

Файл: src/backend/proto/geoscan/validator.proto.

rpc Validate(ValidateRequest) returns (ValidateResponse)

Поле Описание
scenario_json Сценарий (может быть пустым)
plan_json План или объединённое тело {scenario, plan}

Парсинг на стороне валидатора (validator/rpc/convert.py:40-58):

  1. Если оба буфера непусты — два отдельных JSON.
  2. Иначе в plan_json ожидается объект с ключами scenario и plan.

Gateway для POST /api/validate с телом { scenario, plan } режет JSON на два буфера (parse-validate-body.ts:7-12, validate.service.ts:25-27) и передаёт оба в gRPC (validator.client.ts:64-67). Вариант (2) остаётся для совместимости, если в HTTP нет пары ключей.

ValidateResponse: metrics, violations[], warnings[], unassigned[], admitted (bool).

Полный отчёт с checks[] упаковывается в JSON внутри gRPC-ответа и разбирается в validate.service.ts.


JSON Schema: сценарий

Файл: src/backend/schema/scenario.schema.json.

Обязательные корневые поля

schema_version, scenario_id, name, crs, launch_sites, landing_sites, fleet, tasks, airspace, weather, objective, solver (строки 7–19).

Ключевые ограничения

Область Правило
crs Только "EPSG:4326"
Координаты точек [lon, lat], lon ∈ [-180,180], lat ∈ [-90,90]
launch_sites min 1; turnaround_time_min ≥ 0 (минуты)
fleet min 1 борт; model, payload, home
tasks[] survey_type, quality (ровно одно из gsd_cm / point_density / line_spacing), area Polygon
airspace allowed + массив no_fly зон с altitude_min_m / altitude_max_m
weather wind_speed_ms ≥ 0, wind_direction_deg 0…360
objective criterion, energy_reserve (0…1), turn_mode (lzp | flyby), safety_buffer_m
solver time_limit_s > 0, seed integer

additionalProperties: false на объектах — лишние поля дают ошибку AJV.

Нарушение схемы

Эндпоинт HTTP код errorCode
POST /api/scenarios 422 SCHEMA_VALIDATION_FAILED + errors[]
POST /api/plans (inline scenario) 400 SCHEMA_VALIDATION_FAILED

Маппинг ошибок AJV: src/backend/gateway/src/common/pipes/ajv-errors.ts.


JSON Schema: план

Файл: src/backend/schema/plan.schema.json.

Обязательные корневые поля

schema_version, scenario_id, generated_at, generator, solver, metrics, missions, unassigned, warnings.

Метрики плана (metrics)

Единицы: время — секунды, расстояние — метры, coverage_frac и turn_time_frac — доли 0…1, violations — счётчики по видам (nfz, airspace, endurance, wind, payload, altitude, turnaround).

Миссия → вылет → фаза

  • mission: uav_id, uav_model, payload, sorties[]
  • sortie: launch_site, landing_site, duration_s, energy_used_frac (0…1), phases[]
  • phase: phase ∈ {takeoff, transit, survey, turn, return, landing}, duration_s, geometry (GeoJSON LineString), опционально altitude_m, transect_id, photos

Опциональный массив geometry[] на уровне плана — галсы заданий (task_geometry в схеме).


Выгрузка KML и GeoJSON

Реализация в планировщике; gateway только проксирует bytes.

KML (export/kml.py)

  • Корень: KML 2.2, namespace OpenGIS.
  • При переданном scenario: отдельный <Document> со слоями:
  • полигоны заданий, allowed airspace, NFZ, точки ВПП (kml.py:50-99, _scenario_folder 92-99).
  • На каждый борт (или один uav_id): <Folder> «Вылет N» и <Placemark> на фазы (kml.py:168-200); заголовок документа — _document_header 102-109; сборка — export_kml 203-218.

Открыть: Google Earth Pro, QGIS (слой KML), веб-просмотрщики с поддержкой KML 2.2.

GeoJSON (export/geojson.py)

  • Корень: FeatureCollection.
  • Слои сценария: properties.layersurvey_area, launch_site, allowed_airspace, no_fly_zone.
  • Маршруты: LineString с координатами [lon, lat, alt_m] (RFC 7946 + высота), properties: uav_id, sortie, phase, altitude_frame: "AGL", duration_s, start_s, task_id, transect_id (geojson.py:52-99).

Открыть: QGIS, geojson.io, MapLibre в UI geoscan.

Форматы plan и waypoints

Реализованы в planner/export/qgc_plan.py и planner/export/waypoints.py (export/__init__.py:21-24). Назначение — обмен с наземными станциями / постобработкой; структура зависит от выбранного формата QGC.

Живые размеры (inline s01 из src/backend/scenarios/s01-simple.json, 2026-09-17)

План 2acdc05c-9e9b-430e-9993-81853bd31718 после completed:

Формат HTTP Размер (байт)
kml 200 38 633
geojson 200 28 339
plan 200 25 498
waypoints 200 3 485

Фрагмент KML (скачан с GET …/export/kml):

<name>ПЗ s01-simple</name>
<description>makespan 1672 с, налёт 1672 с, галсов 15, снимков 384</description>

GeoJSON: FeatureCollection из 39 features (properties.layer для слоёв сценария и LineString фаз).


classDiagram: поток сообщений Plan

classDiagram
  class PlanRequest {
    +bytes scenario_json
    +string criterion
    +double time_limit_s
    +int64 seed
  }
  class PlanProgress {
    +string stage
    +double progress_frac
    +double elapsed_s
  }
  class PlanResult {
    +bytes plan_json
    +SolverStatus solver_status
    +PlanMetrics metrics
  }
  class PlanEvent {
    +PlanProgress progress
    +PlanResult result
  }
  PlanRequest --> PlanEvent : stream
  PlanEvent o-- PlanProgress
  PlanEvent o-- PlanResult

Вывод: клиент gateway читает поток до PlanResult, сохраняет plan_json в БД и шлёт SSE completed.