Commit Graph

29 Commits

Author SHA1 Message Date
Дмитрий Соловьев 3638afdb85 pcp: откат UTC-слоя — сквозной naive LocalDateTime без зоны на проводе
Возврат к модели «настенных часов»: одна ISO-строка без зоны
("2026-05-29T01:00:00") от БД до экрана, никто не привязывает её к
часовому поясу, сдвигаться нечему.

Бэкенд — снять зону с границы API:
- DTO OffsetDateTime/Instant -> LocalDateTime и убрана конвертация
  atOffset(ZoneOffset.UTC)/toUtcOffset() в фабриках и мапперах:
  tgu PlanResponse/NewChainDTOs/TguPlanningDTO, request RequestApiDtos,
  complex ComplexPlanRun*DTO, dynamic ObservationParametersDebugDTO.
- Удалён толерантный Jackson-слой: PcpTimeModule(+autoconfig, ServiceLoader,
  imports), PcpTimeModuleV3, LocalDateTimeTimeZoneSerializer; снята их
  регистрация в Camunda/Rest/dynamic JacksonConfig. Штатный JSR-310
  (JavaTimeModule + WRITE_DATES_AS_TIMESTAMPS=false) оставлен.
- Kafka-конверт KafkaMessage.time сериализуется ISO без офсета.
- mission-planing Mode/SurveyMode/DropMode entity и репозитории/сервис
  переведены на LocalDateTime; колонки БД остаются timestamptz (Hibernate
  мапит на naive без миграции — прецедент MissionEntity/PlanEntity).
- RequestServiceClient (dynamic) синхронизирован по типу с request-service.

Анти-регрессия: LocalDateTime.now(ZoneOffset.UTC) для серверных штампов
оставлен (выбор настенных часов UTC, не зона на проводе).

Фронт — снять офсет с egress: toUtcIso/фильтры отдают naive без Z
(после удаления толерантного десериализатора штатный парсер падал бы на Z).
Дисплейные хелперы (показ «как пришло», без сдвига) сохранены.

Тесты: контрактные ждут naive ISO; удалены UTC-артефакты (нормализация
офсетов) и тесты прослоек. Удалён docs/standards/DATETIME_UTC.md.

Операционно перед выкатом: сервисы катить согласованно; Kafka-топики с
ранее опубликованным офсетом +00:00 дренировать/проиграть.
2026-06-04 00:03:52 +03:00
Дмитрий Соловьев 9c2d8516bc pcp: создание плана новой цепочки из вкладки «Планы» (база/без истории, окно из двух ЗРВ)
Оператор берёт за основу прошлый план (предыстория) или начинает цепочку с нуля,
набирает окно из двух реальных ЗРВ и создаёт план-кандидат; сразу показывается прогноз
будущей цепочки от него. Рабочим (якорем) план становится после ручной отправки (promote)
и приёмки — отдельного механизма якорения не вводим.

pcp-tgu-service:
- сырые ЗРВ: PlanCalculationService.rawWindows + PlanProjectionService.rawZrvWindows,
  GET /api/plans/{spacecraftId}/zrv (исходные окна снапшота, не cooked-проекция);
- CreateChainPlanUseCase + POST /api/plans/new-chain: опциональная база (клон включений
  в окно) либо пустая миссия без истории; прогноз цепочки через projectAfterWindow;
- ZrvWindowMatcher (старт+КПП одной зоны, конец — другой); assertDraftSlotStillValid
  переведён на него — единая проверка для replace/fork-forward и окна из двух ЗРВ;
- тесты: CreateChainPlanUseCaseTest (8), ZrvWindowMatcherTest (7), правки promote-тестов.

pcp-mission-planing-service:
- POST /api/missions/blank — пустая миссия для сценария «без истории» (+ 2 теста).

pcp-tgu-ops-ui:
- API fetchZrvWindows/createChainPlan; диалог «Создать план» во вкладке «Планы»
  (выбор базы, стартовой и конечной ЗРВ) с показом спрогнозированной цепочки и ручной
  отправкой; подсветка ЗРВ выбранного КА на таймлайне;
- «Целевая ЗРВ» убрана из редактора (он остаётся под replace-правку); удалён ставший
  мёртвым editorForkForward; createChainWindows + тесты.
