pcp-tgu-ops-ui: показывать только КА со статусом OPERATIONAL

С остальными КА мы не работаем, поэтому отсекаем их сразу на входе в
loadData: фильтруем платформы по статусу и оставляем планы только для
эксплуатируемых КА (по norad-id), иначе неoperational-аппарат вернулся бы
fallback-строкой по плану в buildPlatformsForInterval.
This commit is contained in:
Дмитрий Соловьев
2026-06-02 15:00:30 +03:00
parent e738c236a3
commit 061ac1390c
3 changed files with 63 additions and 4 deletions
@@ -10,7 +10,13 @@ import { TguPlanningLayout } from "./TguPlanningLayout";
import { TguSidebar } from "./TguSidebar"; import { TguSidebar } from "./TguSidebar";
import { TguTimeline } from "./TguTimeline"; import { TguTimeline } from "./TguTimeline";
import { TguToolbar } from "./TguToolbar"; import { TguToolbar } from "./TguToolbar";
import { buildPlatformsForInterval, buildTimelineRows, mapPlans } from "./tguTimelineMapper"; import {
buildPlatformsForInterval,
buildTimelineRows,
isOperationalPlatform,
mapPlans,
operationalSpacecraftIds
} from "./tguTimelineMapper";
const DAY_MS = 24 * 60 * 60 * 1000; const DAY_MS = 24 * 60 * 60 * 1000;
@@ -61,19 +67,24 @@ export function TguPlanningPage() {
fetchPlans(historyDays) fetchPlans(historyDays)
]); ]);
// На морде работаем только с эксплуатируемыми КА — остальные отсекаем сразу
// на входе, чтобы они не всплывали ни в списке, ни как fallback-строки по планам.
const operationalPlatforms =
platformResult.status === "fulfilled" ? platformResult.value.filter(isOperationalPlatform) : [];
if (platformResult.status === "rejected") { if (platformResult.status === "rejected") {
setPlatformLoadFailed(true); setPlatformLoadFailed(true);
} else { } else {
setPlatformLoadFailed(false); setPlatformLoadFailed(false);
setRawPlatforms(platformResult.value); setRawPlatforms(operationalPlatforms);
} }
if (planResult.status === "rejected") { if (planResult.status === "rejected") {
setPlans([]); setPlans([]);
setPageError(planResult.reason instanceof Error ? planResult.reason.message : "Не удалось загрузить планы."); setPageError(planResult.reason instanceof Error ? planResult.reason.message : "Не удалось загрузить планы.");
} else { } else {
const platforms = platformResult.status === "fulfilled" ? platformResult.value : []; const operationalIds = operationalSpacecraftIds(operationalPlatforms);
setPlans(mapPlans(planResult.value, platforms)); setPlans(mapPlans(planResult.value, operationalPlatforms).filter((plan) => operationalIds.has(plan.spacecraftId)));
} }
setLoading(false); setLoading(false);
@@ -0,0 +1,30 @@
import { describe, expect, it } from "vitest";
import type { TguPlatform } from "../../model/tguTypes";
import { isOperationalPlatform, operationalSpacecraftIds } from "./tguTimelineMapper";
function platform(overrides: Partial<TguPlatform>): TguPlatform {
return { id: "x", noradId: 1, status: "OPERATIONAL", ...overrides };
}
describe("isOperationalPlatform", () => {
it("принимает только статус OPERATIONAL без учёта регистра и пробелов", () => {
expect(isOperationalPlatform(platform({ status: "OPERATIONAL" }))).toBe(true);
expect(isOperationalPlatform(platform({ status: " operational " }))).toBe(true);
expect(isOperationalPlatform(platform({ status: "STANDBY" }))).toBe(false);
expect(isOperationalPlatform(platform({ status: "LOST" }))).toBe(false);
expect(isOperationalPlatform(platform({ status: null }))).toBe(false);
expect(isOperationalPlatform(platform({ status: undefined }))).toBe(false);
});
});
describe("operationalSpacecraftIds", () => {
it("собирает norad-id в виде строк и пропускает платформы без norad-id", () => {
const ids = operationalSpacecraftIds([
platform({ noradId: 2 }),
platform({ noradId: 30 }),
platform({ noradId: null }),
platform({ noradId: undefined })
]);
expect(ids).toEqual(new Set(["2", "30"]));
});
});
@@ -3,6 +3,24 @@ import type { TguPlanUi, TguPlatformUi, TimelineRange, TimelineRow } from "../..
const PROBLEM_STATUSES = new Set(["REJECTED", "START_AMBIGUOUS"]); const PROBLEM_STATUSES = new Set(["REJECTED", "START_AMBIGUOUS"]);
const OPERATIONAL_STATUS = "OPERATIONAL";
/** На морде показываем только эксплуатируемые КА — с остальными мы не работаем. */
export function isOperationalPlatform(platform: TguPlatform): boolean {
return (platform.status ?? "").trim().toUpperCase() === OPERATIONAL_STATUS;
}
/** norad-id эксплуатируемых КА; planId.spacecraftId сопоставляется именно с norad-id. */
export function operationalSpacecraftIds(platforms: TguPlatform[]): Set<string> {
const ids = new Set<string>();
for (const platform of platforms) {
if (platform.noradId !== null && platform.noradId !== undefined) {
ids.add(String(platform.noradId));
}
}
return ids;
}
export function mapPlans(plans: TguPlan[], platforms: TguPlatform[]): TguPlanUi[] { export function mapPlans(plans: TguPlan[], platforms: TguPlatform[]): TguPlanUi[] {
const platformByNorad = buildPlatformByNorad(platforms); const platformByNorad = buildPlatformByNorad(platforms);