pcp-tgu-ops-ui: панель «Параметры включения» в инспекторе маршрута
- WorkInspector показывает аппарат, виток, крен, угол захвата, центр контура (съёмка) и станцию/сбрасываемые съёмки (сброс) - planModesToWorks/addShoot прокидывают planModeId, revolution, rollDeg, captureAngleDeg, centerLat/Lon, surveyIds в модель включения - место под перечень заданий маршрута (привязка — позже) - секция «Станция приёма» рендерится только в режиме правки
This commit is contained in:
@@ -29,6 +29,10 @@ type WorkInspectorProps = {
|
|||||||
work: EditorWork;
|
work: EditorWork;
|
||||||
modes: EditorMode[];
|
modes: EditorMode[];
|
||||||
stations: EditorGroundStation[];
|
stations: EditorGroundStation[];
|
||||||
|
/** Название аппарата редактора — для блока параметров включения. */
|
||||||
|
spacecraftLabel?: string;
|
||||||
|
/** Метка съёмки по id режима — для перечня сбрасываемых маршрутов у сброса. */
|
||||||
|
surveyModeLabel?: (surveyModeId: number) => string;
|
||||||
conflicts: string[];
|
conflicts: string[];
|
||||||
/** Включение существующего плана — показываем только информацию, без правок. */
|
/** Включение существующего плана — показываем только информацию, без правок. */
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
@@ -118,6 +122,8 @@ export function WorkInspector({
|
|||||||
work,
|
work,
|
||||||
modes,
|
modes,
|
||||||
stations,
|
stations,
|
||||||
|
spacecraftLabel,
|
||||||
|
surveyModeLabel,
|
||||||
conflicts,
|
conflicts,
|
||||||
readOnly,
|
readOnly,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
@@ -160,6 +166,58 @@ export function WorkInspector({
|
|||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section className="tgu-editor-panel__section">
|
||||||
|
<div className="tgu-editor-eyebrow">Параметры включения</div>
|
||||||
|
<dl className="tgu-map-request-details__grid">
|
||||||
|
<dt>Аппарат</dt>
|
||||||
|
<dd>{spacecraftLabel ?? "—"}</dd>
|
||||||
|
<dt>Виток</dt>
|
||||||
|
<dd className="mono">{work.revolution ?? "—"}</dd>
|
||||||
|
{work.kind === "shoot" && (
|
||||||
|
<>
|
||||||
|
<dt>Крен</dt>
|
||||||
|
<dd className="mono">{work.rollDeg != null ? `${work.rollDeg.toFixed(1)}°` : "—"}</dd>
|
||||||
|
<dt>Угол захвата</dt>
|
||||||
|
<dd className="mono">{work.captureAngleDeg != null ? `${work.captureAngleDeg.toFixed(1)}°` : "—"}</dd>
|
||||||
|
<dt>Центр контура</dt>
|
||||||
|
<dd className="mono">
|
||||||
|
{work.centerLat != null && work.centerLon != null
|
||||||
|
? `${work.centerLat.toFixed(2)}, ${work.centerLon.toFixed(2)}`
|
||||||
|
: "—"}
|
||||||
|
</dd>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{work.kind === "downlink" && (
|
||||||
|
<>
|
||||||
|
<dt>Станция</dt>
|
||||||
|
<dd>{stations.find((station) => station.id === work.stationId)?.name ?? work.stationId ?? "—"}</dd>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</dl>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{work.kind === "shoot" && (
|
||||||
|
<section className="tgu-editor-panel__section">
|
||||||
|
<div className="tgu-editor-eyebrow">Задания под маршрутом</div>
|
||||||
|
<div className="tgu-editor-panel__hint">Привязка заданий к маршруту появится позже.</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{work.kind === "downlink" && (
|
||||||
|
<section className="tgu-editor-panel__section">
|
||||||
|
<div className="tgu-editor-eyebrow">Сбрасываемые съёмки</div>
|
||||||
|
{work.surveyIds && work.surveyIds.length > 0 ? (
|
||||||
|
<ul className="tgu-editor-panel__list">
|
||||||
|
{work.surveyIds.map((surveyId) => (
|
||||||
|
<li key={surveyId}>{surveyModeLabel ? surveyModeLabel(surveyId) : `Маршрут #${surveyId}`}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
) : (
|
||||||
|
<div className="tgu-editor-panel__hint">Маршруты съёмки к сбросу не привязаны.</div>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
{work.kind === "shoot" && (
|
{work.kind === "shoot" && (
|
||||||
<section className="tgu-editor-panel__section">
|
<section className="tgu-editor-panel__section">
|
||||||
<div className="tgu-editor-eyebrow">Режим аппаратуры</div>
|
<div className="tgu-editor-eyebrow">Режим аппаратуры</div>
|
||||||
@@ -183,20 +241,16 @@ export function WorkInspector({
|
|||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{work.kind === "downlink" && (
|
{work.kind === "downlink" && !readOnly && (
|
||||||
<section className="tgu-editor-panel__section">
|
<section className="tgu-editor-panel__section">
|
||||||
<div className="tgu-editor-eyebrow">Станция приёма</div>
|
<div className="tgu-editor-eyebrow">Станция приёма</div>
|
||||||
{readOnly ? (
|
<select value={work.stationId ?? ""} onChange={(event) => onUpdate(work.id, { stationId: event.target.value })}>
|
||||||
<div className="tgu-editor-panel__hint">{stations.find((station) => station.id === work.stationId)?.name ?? work.stationId ?? "—"}</div>
|
{stations.map((station) => (
|
||||||
) : (
|
<option key={station.id} value={station.id}>
|
||||||
<select value={work.stationId ?? ""} onChange={(event) => onUpdate(work.id, { stationId: event.target.value })}>
|
{station.name}
|
||||||
{stations.map((station) => (
|
</option>
|
||||||
<option key={station.id} value={station.id}>
|
))}
|
||||||
{station.name}
|
</select>
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
)}
|
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -453,7 +453,12 @@ export function TguEditorTab({ selectedSpacecraftId, selectedPlan, appliedRange,
|
|||||||
endMs,
|
endMs,
|
||||||
label: mode?.label ?? "Съёмка",
|
label: mode?.label ?? "Съёмка",
|
||||||
modeId,
|
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);
|
setSelectedWorkId(id);
|
||||||
@@ -468,6 +473,17 @@ export function TguEditorTab({ selectedSpacecraftId, selectedPlan, appliedRange,
|
|||||||
[stationDtos]
|
[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 () => {
|
const addDropWork = async () => {
|
||||||
if (!selectedPlan || !selectedZone || selectedDropSurveyIds.size === 0) return;
|
if (!selectedPlan || !selectedZone || selectedDropSurveyIds.size === 0) return;
|
||||||
const numericNorad = noradId ? Number(noradId) : NaN;
|
const numericNorad = noradId ? Number(noradId) : NaN;
|
||||||
@@ -735,6 +751,8 @@ export function TguEditorTab({ selectedSpacecraftId, selectedPlan, appliedRange,
|
|||||||
work={selectedWork}
|
work={selectedWork}
|
||||||
modes={modes}
|
modes={modes}
|
||||||
stations={stations}
|
stations={stations}
|
||||||
|
spacecraftLabel={activeLabel}
|
||||||
|
surveyModeLabel={surveyModeLabel}
|
||||||
conflicts={conflicts[selectedWork.id] ?? []}
|
conflicts={conflicts[selectedWork.id] ?? []}
|
||||||
readOnly={selectedWork.origin === "plan"}
|
readOnly={selectedWork.origin === "plan"}
|
||||||
onUpdate={onChangeWork}
|
onUpdate={onChangeWork}
|
||||||
|
|||||||
@@ -88,6 +88,11 @@ describe("editorDraft", () => {
|
|||||||
startMs: parseUtc("2026-05-29T01:10:00Z"),
|
startMs: parseUtc("2026-05-29T01:10:00Z"),
|
||||||
endMs: parseUtc("2026-05-29T01:10:00Z") + 180_000,
|
endMs: parseUtc("2026-05-29T01:10:00Z") + 180_000,
|
||||||
origin: "plan",
|
origin: "plan",
|
||||||
|
planModeId: 11,
|
||||||
|
revolution: 4321,
|
||||||
|
rollDeg: 0,
|
||||||
|
centerLat: 55.7,
|
||||||
|
centerLon: 37.6,
|
||||||
targetLat: 55.7,
|
targetLat: 55.7,
|
||||||
targetLon: 37.6
|
targetLon: 37.6
|
||||||
},
|
},
|
||||||
@@ -98,7 +103,10 @@ describe("editorDraft", () => {
|
|||||||
startMs: parseUtc("2026-05-29T01:40:00Z"),
|
startMs: parseUtc("2026-05-29T01:40:00Z"),
|
||||||
endMs: parseUtc("2026-05-29T01:40:00Z") + 120_000,
|
endMs: parseUtc("2026-05-29T01:40:00Z") + 120_000,
|
||||||
origin: "plan",
|
origin: "plan",
|
||||||
stationId: "102"
|
planModeId: 22,
|
||||||
|
revolution: 4322,
|
||||||
|
stationId: "102",
|
||||||
|
surveyIds: [11]
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ export type AddShootInput = {
|
|||||||
label: string;
|
label: string;
|
||||||
modeId?: string;
|
modeId?: string;
|
||||||
target: EditorTarget;
|
target: EditorTarget;
|
||||||
|
/** Параметры построенного маршрута (если съёмка строилась с превью). */
|
||||||
|
revolution?: number;
|
||||||
|
rollDeg?: number;
|
||||||
|
captureAngleDeg?: number;
|
||||||
|
centerLat?: number;
|
||||||
|
centerLon?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AddDownlinkInput = {
|
export type AddDownlinkInput = {
|
||||||
@@ -66,6 +72,12 @@ export function planModesToWorks(modes: PlanMode[]): EditorWork[] {
|
|||||||
startMs,
|
startMs,
|
||||||
endMs,
|
endMs,
|
||||||
origin: "plan",
|
origin: "plan",
|
||||||
|
planModeId: mode.id,
|
||||||
|
revolution: mode.revolution,
|
||||||
|
rollDeg: mode.roll,
|
||||||
|
centerLat: mode.lat,
|
||||||
|
centerLon: mode.longitude,
|
||||||
|
// Центр контура — он же опорная точка для проверки видимости.
|
||||||
targetLat: mode.lat,
|
targetLat: mode.lat,
|
||||||
targetLon: mode.longitude
|
targetLon: mode.longitude
|
||||||
}];
|
}];
|
||||||
@@ -78,7 +90,10 @@ export function planModesToWorks(modes: PlanMode[]): EditorWork[] {
|
|||||||
startMs,
|
startMs,
|
||||||
endMs,
|
endMs,
|
||||||
origin: "plan",
|
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,
|
targetName: input.target.name,
|
||||||
targetLat: input.target.lat,
|
targetLat: input.target.lat,
|
||||||
targetLon: input.target.lon,
|
targetLon: input.target.lon,
|
||||||
|
revolution: input.revolution,
|
||||||
|
rollDeg: input.rollDeg,
|
||||||
|
captureAngleDeg: input.captureAngleDeg,
|
||||||
|
centerLat: input.centerLat,
|
||||||
|
centerLon: input.centerLon,
|
||||||
origin: "added",
|
origin: "added",
|
||||||
isNew: true
|
isNew: true
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,11 +20,28 @@ export type EditorShootWork = EditorWorkBase & {
|
|||||||
targetName?: string;
|
targetName?: string;
|
||||||
targetLat?: number;
|
targetLat?: number;
|
||||||
targetLon?: number;
|
targetLon?: number;
|
||||||
|
/** Идентификатор сохранённого режима съёмки в БД (есть у включений плана). */
|
||||||
|
planModeId?: number;
|
||||||
|
/** Номер витка. */
|
||||||
|
revolution?: number;
|
||||||
|
/** Крен (gamma), град. */
|
||||||
|
rollDeg?: number;
|
||||||
|
/** Угол захвата (полуширина полосы), град. Есть только у свежепостроенного маршрута. */
|
||||||
|
captureAngleDeg?: number;
|
||||||
|
/** Центр контура съёмки, град. */
|
||||||
|
centerLat?: number;
|
||||||
|
centerLon?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EditorDownlinkWork = EditorWorkBase & {
|
export type EditorDownlinkWork = EditorWorkBase & {
|
||||||
kind: "downlink";
|
kind: "downlink";
|
||||||
stationId?: string;
|
stationId?: string;
|
||||||
|
/** Идентификатор сохранённого режима сброса в БД (есть у включений плана). */
|
||||||
|
planModeId?: number;
|
||||||
|
/** Номер витка зоны радиовидимости. */
|
||||||
|
revolution?: number;
|
||||||
|
/** Идентификаторы режимов съёмки (survey_modes), сбрасываемых в этом сеансе. */
|
||||||
|
surveyIds?: number[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EditorWork = EditorShootWork | EditorDownlinkWork;
|
export type EditorWork = EditorShootWork | EditorDownlinkWork;
|
||||||
|
|||||||
@@ -47,6 +47,15 @@
|
|||||||
font-size: 0.68rem;
|
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 {
|
.tgu-editor-toolbar__group {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.35rem;
|
gap: 0.35rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user