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.
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import type { TguPlanUi, TguPlatformUi, TimelineRange } from "../../model/timelineTypes";
|
||||
import { platformLabel } from "../tgu-planning/tguTimelineMapper";
|
||||
import { DEFAULT_TGU_2D_MAP_LAYERS, type Tgu2DMapLayerKey, type Tgu2DMapLayersState } from "./model/mapLayerTypes";
|
||||
import type { Tgu2DMapSelection } from "./model/mapSelectionTypes";
|
||||
import { buildTgu2DMapScene, getLayerAvailability } from "./Tgu2DMapLayers";
|
||||
import { Tgu2DMapDetails } from "./Tgu2DMapDetails";
|
||||
import { Tgu2DMapSidebar } from "./Tgu2DMapSidebar";
|
||||
import { Tgu2DMapToolbar } from "./Tgu2DMapToolbar";
|
||||
import { Tgu2DMapView } from "./Tgu2DMapView";
|
||||
import { getTgu2DMapIntervalState, TGU_2D_MAP_INTERVAL_WARNING } from "./tgu2DMapInterval";
|
||||
|
||||
type Tgu2DMapTabProps = {
|
||||
range: TimelineRange;
|
||||
invalidRange: boolean;
|
||||
selectedSpacecraftId?: string;
|
||||
selectedPlan?: TguPlanUi;
|
||||
platforms: TguPlatformUi[];
|
||||
onSelectSpacecraft: (spacecraftId?: string) => void;
|
||||
};
|
||||
|
||||
export function Tgu2DMapTab({
|
||||
range,
|
||||
invalidRange,
|
||||
selectedSpacecraftId,
|
||||
selectedPlan,
|
||||
platforms,
|
||||
onSelectSpacecraft
|
||||
}: Tgu2DMapTabProps) {
|
||||
const [layers, setLayers] = useState<Tgu2DMapLayersState>(DEFAULT_TGU_2D_MAP_LAYERS);
|
||||
const [search, setSearch] = useState("");
|
||||
const [redrawToken, setRedrawToken] = useState(0);
|
||||
const [selectedObject, setSelectedObject] = useState<Tgu2DMapSelection>();
|
||||
const satelliteId = selectedPlan?.spacecraftId ?? selectedSpacecraftId;
|
||||
const mapInterval = getTgu2DMapIntervalState(range, selectedPlan);
|
||||
const mapInvalidRange = invalidRange || mapInterval.tooLarge;
|
||||
const platform = useMemo(
|
||||
() => platforms.find((item) => item.spacecraftId === satelliteId),
|
||||
[platforms, satelliteId]
|
||||
);
|
||||
const satelliteLabel = satelliteId ? platformLabel(platform, satelliteId) : "КА не выбран";
|
||||
const intervalLabel = `${formatDate(mapInterval.range.fromMs)} - ${formatDate(mapInterval.range.toMs)}`;
|
||||
const scene = useMemo(
|
||||
() => buildTgu2DMapScene({ range: mapInterval.range, selectedPlan, selectedSpacecraftId: satelliteId, platforms }),
|
||||
[mapInterval.range, platforms, satelliteId, selectedPlan]
|
||||
);
|
||||
const layerAvailability = useMemo(() => getLayerAvailability(layers, scene), [layers, scene]);
|
||||
const overlayMessage = !satelliteId
|
||||
? "Выберите КА в sidebar или план на timeline."
|
||||
: invalidRange
|
||||
? "Некорректный интервал: from должен быть меньше to."
|
||||
: mapInterval.tooLarge
|
||||
? TGU_2D_MAP_INTERVAL_WARNING
|
||||
: undefined;
|
||||
|
||||
const toggleLayer = (key: Tgu2DMapLayerKey) => {
|
||||
setLayers((current) => ({ ...current, [key]: !current[key] }));
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="tgu-map-workspace">
|
||||
<Tgu2DMapToolbar
|
||||
satelliteLabel={satelliteLabel}
|
||||
intervalLabel={intervalLabel}
|
||||
layers={layers}
|
||||
onRefresh={() => setRedrawToken((value) => value + 1)}
|
||||
onToggleLayer={toggleLayer}
|
||||
/>
|
||||
|
||||
<div className="tgu-map-content">
|
||||
<Tgu2DMapSidebar
|
||||
platforms={platforms}
|
||||
selectedSpacecraftId={satelliteId}
|
||||
selectedPlanId={selectedPlan?.planId}
|
||||
layers={layers}
|
||||
search={search}
|
||||
layerAvailability={layerAvailability}
|
||||
onSearchChange={setSearch}
|
||||
onSelectSpacecraft={onSelectSpacecraft}
|
||||
onToggleLayer={toggleLayer}
|
||||
/>
|
||||
<section className="tgu-map-stage">
|
||||
{overlayMessage && <div className="tgu-map-overlay">{overlayMessage}</div>}
|
||||
<Tgu2DMapView
|
||||
scene={mapInvalidRange ? { polygons: [], lines: [], markers: [] } : scene}
|
||||
layers={layers}
|
||||
redrawToken={redrawToken}
|
||||
onObjectSelect={setSelectedObject}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div className="tgu-map-errors">
|
||||
{Object.entries(layerAvailability).map(([layer, message]) =>
|
||||
message ? (
|
||||
<div className="tgu-alert tgu-alert--warning" key={layer}>
|
||||
{layerLabel(layer as Tgu2DMapLayerKey)}: {message}
|
||||
</div>
|
||||
) : null
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Tgu2DMapDetails selectedPlan={selectedPlan} selectedObject={selectedObject} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
function formatDate(ms: number): string {
|
||||
return new Date(ms).toLocaleString("ru-RU", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit"
|
||||
});
|
||||
}
|
||||
|
||||
function layerLabel(layer: Tgu2DMapLayerKey): string {
|
||||
switch (layer) {
|
||||
case "tracks":
|
||||
return "Трасса КА";
|
||||
case "swath":
|
||||
return "Полоса обзора";
|
||||
case "planWorks":
|
||||
return "Работы плана";
|
||||
case "stations":
|
||||
return "Станции";
|
||||
case "spacecraftMarkers":
|
||||
return "Маркеры КА";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user