feat: introduce device plugin runtime boundary
This commit is contained in:
@@ -1,39 +1,38 @@
|
||||
import type { StatusTone } from "@nodedc/ui-react";
|
||||
|
||||
import type { K1Metrics } from "./api";
|
||||
import type { BackendStatus } from "./useK1Console";
|
||||
import type {
|
||||
BackendStatus,
|
||||
RuntimePhase,
|
||||
SourceMode,
|
||||
StreamMetrics,
|
||||
} from "./core/runtime/contracts";
|
||||
|
||||
const phaseLabels: Record<string, string> = {
|
||||
const phaseLabels: Record<RuntimePhase, string> = {
|
||||
unconfigured: "Модель не выбрана",
|
||||
idle: "Ожидание",
|
||||
scanning: "Поиск Bluetooth",
|
||||
device_selected: "Устройство выбрано",
|
||||
provisioning: "Передача настроек Wi‑Fi",
|
||||
connecting: "Подключение",
|
||||
configuring: "Настройка устройства",
|
||||
connected: "Устройство подключено",
|
||||
starting_live: "Запуск потока",
|
||||
live: "Поток в реальном времени",
|
||||
replay: "Повтор записи",
|
||||
starting: "Запуск потока",
|
||||
streaming: "Поток в реальном времени",
|
||||
replaying: "Повтор записи",
|
||||
stopping: "Остановка",
|
||||
error: "Ошибка",
|
||||
};
|
||||
|
||||
export function phaseLabel(phase: string | null | undefined): string {
|
||||
if (!phase) return "Нет состояния";
|
||||
return phaseLabels[phase] ?? "Неизвестное состояние";
|
||||
export function phaseLabel(phase: RuntimePhase | null | undefined): string {
|
||||
return phase ? phaseLabels[phase] : "Нет состояния";
|
||||
}
|
||||
|
||||
export function phaseTone(phase: string | null | undefined): StatusTone {
|
||||
if (!phase) return "neutral";
|
||||
export function phaseTone(phase: RuntimePhase | null | undefined): StatusTone {
|
||||
if (!phase || phase === "unconfigured" || phase === "idle") return "neutral";
|
||||
if (phase === "error") return "danger";
|
||||
if (["connected", "live", "replay"].includes(phase)) return "success";
|
||||
if (["scanning", "provisioning", "connecting", "starting_live", "stopping"].includes(phase)) {
|
||||
return "accent";
|
||||
}
|
||||
return "neutral";
|
||||
if (["connected", "streaming", "replaying"].includes(phase)) return "success";
|
||||
return "accent";
|
||||
}
|
||||
|
||||
export function backendLabel(status: BackendStatus): string {
|
||||
return {
|
||||
unconfigured: "Модель не выбрана",
|
||||
checking: "Проверка контура",
|
||||
online: "Контур доступен",
|
||||
degraded: "Контур ограничен",
|
||||
@@ -44,33 +43,16 @@ export function backendLabel(status: BackendStatus): string {
|
||||
export function backendTone(status: BackendStatus): StatusTone {
|
||||
if (status === "online") return "success";
|
||||
if (status === "degraded" || status === "checking") return "warning";
|
||||
if (status === "unconfigured") return "neutral";
|
||||
return "danger";
|
||||
}
|
||||
|
||||
export function eventStatusLabel(status: string): string {
|
||||
return {
|
||||
connecting: "подключение",
|
||||
open: "подключён",
|
||||
closed: "закрыт",
|
||||
error: "ошибка",
|
||||
}[status] ?? "неизвестно";
|
||||
}
|
||||
|
||||
export function finiteMetric(value: number | null | undefined): number | null {
|
||||
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
||||
}
|
||||
|
||||
export function pipelineLatency(metrics: K1Metrics | undefined): number | null {
|
||||
if (!metrics) return null;
|
||||
const direct = finiteMetric(metrics.pipeline_ms ?? metrics.end_to_end_ms);
|
||||
if (direct !== null) return direct;
|
||||
|
||||
const segments = [
|
||||
finiteMetric(metrics.mqtt_to_decode_ms),
|
||||
finiteMetric(metrics.publish_ms),
|
||||
].filter((value): value is number => value !== null);
|
||||
|
||||
return segments.length === 2 ? segments.reduce((total, value) => total + value, 0) : null;
|
||||
export function pipelineLatency(metrics: StreamMetrics | undefined): number | null {
|
||||
return finiteMetric(metrics?.latencyMs);
|
||||
}
|
||||
|
||||
export function formatNumber(value: number | null, digits = 1): string {
|
||||
@@ -81,7 +63,7 @@ export function formatNumber(value: number | null, digits = 1): string {
|
||||
});
|
||||
}
|
||||
|
||||
export function sourceModeLabel(mode: string | null | undefined): string {
|
||||
export function sourceModeLabel(mode: SourceMode | null | undefined): string {
|
||||
if (mode === "live") return "Реальное время";
|
||||
if (mode === "replay") return "Повтор записи";
|
||||
if (mode === "idle") return "Ожидание";
|
||||
|
||||
Reference in New Issue
Block a user