2026-06-03 16:25:46 +03:00
Дмитрий Соловьев 2a841fc7b3 tgu: DELETE /plans/{draftPlanId} — отказ от черновика (бэкенд + кнопка «Отмена» и авто-отмена в редакторе) 2026-06-03 13:59:52 +03:00
Дмитрий Соловьев ae7effe1fa pcp-tgu-service: Ф5 — promote черновика с оптимистичным локом базы (POST /plans/{id}/promote) 2026-06-03 13:36:59 +03:00
Дмитрий Соловьев 85ce238f52 pcp: Ф2 — доработки по ревью (валидация ошибок клона, идемпотентность DRAFT, тесты)
- MissionPlaningClient: маппинг ошибок mission-planing (4xx как есть, 5xx → 502) + таймаут 30с
- CreateDraftUseCase: гард идемпотентности — второй открытый DRAFT на тот же план → 409
- cloneMission: явная валидация mode (неизвестный → 400 вместо тихого полного клона)
- константы DraftMode/CloneMode вместо строковых литералов
- kdoc: семантика fork-forward (границы-слот vs времена включений; съёмка без сброса)
- тесты: CreateDraftUseCaseTest (13 кейсов), MissionCloneTest (+съёмка без сброса)
2026-06-03 13:09:52 +03:00
Дмитрий Соловьев 0d69617420 pcp: Ф2 — черновик-клон миссии (replace + fork-forward)
mission-planing: POST /api/missions/{id}/clone — полный клон (replace)
или по перекрытию с окном (fork-forward, [start,stop) открытый правый конец);
пустые drop-mode (все съёмки вне окна) не клонируются

tgu-service: PlannedPlanStatus.DRAFT; POST /api/plans/{planId}/draft
(CreateDraftUseCase: guard replace/fork-forward, clone mission → planId=newMissionId,
компенсация при сбое); DRAFT отфильтрован из PlanQueryService read-пути;
MissionPlaningClient + MissionPlaningProperties + mission-planing.base-url в yaml

тесты: MissionCloneTest (6 граничных кейсов предиката перекрытия),
PlanQueryServiceTest (DRAFT не видна в ответе)
2026-06-03 12:53:57 +03:00
Дмитрий Соловьев e060e13b85 pcp-tgu-service: боевой POST /api/plans/{planId}/issue + гард точки невозврата
- ручная выдача вынесена из TestTguController в PlanController
- assertReissuableInSlot: 422 при LAID_IN и заморозке (now >= start - layinFreezeMinutes),
  409 при активной выдаче другого плана КА; перевыдача того же плана поверх своей
  in-flight attempt разрешена (новая attemptNo++ замещает летящую)
- REISSUABLE_STATUSES удалён — гард по времени/статусу его заместил
- layinFreezeMinutes (деф. 10) в PlanningProperties + config-repo/application yaml
- граничные тесты в IssueDuePlanWorkerOnDemandTest, переписан PlanIssueAndDecisionFlowTest
- дизайн-док версионирования (фаза 1 отмечена сделанной)
2026-06-03 12:40:03 +03:00
Дмитрий Соловьев e68c17e792 pcp-tgu-service: миграция с WebFlux на servlet/MVC
WebFlux в tgu был фиктивным: блокирующая JPA, контроллеры оборачивали
блокирующие вызовы в Mono.fromCallable{}.subscribeOn(boundedElastic()),
WebClient .block()-ался сразу. Единственный WebFlux-only сервис флота.

- build: подключён pcp-types-lib (тянет starter-web → servlet), springdoc
  webflux-ui → webmvc-ui; starter-webflux оставлен только ради WebClient
- контроллеры возвращают значения напрямую (без Mono-обёрток)
- удалено локальное Jackson-2 зеркало PcpTimeModule.kt → модуль из либы
  в CamundaJacksonConfig; Jackson-3 зеркало остаётся (Boot 4 держит
  Jackson 3 на HTTP-границе и для MVC), WebFluxJacksonConfig → RestJacksonConfig
