Files
dc-observatio/services/pcp-tgu-ops-ui/src/features/tgu-editor/editorPresentation.ts
T
Дмитрий Соловьев 2499aebf2d routes: объём маршрута (volumeMb) — расчёт, хранение в БД и отображение
Объём режима = скорость × длительность, Мбайт: съёмка 120 Мбайт/с (12 ГБ / 100 с), сброс — канал 1200 Мбит/с ÷ 8. Единый источник — ModeDataRates в pcp-types-lib. Хранится колонкой volume_mb (Flyway V10 mission-planing, V9 complex-mission, бэкфилл из duration), заполняется на всех путях построения через @PrePersist/@PreUpdate. DTO отдают вычисляемое read-only поле (та же формула). Фронт pcp-tgu-ops-ui показывает объём в редакторе (инспектор, построенный маршрут) и в сводке комплексного плана.
2026-06-06 18:34:29 +03:00

114 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { EditorWorkKind } from "./model/editorTypes";
// Объём данных — общий форматтер (используется и в комплексном плане).
export { formatVolume } from "../../utils/dataVolume";
export const EDITOR_MONTHS = ["янв", "фев", "мар", "апр", "май", "июн", "июл", "авг", "сен", "окт", "ноя", "дек"];
export const DEFAULT_EDITOR_MODES = [
{ id: "pan", label: "Панхром." },
{ id: "ms", label: "Мультиспектр." },
{ id: "stereo", label: "Стерео" },
{ id: "scan", label: "Скан" }
];
export function kindLabel(kind: EditorWorkKind): string {
switch (kind) {
case "shoot":
return "Съёмка";
case "downlink":
return "Сброс";
}
}
export function kindColor(kind: EditorWorkKind): string {
return kind === "shoot" ? "var(--t-optical)" : "var(--accent)";
}
export function formatTime(ms: number): string {
const date = new Date(ms);
return `${pad(date.getHours())}:${pad(date.getMinutes())}`;
}
/**
* Подпись деления оси с учётом шага: при шаге меньше минуты показываем секунды
* (ЧЧ:ММ:СС), иначе — ЧЧ:ММ. Так при сильном зуме видно более мелкий масштаб времени.
*/
export function formatTick(ms: number, stepMs: number): string {
const date = new Date(ms);
if (stepMs < 60_000) {
return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
}
return `${pad(date.getHours())}:${pad(date.getMinutes())}`;
}
export function formatDate(ms: number): string {
const date = new Date(ms);
return `${pad(date.getDate())} ${EDITOR_MONTHS[date.getMonth()]}`;
}
export function formatDateTime(ms: number): string {
return `${formatDate(ms)} ${formatTime(ms)}`;
}
/** Время с секундами и одной десятой долей: ЧЧ:ММ:СС.с (локальное). */
export function formatTimeSeconds(ms: number): string {
const date = new Date(ms);
const tenths = Math.floor((((ms % 1000) + 1000) % 1000) / 100);
return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}.${tenths}`;
}
/** Дата и время с десятыми долями секунды: «дд мес ЧЧ:ММ:СС.с» (локальное). */
export function formatDateTimeSeconds(ms: number): string {
return `${formatDate(ms)} ${formatTimeSeconds(ms)}`;
}
export function formatDuration(ms: number): string {
const totalSeconds = Math.max(0, Math.round(ms / 1000));
if (totalSeconds < 60) {
return `${totalSeconds} с`;
}
if (totalSeconds < 3600) {
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
return seconds > 0 ? `${minutes} мин ${seconds} с` : `${minutes} мин`;
}
const hours = ms / 3_600_000;
if (hours < 24) {
return `${Number.isInteger(hours) ? hours : hours.toFixed(1)} ч`;
}
const days = Math.floor(hours / 24);
const restHours = Math.round(hours - days * 24);
return restHours > 0 ? `${days} сут ${restHours} ч` : `${days} сут`;
}
/** Деления оси времени: {@link ticks} — отметки, {@link step} — выбранный шаг (мс). */
export type TimelineTicks = { ticks: number[]; step: number };
export function niceTicks(fromMs: number, toMs: number, pxPerMs: number, minPx: number): TimelineTicks {
const minuteMs = 60_000;
const hourMs = 3_600_000;
const dayMs = 24 * hourMs;
// От секунд до недели — чем сильнее зум, тем мельче шаг, который влезает по minPx.
const steps = [
1_000, 2_000, 5_000, 10_000, 15_000, 30_000,
minuteMs, 2 * minuteMs, 5 * minuteMs, 10 * minuteMs, 15 * minuteMs, 30 * minuteMs,
hourMs, 2 * hourMs, 3 * hourMs, 6 * hourMs, 12 * hourMs, dayMs, 2 * dayMs, 7 * dayMs
];
const step = steps.find((item) => item * pxPerMs >= minPx) ?? steps[steps.length - 1];
const ticks: number[] = [];
const firstTick = Math.ceil(fromMs / step) * step;
for (let timeMs = firstTick; timeMs <= toMs; timeMs += step) {
ticks.push(timeMs);
}
return { ticks, step };
}
function pad(value: number): string {
return String(value).padStart(2, "0");
}