diff --git a/services/pcp-tgu-ops-ui/src/features/tgu-editor/EditorPanels.tsx b/services/pcp-tgu-ops-ui/src/features/tgu-editor/EditorPanels.tsx index 822f593..19ba16e 100644 --- a/services/pcp-tgu-ops-ui/src/features/tgu-editor/EditorPanels.tsx +++ b/services/pcp-tgu-ops-ui/src/features/tgu-editor/EditorPanels.tsx @@ -29,6 +29,10 @@ type WorkInspectorProps = { work: EditorWork; modes: EditorMode[]; stations: EditorGroundStation[]; + /** Название аппарата редактора — для блока параметров включения. */ + spacecraftLabel?: string; + /** Метка съёмки по id режима — для перечня сбрасываемых маршрутов у сброса. */ + surveyModeLabel?: (surveyModeId: number) => string; conflicts: string[]; /** Включение существующего плана — показываем только информацию, без правок. */ readOnly?: boolean; @@ -118,6 +122,8 @@ export function WorkInspector({ work, modes, stations, + spacecraftLabel, + surveyModeLabel, conflicts, readOnly, onUpdate, @@ -160,6 +166,58 @@ export function WorkInspector({ )} +
+
Параметры включения
+
+
Аппарат
+
{spacecraftLabel ?? "—"}
+
Виток
+
{work.revolution ?? "—"}
+ {work.kind === "shoot" && ( + <> +
Крен
+
{work.rollDeg != null ? `${work.rollDeg.toFixed(1)}°` : "—"}
+
Угол захвата
+
{work.captureAngleDeg != null ? `${work.captureAngleDeg.toFixed(1)}°` : "—"}
+
Центр контура
+
+ {work.centerLat != null && work.centerLon != null + ? `${work.centerLat.toFixed(2)}, ${work.centerLon.toFixed(2)}` + : "—"} +
+ + )} + {work.kind === "downlink" && ( + <> +
Станция
+
{stations.find((station) => station.id === work.stationId)?.name ?? work.stationId ?? "—"}
+ + )} +
+
+ + {work.kind === "shoot" && ( +
+
Задания под маршрутом
+
Привязка заданий к маршруту появится позже.
+
+ )} + + {work.kind === "downlink" && ( +
+
Сбрасываемые съёмки
+ {work.surveyIds && work.surveyIds.length > 0 ? ( + + ) : ( +
Маршруты съёмки к сбросу не привязаны.
+ )} +
+ )} + {work.kind === "shoot" && (
Режим аппаратуры
@@ -183,20 +241,16 @@ export function WorkInspector({
)} - {work.kind === "downlink" && ( + {work.kind === "downlink" && !readOnly && (
Станция приёма
- {readOnly ? ( -
{stations.find((station) => station.id === work.stationId)?.name ?? work.stationId ?? "—"}
- ) : ( - - )} +
)} diff --git a/services/pcp-tgu-ops-ui/src/features/tgu-editor/TguEditorTab.tsx b/services/pcp-tgu-ops-ui/src/features/tgu-editor/TguEditorTab.tsx index a3bfedd..1530538 100644 --- a/services/pcp-tgu-ops-ui/src/features/tgu-editor/TguEditorTab.tsx +++ b/services/pcp-tgu-ops-ui/src/features/tgu-editor/TguEditorTab.tsx @@ -453,7 +453,12 @@ export function TguEditorTab({ selectedSpacecraftId, selectedPlan, appliedRange, endMs, label: mode?.label ?? "Съёмка", modeId, - target + target, + revolution: surveyRoute?.revolution ?? selectedShootPass.revolution, + rollDeg: surveyRoute?.roll ?? routeRoll, + captureAngleDeg: surveyRoute?.captureAngle ?? DEFAULT_CAPTURE_ANGLE_DEG, + centerLat: surveyRoute?.lat, + centerLon: surveyRoute?.longitude }) ); setSelectedWorkId(id); @@ -468,6 +473,17 @@ export function TguEditorTab({ selectedSpacecraftId, selectedPlan, appliedRange, [stationDtos] ); + // Метка съёмки по id режима — для перечня сбрасываемых маршрутов в инспекторе сброса. + const surveyModeLabel = useCallback( + (surveyModeId: number) => { + const mode = planModes.find( + (m): m is PlanSurveyMode => m.type === "SURVEY" && m.id === surveyModeId + ); + return mode ? `Съёмка · виток ${mode.revolution}` : `Маршрут #${surveyModeId}`; + }, + [planModes] + ); + const addDropWork = async () => { if (!selectedPlan || !selectedZone || selectedDropSurveyIds.size === 0) return; const numericNorad = noradId ? Number(noradId) : NaN; @@ -735,6 +751,8 @@ export function TguEditorTab({ selectedSpacecraftId, selectedPlan, appliedRange, work={selectedWork} modes={modes} stations={stations} + spacecraftLabel={activeLabel} + surveyModeLabel={surveyModeLabel} conflicts={conflicts[selectedWork.id] ?? []} readOnly={selectedWork.origin === "plan"} onUpdate={onChangeWork} diff --git a/services/pcp-tgu-ops-ui/src/features/tgu-editor/editorDraft.test.ts b/services/pcp-tgu-ops-ui/src/features/tgu-editor/editorDraft.test.ts index eedaae2..4eb494e 100644 --- a/services/pcp-tgu-ops-ui/src/features/tgu-editor/editorDraft.test.ts +++ b/services/pcp-tgu-ops-ui/src/features/tgu-editor/editorDraft.test.ts @@ -88,6 +88,11 @@ describe("editorDraft", () => { startMs: parseUtc("2026-05-29T01:10:00Z"), endMs: parseUtc("2026-05-29T01:10:00Z") + 180_000, origin: "plan", + planModeId: 11, + revolution: 4321, + rollDeg: 0, + centerLat: 55.7, + centerLon: 37.6, targetLat: 55.7, targetLon: 37.6 }, @@ -98,7 +103,10 @@ describe("editorDraft", () => { startMs: parseUtc("2026-05-29T01:40:00Z"), endMs: parseUtc("2026-05-29T01:40:00Z") + 120_000, origin: "plan", - stationId: "102" + planModeId: 22, + revolution: 4322, + stationId: "102", + surveyIds: [11] } ]); }); diff --git a/services/pcp-tgu-ops-ui/src/features/tgu-editor/editorDraft.ts b/services/pcp-tgu-ops-ui/src/features/tgu-editor/editorDraft.ts index d3b1b4e..54179fe 100644 --- a/services/pcp-tgu-ops-ui/src/features/tgu-editor/editorDraft.ts +++ b/services/pcp-tgu-ops-ui/src/features/tgu-editor/editorDraft.ts @@ -22,6 +22,12 @@ export type AddShootInput = { label: string; modeId?: string; target: EditorTarget; + /** Параметры построенного маршрута (если съёмка строилась с превью). */ + revolution?: number; + rollDeg?: number; + captureAngleDeg?: number; + centerLat?: number; + centerLon?: number; }; export type AddDownlinkInput = { @@ -66,6 +72,12 @@ export function planModesToWorks(modes: PlanMode[]): EditorWork[] { startMs, endMs, origin: "plan", + planModeId: mode.id, + revolution: mode.revolution, + rollDeg: mode.roll, + centerLat: mode.lat, + centerLon: mode.longitude, + // Центр контура — он же опорная точка для проверки видимости. targetLat: mode.lat, targetLon: mode.longitude }]; @@ -78,7 +90,10 @@ export function planModesToWorks(modes: PlanMode[]): EditorWork[] { startMs, endMs, origin: "plan", - stationId: mode.station ?? undefined + planModeId: mode.id, + revolution: mode.revolution, + stationId: mode.station ?? undefined, + surveyIds: mode.surveys }]; }); } @@ -136,6 +151,11 @@ export function addShoot(draft: Draft, input: AddShootInput): Draft { targetName: input.target.name, targetLat: input.target.lat, targetLon: input.target.lon, + revolution: input.revolution, + rollDeg: input.rollDeg, + captureAngleDeg: input.captureAngleDeg, + centerLat: input.centerLat, + centerLon: input.centerLon, origin: "added", isNew: true }; diff --git a/services/pcp-tgu-ops-ui/src/features/tgu-editor/model/editorTypes.ts b/services/pcp-tgu-ops-ui/src/features/tgu-editor/model/editorTypes.ts index c1bfa97..2a69a9d 100644 --- a/services/pcp-tgu-ops-ui/src/features/tgu-editor/model/editorTypes.ts +++ b/services/pcp-tgu-ops-ui/src/features/tgu-editor/model/editorTypes.ts @@ -20,11 +20,28 @@ export type EditorShootWork = EditorWorkBase & { targetName?: string; targetLat?: number; targetLon?: number; + /** Идентификатор сохранённого режима съёмки в БД (есть у включений плана). */ + planModeId?: number; + /** Номер витка. */ + revolution?: number; + /** Крен (gamma), град. */ + rollDeg?: number; + /** Угол захвата (полуширина полосы), град. Есть только у свежепостроенного маршрута. */ + captureAngleDeg?: number; + /** Центр контура съёмки, град. */ + centerLat?: number; + centerLon?: number; }; export type EditorDownlinkWork = EditorWorkBase & { kind: "downlink"; stationId?: string; + /** Идентификатор сохранённого режима сброса в БД (есть у включений плана). */ + planModeId?: number; + /** Номер витка зоны радиовидимости. */ + revolution?: number; + /** Идентификаторы режимов съёмки (survey_modes), сбрасываемых в этом сеансе. */ + surveyIds?: number[]; }; export type EditorWork = EditorShootWork | EditorDownlinkWork; diff --git a/services/pcp-tgu-ops-ui/src/styles/tgu-editor.css b/services/pcp-tgu-ops-ui/src/styles/tgu-editor.css index 01f341e..0b8a24a 100644 --- a/services/pcp-tgu-ops-ui/src/styles/tgu-editor.css +++ b/services/pcp-tgu-ops-ui/src/styles/tgu-editor.css @@ -47,6 +47,15 @@ font-size: 0.68rem; } +.tgu-editor-panel__list { + margin: 0; + padding-left: 1.1rem; + display: flex; + flex-direction: column; + gap: 0.2rem; + font-size: 0.72rem; +} + .tgu-editor-toolbar__group { display: flex; gap: 0.35rem;