2026-06-02 23:14:12 +03:00
Дмитрий Соловьев cde4ffa9b6 pcp-tgu-service: толерантность времени на Jackson-3 пути (REST-кодеки WebFlux) 2026-06-02 22:57:49 +03:00
Дмитрий Соловьев 405f02aa86 pcp: слой толерантности времени — читать naive(=UTC) и Z во всём флоте
PcpTimeModule (libs/pcp-types-lib): толерантные Jackson-десериализаторы
LocalDateTime/OffsetDateTime/Instant — любой потребитель читает оба формата
на проводе и нормализует к UTC. Только чтение, формат записи не меняется.

Подключение фронтом по флоту:
- Spring Boot auto-configuration + AutoConfiguration.imports — Boot-маппер
  всех 12 сервисов на pcp-types-lib (без правок в сервисах);
- Jackson SPI (META-INF/services) — ручные мапперы с findAndRegisterModules();
- точечно: dynamic-plan/JacksonConfig (ручной ObjectMapper);
- tgu (WebFlux, не зависит от либы) — локальное зеркало модуля в
  CamundaJacksonConfig (Jackson-2 путь).

Зачем: микросервисы деплоятся независимо; с толерантностью продюсера можно
флипать LocalDateTime -> OffsetDateTime (Z) в любом порядке без синхронного
деплоя — потребитель не падает на парсинге офсета.

Тесты: PcpTimeModuleTest (оба формата -> один UTC-момент + авто-подхват SPI).
Доки: DATETIME_UTC.md (раздел 2bis + найденные баги формата), CHECKLIST.md.
2026-06-02 22:35:50 +03:00
Дмитрий Соловьев c2e2b2922f pcp: миграция времени плана ТГУ на явный Z (OffsetDateTime)
PlanResponse (pcp-tgu-service) и зависящий от него TguPlanResponse (pcp-ui-service)
переведены с naive LocalDateTime на OffsetDateTime в UTC → на проводе явная зона Z.
Домен/БД (PlannedPlanEntity/CalculatedPlan) остаются naive LocalDateTime (= UTC);
конвертация только на границе API (atOffset(ZoneOffset.UTC)). Потребитель один
(ui-service.TguPlanningService), обновлён синхронно; фронт parseUtc совместим с Z.

- контрактные тесты обновлены на Z (PlanResponseContractTest, TguPlanningDtoContractTest);
- тестовые фикстуры/ассерты приведены к OffsetDateTime/Z (PlanQueryServiceTest,
  CatalogControllerTest, TguPlanningServiceTest);
- docs/standards/DATETIME_UTC.md: отражена миграция и обновлена таблица сервисов.
2026-06-02 22:02:13 +03:00
Дмитрий Соловьев 260583b134 pcp: единообразие времени (UTC) по группе сервисов
Фронт (pcp-tgu-ops-ui):
- тест-страж utcTime.guard.test.ts: запрещает Date.parse / new Date(string) /
  toLocale* без timeZone вне utils/utcTime; тестовые фикстуры переведены на parseUtc.

