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):
PlanProgress—stage,progress_frac(0…1),objective_value,elapsed_s,messagePlanResult—plan_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):
- Если оба буфера непусты — два отдельных JSON.
- Иначе в
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_folder92-99). - На каждый борт (или один
uav_id):<Folder>«Вылет N» и<Placemark>на фазы (kml.py:168-200); заголовок документа —_document_header102-109; сборка —export_kml203-218.
Открыть: Google Earth Pro, QGIS (слой KML), веб-просмотрщики с поддержкой KML 2.2.
GeoJSON (export/geojson.py)¶
- Корень:
FeatureCollection. - Слои сценария:
properties.layer∈survey_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.