Диалог получает initialBasePlanId — выбранный (в фокусе) план; селект
«Базовое состояние» предзаполняется им, если он годится в базу (тот же КА,
не PLANNED/DRAFT), иначе остаётся «без истории». Оператор может сменить
базу вручную.
- Тест-кнопка «Подтвердить закладку» в деталях плана для AWAITING_LAYIN
(под подписью «ТЕСТОВЫЙ ФУНКЦИОНАЛ», с подтверждением). Гасится, если по
КА уже заложен план со стартом в пределах ±10 мин (дублирует бэкенд-гард).
- Статус SUPERSEDED: стиль/подпись/легенда.
- Поиск уже созданного черновика — по слоту (КА + окно исходного плана),
т.к. basePlanId черновика теперь = предшественник, а не исходный план.
- Таймлайн: при выборе плана непричастные планы гаснут, а текущий/прошлый/
следующий обводятся яркими рамками (current #2563EB, prev #94A3B8,
next #F59E0B). Асимметрия: prev (явная база выбранного) показывается
всегда, даже REJECTED/SUPERSEDED; next (обратная ссылка) не подсвечивает
мёртвые статусы — иначе вытесненная коррекция ложно числилась бы
«следующей» у заложенного плана.
- детали плана: подтверждение (confirm) на «Отправить» и «Удалить» черновика
- редактор: убраны кнопки «Применить»/«Отмена», undo/redo и сдвиг ◀▶ из тулбара
- окно: предел отдаления план±1ч, дефолт тоже план±1ч; зум кнопками и колесом мыши (центр — курсор)
- таймлайн: drag по горизонтали как карту; минимум окна ~10с
- ось времени: адаптивные деления от секунд до недель, секунды в подписи при мелком масштабе
Вкладка «Планы»:
- «Создать черновик на основе плана» больше не перекидывает в редактор: черновик
создаётся, выбирается на схеме (тумблер «Черновики» вкл.) и показывается подсказка —
оператор сам переходит в «Редактор».
Редактор:
- Витки больше не пропадают «иногда»: жизненный цикл flightLineData теперь полностью
внутри fetch-эффекта на [noradId, mapRange] (сброс + перезагрузка атомарны). Раньше
трассу сбрасывал общий reset по ссылке selectedPlan, а перезагружал отдельный эффект —
фоновый loadData пересобирал selectedPlan с тем же окном, mapRange по значению не менялся,
фетч не перезапускался, витки молча исчезали.
- Явный индикатор вместо немой пустоты: «Загрузка трассы…» / «Нет данных трассы на это окно».
- После удаления открытого черновика редактор не «всплывал» обратно: guard загрузки включений
не тянет режимы по устаревшему draftPlanId, когда план не выбран.
- Редактор включений гейтится на наличие плана (план == миссия): без плана — плейсхолдер
«Выберите КА и план…». Иначе после удаления черновика вкладка оставалась активной по КА
(витки, режим съёмки), хотя редактировать нечего.
- Карта 2D: клик по линии (трасса КА / сеанс сброса) выбирает объект — общий
инспектор с таймлайном; MapLine.name, тип selection "line" (kind track/drop),
связь с работой через modeId; spatialIndex учитывает линии.
- Редактор (TguEditorTab/EditorPanels): единый WorkInspector для клика по карте
и по таймлайну, подсветка соответствующего включения, стили tgu-editor.css.
- config-repo: server.error.include-message=always для pcp-tgu-service и
pcp-mission-planing-service — UI показывает причину 4xx (reason/exception),
а не только HTTP-фразу.
- Кнопка «Создать черновик на основе плана» в деталях плана: создаёт
replace-черновик (окно 1:1, все включения клонируются) и открывает редактор.
Путь «сделать исправленный план» вместо повторной отправки. Доступна для
персистентных правимых баз; DRAFT/PLANNED/LAID_IN исключены (для LAID_IN
replace 1:1 запрещён). Если черновик по базе уже есть — открываем его.
- Диалог создания плана цепочки: вместо списка будущих планов и «Отправить
вручную» — кнопка «Редактировать» (сразу в редактор черновика).
- Сайдбар: повторный клик по выбранному КА снимает выбор (показать все КА).
- STATUS_STYLES в порядке жизненного цикла: убран легаси ACCEPTED, добавлены
LAID_IN/NOT_ACCEPTED/LAYIN_FAILED, у каждого статуса человеческое описание.
- Легенда вкладки «Планы»: тултипы по наведению (title=description), убрана
устаревшая приписка про стрелки.
- TimelineStatusStyle: добавлено обязательное поле description.
- HandlePlanDecisionUseCase: TODO(layin-confirmed) — обработчик приёма события
«план заложен» (AWAITING_LAYIN → LAID_IN) ещё не реализован.
- docs/architect/PCP_TGU_PLAN_STATUS_LIFECYCLE.md: справочник по каждому статусу
(когда наступает / что значит / какой переход), диаграмма, зарезервированный ACCEPTED.
Стрелки и бейджи-цифры цепочки строились по ВРЕМЕНИ (соседние по времени
планы КА) и ложно сцепляли два черновика с одной предысторией как «один —
предок другого». Убираем эту логику целиком (buildSequentialLinks,
buildPlanChains, CHAIN_COLORS/chainColor, тип TimelineLink) и показываем
связь только по реальному basePlanId:
- В деталях плана — строка basePlanId (рендерится, если задан).
- При выборе плана на таймлайне подсвечиваем гало два соседних звена по
basePlanId: предыдущий (planId == basePlanId выбранного) — тёплый янтарь,
следующий (basePlanId == выбранный) — бирюза. Следующих может быть
несколько (черновики с одной предысторией) — подсвечиваются все.
Бэкенд: basePlanId/projected проброшены через PlanResponse (pcp-tgu-service)
и TguPlanResponse (pcp-ui-service — прокси /api/tgu-planning/plans, иначе
Jackson срезал бы неизвестные поля). Требует рестарт обоих сервисов.
Заодно: очистка нотиса деталей при смене фокуса (план/ЗРВ) и блок
«Включения» (счётчики съёмок/сбросов) в правой панели.
Отказ от остаточной UTC-семантики: naive LocalDateTime трактуется как
локальное время единой зоны всех сервисов.
Backend (границы внешних систем):
- VisibilityPayloadParser, SyncSpacecraftFromNsiUseCase: ZoneOffset.UTC →
ZoneId.systemDefault() для проекции внешних Instant/ISO-с-Z в naive.
- тесты границ перевёрнуты на systemDefault (под Europe/Moscow ждут +3).
- чистка UTC-формулировок в DTO-комментариях, мёртвых ссылок на
docs/standards/DATETIME_UTC.md.
Frontend (pcp-tgu-ops-ui):
- utils/utcTime.ts → utils/localTime.ts: parseLocal/formatLocal*/toLocalIso/
toLocalInputValue с локальной семантикой (без дописывания Z и timeZone:UTC).
- замена getUTC*/Date.UTC/toISOString-сериализации на локальные аналоги.
- убраны пользовательские подписи «UTC» в редакторе/таймлайне.
- удалён utcTime.guard.test.ts (форсил обратный UTC-инвариант); тест-фикстуры
сделаны зоно-независимыми. tsc + vitest зелёные под TZ=UTC и TZ=Europe/Moscow.
Включает также прочие незакоммиченные правки рабочего дерева.
Бэкенд (pcp-tgu-service):
- REJECTED трактуется как «план неправильный»: авто-перевыдача больше НЕ
взводится, цепочка встаёт на паузу (новый флаг
SpacecraftPlanningStateEntity.pausedAfterReject, миграция V13). Технические
негативы («не заложен»/«не принят») по-прежнему дают срочную перевыдачу.
- Пауза централизована в PlanProjectionService.project(): для отклонённой
цепочки проекция пустая, воркер выдачи ничего не материализует. Флаг
снимается при ручной выдаче/промоуте черновика/приёмке (оператор вмешался).
- Режим «Черновики»: GET /plans/drafts (опц. ?spacecraftId) -> только DRAFT.
- Гард дублей черновиков: (КА + начало + конец) уникальны среди DRAFT,
повторное создание окна -> 409.
- ZrvWindowMatcher: сравнение по истинной abs-длительности Duration (<=1с),
устойчиво к усечению ISO на фронте до целых секунд.
Фронт (pcp-tgu-ops-ui): вкладка «Планы» — таймлайн цепочек, детали плана,
панель ЗРВ (TguZrvPanel), диалог создания цепочки, режим черновиков.
Удалён устаревший docs/tasks/TASK_tgu-plan-versioning-manual-reissue.md.
Возврат к модели «настенных часов»: одна 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 дренировать/проиграть.
Оператор берёт за основу прошлый план (предыстория) или начинает цепочку с нуля,
набирает окно из двух реальных ЗРВ и создаёт план-кандидат; сразу показывается прогноз
будущей цепочки от него. Рабочим (якорем) план становится после ручной отправки (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 + тесты.
Селектор «Целевая ЗРВ» в EditorToolbar: текущий слот (replace, по умолчанию)
либо будущая ЗРВ из проекции pcp-tgu-service (fetchSpacecraftPlans →
GET /api/plans/{spacecraftId}, фильтр futureZrvWindows: PLANNED строго в
будущем, кроме базы — та же проекция, что валидирует бэк). Выбор окна
переводит обзор редактора и заставляет ensureDraft слать mode=fork-forward
(naive-UTC окно + kpp); селектор блокируется после создания DRAFT.
- WorkInspector показывает аппарат, виток, крен, угол захвата, центр контура (съёмка)
и станцию/сбрасываемые съёмки (сброс)
- planModesToWorks/addShoot прокидывают planModeId, revolution, rollDeg, captureAngleDeg,
centerLat/Lon, surveyIds в модель включения
- место под перечень заданий маршрута (привязка — позже)
- секция «Станция приёма» рендерится только в режиме правки
Time24Field принимал любой текст (99:99 и т.п.), валидным он был только при
коммите/блюре. Добавлена маска maskTime: на каждый ввод оставляет цифры,
расставляет двоеточия и зажимает сегменты (часы 0-23, мин/сек 0-59) с авто-
дополнением первого разряда, поэтому набрать невозможное время нельзя.
Покрыто юнит-тестами (timeFields.test.ts).
Нативные input type=time/datetime-local рендерят время по локали браузера/ОС
(AM/PM) и игнорируют атрибут lang в Chrome — поэтому lang=ru-RU не помогал.
Заменено на собственные контролы (components/timeFields.tsx):
- Time24Field — текстовый ввод ЧЧ:ММ[:СС] с валидацией, детерминированный 24ч;
- DateTime24Field — нативный календарь для даты + Time24Field для времени.
Подключено в окне маршрута редактора и во всех datetime-local фильтрах
(Планы, заявки на карте, создание/фильтр заявок).
- Редактор: убрано накопление целей съёмки — на карте только одно
перекрестие текущей точки (pickTarget заменяется при каждом клике),
с перекрестия убрана подпись. Правая панель переименована в
«Опорная точка прицеливания» с координатами.
- 24-часовой формат: lang="ru-RU" на всех datetime-local инпутах
(Планы, фильтры заявок на карте, создание/фильтр заявок), иначе
браузер рендерил их по локали ОС в AM/PM.
Фронт (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).
- Слева выводим даты начала/конца плана и корректные счётчики
съёмки/сброса (из plan modes, а не из заглушки-черновика).
- На таймлайне показываем реальные включения плана (read-only): убран
фейковый «сброс на весь интервал»; вместо линии «сейчас» — маркеры
начала и конца плана; клик по включению открывает его инфо справа.
- Включения съёмки и сброса разнесены на два ряда (верх/низ) и различаются
цветом, слева — подписи рядов.
- Нижняя панель: активная дорожка резиновая — примыкает к низу и не
пустует, при подъёме панели ряды расширяются; высота бара ограничена
потолком и центрируется в ряду, чтобы узкие включения не вытягивались.
- Время в редакторе парсится/форматируется через utils/utcTime.
Время бэка (naive LocalDateTime и OffsetDateTime) разбиралось через
new Date(string)/Date.parse, из-за чего naive-строки трактовались как
локальное время браузера: вкладки расходились по времени, а путь создания
заявки сдвигал введённое время на офсет. Ввёл единую точку utils/utcTime
(parseUtc/formatUtc*/toUtcInputValue/toUtcIso) и перевёл на неё вкладки
«Планы», «Заявки», «Карта 2D» — чтение, пикеры диапазона, создание заявки.
Плюс на вкладке «Планы» график по умолчанию открывается на интервал
сейчас−30 мин … сейчас+3 дня.
docs/tasks/TASK_datetime-utc-consistency.md — ТЗ на сверку всей группы
сервисов pcp на единообразие времени.
drawPlanWorks проецировал точки напрямую и проверял видимость по одной
копии мира: полигон, пересекающий 180°, рисовался через всю карту, а слой
мигал «то есть, то нет» при зуме/панораме. Перенёс паттерн из
drawRequests/drawSwath: unwrapX (склейка контура через шов) + отрисовка в
трёх копиях мира с culling по каждой.
С остальными КА мы не работаем, поэтому отсекаем их сразу на входе в
loadData: фильтруем платформы по статусу и оставляем планы только для
эксплуатируемых КА (по norad-id), иначе неoperational-аппарат вернулся бы
fallback-строкой по плану в buildPlatformsForInterval.
Статусы SUPERSEDED/EXPIRED удалены из модели tgu-service, поэтому чистим их и
во фронте, чтобы не описывать несуществующие состояния.
- tguTypes: убраны EXPIRED/SUPERSEDED из union TguPlanStatus.
- tguStatus: удалены их стили; заодно снят осиротевший фильтр
isActivePlanStatus/ACTIVE_PLAN_STATUSES (после удаления чекбокса не нужен).
- tguTimelineMapper: EXPIRED убран из PROBLEM_STATUSES.
- tgu-planning.css: удалены классы .status-superseded/.status-expired и
осиротевший блок .tgu-timeline-controls.
- TguPlanningPage: убран тумблер «Показать историю версий» вместе со стейтом
showHistory и фильтром timelinePlans — таймлайн рендерит все планы из API
напрямую (перестали скрываться AWAITING_LAYIN/REJECTED/LAYIN_FAILED/NOT_ACCEPTED).
EXPIRED в requestTypes (статус заявок) — отдельный домен, не тронут.
Раньше таймлайн рисовал планы всех статусов, включая историю версий
(SUPERSEDED/EXPIRED/REJECTED), из-за чего активная цепочка тонула в
наложении старых планов. Теперь по умолчанию показываются только
актуальные планы (PLANNED/ISSUING/WAITING_DECISION/ACCEPTED), а историю
версий можно включить чекбоксом над таймлайном.
Новая вкладка «Заявки»: показ заявок за интервал на 2D-карте,
создание заявки рисованием полигона на карте с формой параметров
(Оптика/РСА) и удаление выбранной заявки (createRequest/deleteRequest,
RequestCreatePanel/RequestDetailsPanel, polygonToWkt). В Tgu2DMapView
добавлен изолированный режим рисования полигона.
2D-карта: панели информации о станции и о заявках, слой станций
(stationsApi), поддержка выбора объектов через спатиальный индекс.
Список слоёв упрощён — трасса/полоса/маркеры КА убраны из переключателя,
по умолчанию включены работы плана, станции и заявки.
Режим «Сброс» теперь рабочий: загружает ЗРВ станций
(ballisticsApi.fetchSatelliteRva), даёт оператору выбрать зону, затем
показывает съёмки плана, заканчивающиеся строго до начала зоны, и
сохраняет сброс в плане (missionPlanningApi.saveDropRoute → DropPanel).
Старый фейковый DownlinkPanel на stationPasses убран из потока.
Режим «Съёмка» сохраняет маршрут через saveSurveyRoute.
Времена ЗРВ приходят как LocalDateTime без зоны и трактуются как UTC
(helper localDateTimeMs), чтобы сравнивать с временами съёмок.
В vite-прокси добавлены pcp-ballistics, pcp-mission и stations.
Polygons crossing the antimeridian were rendered with a large visual jump.
Now each polygon is drawn three times (dx = 0, ±worldSize) after unwrapping
X-coordinates, with viewport culling to skip invisible copies.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tgu-editor.css: add .map-pass-selector styles — floating panel with
semi-transparent background, range slider track, fill, tick marks, draggable
thumbs, and per-handle labels showing orbit number + UTC time.
docs: update PCP_ARCHITECTURE.md to reflect pcp-tgu-ops-ui is now a real SPA
(not a placeholder); add PCP_TGU_OPS_UI_CAPABILITY_MAP.md describing feature
areas and data sources for the TGU ops frontend.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
drawTracks: detect antimeridian crossing by comparing consecutive screen X
positions against worldSize/2; use moveTo instead of lineTo on large jumps
so tracks don't draw a horizontal line across the globe.
Tgu2DMapView: replace the heuristic lon-range check (> 300°) with an exact
condition — skip bbox culling when the canvas is wider than the world tile
(entire globe visible) or when topLeft.lon >= bottomRight.lon (viewport
straddles the antimeridian). Both cases previously produced a nonsensical
bbox that hid polygons near ±180°.
drawRequests: remove per-polygon screen-space culling; viewport filtering is
already handled upstream by visiblePolygons, so the redundant check was
incorrectly hiding requests at high zoom levels near canvas edges.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace synthetic orbit calculation (Kepler + spacecraft ID hash) with real
data from pcp-ballistics-service via a new proxy endpoint in pcp-ui-service.
Backend:
- TguPlanningController: add GET /api/tgu-planning/spacecraft/{noradId}/flight-line
that proxies FlightLineDTO[] from BallisticsService for a given time interval
Frontend:
- Fetch FlightLineDTO[] (revolution, time, lat/long, swath boundaries) using
the spacecraft's noradId for the current 24h map window
- Group points by real revolution number to build OrbitPassInfo[] with actual
orbit numbers and UTC times instead of synthetic Kepler-based estimates
- Build ground track MapLines and swath MapPolygons from real coordinates;
swath polygon = outer-left edge forward + outer-right edge reversed
- Enable swath layer in the editor map; draw with teal fill/stroke matching
the track colour
- MapPassSelector now shows real revolution numbers in header and handle labels
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
На вкладке Редактор 2D-карта теперь показывает включения выбранного плана
и новые съёмки, добавленные в текущей сессии.
- editorApi: типы PlanSurveyMode/PlanDropMode/PlanMode и fetchPlanModes —
вызывает GET /api/current-plans/missions/{planId}/modes на pcp-mission-planing-service,
где planId TGU совпадает с missionId (выяснено по Camunda-процессу)
- TguEditorTab: загружает включения при выборе плана, строит MapPolygon[]
из SURVEY-режимов через parseWktPolygon(contourWkt), включён слой planWorks
- Tgu2DMapView: разделены pending-маркер (оранжевый, выбор точки)
и confirmed-маркеры (бирюзовые, подтверждённые съёмки из драфта);
клик на маркер съёмки открывает WorkInspector
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switch the 2D map sidebar from single-spacecraft selection to per-spacecraft visibility toggles, add a show-all action, and update map tab labels and styles for the all-spacecraft view.
Port the TGU editor prototype into pcp-tgu-ops-ui as a new editor tab, add draft/conflict/pass logic with tests, extend the 2D map picking API, and include editor styles/theme aliases and task/prototype docs.
Validation: npm run test; npm run build in services/pcp-tgu-ops-ui.