Бэкенд:
- UTC-now: LocalDateTime.now() -> LocalDateTime.now(ZoneOffset.UTC) по всем сервисам
  (entity-дефолты, воркеры, use case'ы) и в тестовых фикстурах.
- Конвертации к UTC (баги на не-UTC JVM, каждая с регресс-тестом в не-UTC поясе):
  - tgu: VisibilityPayloadParser, SyncSpacecraftFromNsiUseCase (Instant -> UTC LocalDateTime);
  - mission-planing: MissionPlaningService (OffsetDateTime -> UTC через toUtcLocalDateTime);
  - dynamic-plan: RequestServiceClient (OffsetDateTime -> UTC);
  - slots: SlotRepositoryImpl (getObject(LocalDateTime) вместо getTimestamp().toLocalDateTime()).
- Контрактные сериализационные тесты формата на проводе (naive без зоны / Z):
  PlanResponse (tgu), RequestResponseDto (request, эталон), TguPlanning DTO (ui).
- KDoc-инвариант «=UTC» на UI-facing DTO (tgu PlanResponse, ui-service DTO вкладки ТГУ).

Документация: docs/standards/DATETIME_UTC.md (соглашение, таблица сервисов, под-задачи на миграцию к Z),
docs/standards/CHECKLIST.md (пункт про UTC на API).
2026-06-02 21:51:38 +03:00
Дмитрий Соловьев a61e23c095 pcp-tgu-service: не дублировать выданный план проекцией
Проекция будущей цепочки якорилась только на принятый базовый план
(currentBasePlanId) и игнорировала выданный, но ещё не принятый план в
полёте (ISSUING/WAITING_DECISION). Из-за этого она заново выводила уже
выданный слот, и при дрейфе снапшота ЗРВ (сместился КПП или границы окна)
точечный дедуп по businessKey в PlanQueryService не срабатывал — на
вкладке «Планы» появлялся призрак-дубль PLANNED поверх WAITING_DECISION.

Теперь якорь проекции — самая дальняя зафиксированная точка цепочки: если
есть план в полёте, цепочку строим после него. На путь выдачи не влияет:
там activePlanId к моменту вызова проекции уже снят.
2026-06-02 16:53:24 +03:00
Дмитрий Соловьев e738c236a3 Не блокировать планирование неподтверждённым планом
Если план простоял в WAITING_DECISION дольше waiting-decision-grace-minutes
(по умолчанию 30) после своего начала и решения по нему так и нет — перестаём
ждать подтверждение: снимаем активную выдачу, двигаем якорь цепочки на этот
план и выдаём следующий. Статус плана не меняется (остаётся WAITING_DECISION,
не ACCEPTED) — это молчаливое продолжение цепочки, а не приёмка. Запоздалое
решение по такому плану просто пишется на attempt и не откатывает ушедшую
вперёд цепочку.
2026-06-02 14:51:43 +03:00
Дмитрий Соловьев c76a823dbc Редизайн pcp-tgu-service: хранить только выданное, будущее считать на лету
Раньше вся будущая цепочка планов материализовалась в planned_plan как
PLANNED-строки, а отдельный воркер пересчёта (RebuildPlanScheduleUseCase +
PlanRecalculationJob) держал её в актуальном состоянии и гасил выпавшие планы
в SUPERSEDED/EXPIRED. Это плодило вложенные планы, гонки пересчёта и мусор в БД.

Новая модель: в БД живёт только выданное (история навсегда), будущее — проекция,
вычисляемая по запросу из ЗРВ. Перекрывать (supersede) и протухать (expire)
больше нечего.

Бэкенд:
- Удалены TrackedPlan (entity/source/status + repo) и PlanRecalculationJob
  (entity/repo/service/worker) — материализация и воркер пересчёта больше не нужны.
- Удалён RebuildPlanScheduleUseCase; будущее строит новый PlanProjectionService
  поверх PlanCalculationService.
- PlanQueryService отдаёт проекцию (выданное из БД + будущее на лету).
- Новый флоу реакции на смену параметров станции: StationParamsChangedConsumer +
  HandleStationParamsChangedUseCase (срочный перевыпуск через planning_state).
- Новые консьюмеры обратной связи исполнения: LayinFailedConsumer,
  PlanNotAcceptedConsumer (+ DTO PlanExecutionFeedbackMessage,
  StationParamsChangedMessage).
- PlannedPlanStatus: убраны мёртвые SUPERSEDED/EXPIRED; статусы выровнены под
  жизненный цикл выдачи/закладки.

Миграции (ddl-auto: validate):
- V7  drop tracked_plan
- V8  drop plan_recalculation_job
- V9  planning_state: поля срочного перевыпуска
- V10 снять unique-ограничение бизнес-ключа planned_plan
- V11 удалить легаси PLANNED-строки (материализованное будущее)
- V12 удалить легаси SUPERSEDED/EXPIRED и столбец superseded_by_plan_id

Тесты: удалены JobRerunFlowTest и RebuildPlanScheduleUseCaseTest; добавлены
PlanProjectionServiceTest, PlanQueryServiceTest, IssueDuePlanWorkerOnDemandTest,
HandleStationParamsChangedUseCaseTest, VisibilityPayloadParserTest. Скорректированы
PlanIssueAndDecisionFlowTest, PlanCalculationServiceTest, VisibilityFlowTest и др.

V11/V12 необратимы — перед миграцией на проде снять дамп planned_plan.
2026-06-02 14:32:51 +03:00
Дмитрий Соловьев d630a891f6 Тест: провал видимости перекрывается одним планом (форма Reflexio)
Воспроизводит форму с экрана Reflexio (раннее окно, большой провал
видимости, далёкий кластер) и проверяет, что цепочка перекрывает провал
одним длинным планом и доходит до далёкого кластера без разрыва.
2026-06-02 11:32:16 +03:00
Дмитрий Соловьев e8f253ac6a Гарантия строгого нахлёста планов без разрывов в pcp-tgu-service
Старт следующего плана всегда выбирается строго внутри текущего плана
(нахлёст по времени), а раньше был fallback на ближайший более поздний
старт, который мог дать разрыв. Если ни одна ЗРВ не начинается внутри
текущего плана — цепочка обрывается, но разрыв между планами не создаётся.
calculatePlansAfter тоже стартует хвост строго внутри принятого плана.

Инвариант цепочки в тестах усилен проверкой строгого нахлёста; добавлен
тест на провал видимости.
2026-06-02 11:18:02 +03:00
Дмитрий Соловьев 96f698112b Запрет вложенных планов: строго монотонная цепочка в pcp-tgu-service
Цепочка планов теперь строит соседние планы со строго возрастающими
стартом и концом, поэтому ни один план не может лежать целиком внутри
другого. Нахлёст «на одну ЗРВ» считается по времени, а не по плоскому
индексу кандидата — это убирает вложенность при нескольких станциях,
видящих один пролёт одновременно (кейс Emissio).

Подчистка устаревших PLANNED-планов при rebuild переведена на условие
end_time > now, чтобы гасить и уже стартовавшие вложенные планы.
2026-06-02 11:04:38 +03:00
Дмитрий Соловьев bc3dd8f0b8 Конфигурация как в dev 2026-06-01 10:16:41 +03:00
Дмитрий Соловьев 58d067c36f Merge branch 'dev' into route-processing
Conflict resolution:
- build.gradle.kts: dev version (keep nstart.cloud repo + jacoco/asm mavenCentral)
- settings.gradle.kts: project name from route-processing (observatio-terrae), rest from dev
- config addresses (application-local, pcp-mission-planing-service, pcp-dynamic-plan application.yaml): dev versions
- menu.html: merged both branches (TGU planning + bookings items)
- CatalogControllerTest.kt: merged both mocks (TguPlanningService + BallisticsService)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 09:19:29 +03:00
Дмитрий Соловьев 39f108e496 Handle Ordinis outage in TGU service
Classify Ordinis classifier transport failures and empty responses as unavailable, keep stale platform cache when possible, and expose Prometheus metrics/alerting for the outage signal.
2026-05-30 21:04:57 +03:00
Дмитрий Соловьев c3a1a8b4a1 Add PCP architecture docs and TGU ops updates
Capture current PCP architecture notes, service-map prototypes, TGU operations UI/map work, local configuration updates, database helper scripts, and request/sample JSON artifacts.
2026-05-30 14:18:19 +03:00
Дмитрий Соловьев e7c2060e2e pcp-tgu 2026-05-29 13:35:32 +03:00
emelianov 7dc786362c -sonar 2026-05-27 17:09:47 +03:00
Дмитрий Соловьев 3dce0aec0d Основные изменения:
состояние окон видимости, расписания планов, попыток выдачи и job’ов перенесено в БД;
план и попытка выдачи разделены на разные сущности;
Kafka-события теперь двигают persisted state/jobs, а не локальную память pod’а;
BPMN createSatelliteMission теперь отвечает только за одну попытку выдачи плана;
обновление слотов после принятия плана вынесено в отдельный BPMN-процесс;
workers используют DB claim/lock model и безопасны для нескольких pod’ов;
тестовый ручной API вынесен в отдельный disabled-by-default controller
2026-05-27 10:29:41 +03:00
Дмитрий Соловьев cf3bb63bc1 Запрос только аппаратов со статусом operational 2026-05-26 14:03:59 +03:00
Дмитрий Соловьев 4edf9d7aa8 Добавлен кэш, запросы в НСИ раз в 15 минут 2026-05-26 12:41:08 +03:00
Дмитрий Соловьев cbbafebf79 Перевод pcp-tgu на новый НСИ 2026-05-26 11:07:37 +03:00
Дмитрий Соловьев d48ddd2657 Init 2026-05-25 14:23:52 +03:00