fix(ui): align telemetry and laboratory navigation
This commit is contained in:
@@ -619,7 +619,7 @@ export default function App() {
|
||||
refreshRuntime: runtime.refresh,
|
||||
saveWorkspaceLayout,
|
||||
workspaceLayoutSaving: workspaceLayoutProfile.state === "saving",
|
||||
systemUtilityAction: computeContourSettings.utilityAction,
|
||||
systemUtilityActions: computeContourSettings.utilityActions,
|
||||
});
|
||||
|
||||
const header = (
|
||||
|
||||
@@ -2,6 +2,7 @@ interface TelemetrySeriesProps {
|
||||
label: string;
|
||||
values: Array<number | null>;
|
||||
value: string;
|
||||
resource?: string | null;
|
||||
ceiling?: number;
|
||||
}
|
||||
|
||||
@@ -24,13 +25,17 @@ export function TelemetrySeries({
|
||||
label,
|
||||
values,
|
||||
value,
|
||||
resource,
|
||||
ceiling,
|
||||
}: TelemetrySeriesProps) {
|
||||
const points = linePoints(values, ceiling);
|
||||
return (
|
||||
<div className="system-telemetry-series">
|
||||
<div>
|
||||
<span>{label}</span>
|
||||
<span className="system-telemetry-series__label">
|
||||
<span>{label}</span>
|
||||
{resource ? <small>{resource}</small> : null}
|
||||
</span>
|
||||
<strong>{value}</strong>
|
||||
</div>
|
||||
<svg
|
||||
|
||||
@@ -8,7 +8,7 @@ interface ComputeContourSettingsController {
|
||||
canEdit: boolean;
|
||||
openCreate: () => void;
|
||||
openEdit: () => void;
|
||||
utilityAction: ApplicationPanelUtilityAction;
|
||||
utilityActions: readonly ApplicationPanelUtilityAction[];
|
||||
window: ReactNode;
|
||||
}
|
||||
|
||||
@@ -24,18 +24,30 @@ export function useComputeContourSettings(): ComputeContourSettingsController {
|
||||
setMode("edit");
|
||||
setOpen(true);
|
||||
}, []);
|
||||
const utilityAction = useMemo<ApplicationPanelUtilityAction>(() => ({
|
||||
label: "Настроить выбранный вычислительный контур",
|
||||
icon: "settings",
|
||||
disabled: contours.selectedContour === null,
|
||||
onClick: openEdit,
|
||||
}), [contours.selectedContour, openEdit]);
|
||||
const utilityActions = useMemo<readonly ApplicationPanelUtilityAction[]>(() => [
|
||||
{
|
||||
label: "Обновить телеметрию выбранного контура",
|
||||
icon: "refresh",
|
||||
disabled: contours.selectedContour?.contour_id !== "worker-006",
|
||||
onClick: contours.refreshTelemetry,
|
||||
},
|
||||
{
|
||||
label: "Настроить выбранный вычислительный контур",
|
||||
icon: "settings",
|
||||
disabled: contours.selectedContour === null,
|
||||
onClick: openEdit,
|
||||
},
|
||||
], [
|
||||
contours.refreshTelemetry,
|
||||
contours.selectedContour,
|
||||
openEdit,
|
||||
]);
|
||||
|
||||
return {
|
||||
canEdit: contours.selectedContour !== null,
|
||||
openCreate,
|
||||
openEdit,
|
||||
utilityAction,
|
||||
utilityActions,
|
||||
window: (
|
||||
<ComputeContourSettingsWindow
|
||||
open={open}
|
||||
|
||||
@@ -8,7 +8,7 @@ interface ApplicationPanelActionsOptions {
|
||||
refreshRuntime: () => void;
|
||||
saveWorkspaceLayout: () => Promise<void>;
|
||||
workspaceLayoutSaving: boolean;
|
||||
systemUtilityAction: ApplicationPanelUtilityAction;
|
||||
systemUtilityActions: readonly ApplicationPanelUtilityAction[];
|
||||
}
|
||||
|
||||
export function useApplicationPanelActions({
|
||||
@@ -16,7 +16,7 @@ export function useApplicationPanelActions({
|
||||
refreshRuntime,
|
||||
saveWorkspaceLayout,
|
||||
workspaceLayoutSaving,
|
||||
systemUtilityAction,
|
||||
systemUtilityActions,
|
||||
}: ApplicationPanelActionsOptions): ApplicationPanelUtilityAction[] {
|
||||
return useMemo(() => {
|
||||
const actions: ApplicationPanelUtilityAction[] = [];
|
||||
@@ -35,13 +35,13 @@ export function useApplicationPanelActions({
|
||||
onClick: () => void saveWorkspaceLayout(),
|
||||
});
|
||||
}
|
||||
if (definition?.root === "system") actions.push(systemUtilityAction);
|
||||
if (definition?.root === "system") actions.push(...systemUtilityActions);
|
||||
return actions;
|
||||
}, [
|
||||
definition,
|
||||
refreshRuntime,
|
||||
saveWorkspaceLayout,
|
||||
systemUtilityAction,
|
||||
systemUtilityActions,
|
||||
workspaceLayoutSaving,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ interface ComputeContourContextValue {
|
||||
selectedContour: ComputeContour | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
telemetryRefreshGeneration: number;
|
||||
selectContour: (contourId: string) => void;
|
||||
createContour: (draft: ComputeContourDraft) => Promise<ComputeContour>;
|
||||
updateContour: (
|
||||
@@ -30,6 +31,7 @@ interface ComputeContourContextValue {
|
||||
draft: ComputeContourDraft,
|
||||
) => Promise<ComputeContour>;
|
||||
refresh: () => void;
|
||||
refreshTelemetry: () => void;
|
||||
}
|
||||
|
||||
const ComputeContourContext = createContext<ComputeContourContextValue | null>(null);
|
||||
@@ -42,7 +44,12 @@ export function ComputeContourProvider({ children }: { children: ReactNode }) {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [generation, setGeneration] = useState(0);
|
||||
const [telemetryRefreshGeneration, setTelemetryRefreshGeneration] = useState(0);
|
||||
const refresh = useCallback(() => setGeneration((value) => value + 1), []);
|
||||
const refreshTelemetry = useCallback(
|
||||
() => setTelemetryRefreshGeneration((value) => value + 1),
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
@@ -103,19 +110,23 @@ export function ComputeContourProvider({ children }: { children: ReactNode }) {
|
||||
selectedContour,
|
||||
loading,
|
||||
error,
|
||||
telemetryRefreshGeneration,
|
||||
selectContour,
|
||||
createContour,
|
||||
updateContour,
|
||||
refresh,
|
||||
refreshTelemetry,
|
||||
}), [
|
||||
contours,
|
||||
selectedContour,
|
||||
loading,
|
||||
error,
|
||||
telemetryRefreshGeneration,
|
||||
selectContour,
|
||||
createContour,
|
||||
updateContour,
|
||||
refresh,
|
||||
refreshTelemetry,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface WorkerTelemetryState {
|
||||
export function useWorkerTelemetry(
|
||||
pollMilliseconds = DEFAULT_WORKER_TELEMETRY_POLL_MILLISECONDS,
|
||||
enabled = true,
|
||||
externalRefreshGeneration = 0,
|
||||
): WorkerTelemetryState {
|
||||
const normalizedPollMilliseconds = normalizeWorkerTelemetryPollMilliseconds(
|
||||
pollMilliseconds,
|
||||
@@ -51,7 +52,7 @@ export function useWorkerTelemetry(
|
||||
if (!controller.signal.aborted) setLoading(false);
|
||||
});
|
||||
return () => controller.abort();
|
||||
}, [enabled, generation]);
|
||||
}, [enabled, externalRefreshGeneration, generation]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled || loading) return;
|
||||
|
||||
@@ -338,10 +338,10 @@
|
||||
.scene-operation-status-stack {
|
||||
position: absolute;
|
||||
z-index: 11;
|
||||
right: 0.85rem;
|
||||
left: 0.85rem;
|
||||
bottom: 7.15rem;
|
||||
display: grid;
|
||||
justify-items: end;
|
||||
justify-items: start;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
|
||||
.system-telemetry-series > div {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
@@ -153,6 +153,22 @@
|
||||
font-size: 0.62rem;
|
||||
}
|
||||
|
||||
.system-telemetry-series__label {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
gap: 0.16rem;
|
||||
}
|
||||
|
||||
.system-telemetry-series__label small {
|
||||
overflow: hidden;
|
||||
max-width: 11rem;
|
||||
color: var(--nodedc-text-secondary);
|
||||
font-size: 0.54rem;
|
||||
line-height: 1.15;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.system-telemetry-series strong,
|
||||
.network-stat-card strong {
|
||||
color: var(--nodedc-text-primary);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
type ComponentType,
|
||||
} from "react";
|
||||
@@ -71,6 +72,11 @@ const EMPTY_ADVANCED_RESULTS: AdvancedLaboratoryResults = {
|
||||
e37: null,
|
||||
};
|
||||
|
||||
function laboratoryWorkOrdinal(value: string): number {
|
||||
const match = value.match(/\bE(\d+)\b/i);
|
||||
return match ? Number(match[1]) : -1;
|
||||
}
|
||||
|
||||
function digestFromContentId(value: string | null | undefined): string | null {
|
||||
const digest = value?.split("-").at(-1) ?? "";
|
||||
return /^[a-f0-9]{64}$/.test(digest) ? digest : null;
|
||||
@@ -559,6 +565,7 @@ function PublishedLaboratoryResult({
|
||||
export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
|
||||
const [profileId, setProfileId] = useState<LaboratoryProfileId>("sensor-fusion");
|
||||
const [workId, setWorkId] = useState<LaboratoryWorkId>("e28-local-surface");
|
||||
const initialWorkSelectedRef = useRef(false);
|
||||
const [e28Model, setE28Model] = useState<LidarLocalSurfaceModel | null>(null);
|
||||
const [e29Result, setE29Result] = useState<E29EvidenceResult | null>(null);
|
||||
const [e30Result, setE30Result] = useState<E30ReviewResult | null>(null);
|
||||
@@ -580,7 +587,12 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
|
||||
&& session.status === "ready"
|
||||
&& session.replayable
|
||||
&& session.modalities.includes("point-cloud")
|
||||
)),
|
||||
)).sort((left, right) => {
|
||||
const ordinalDelta = laboratoryWorkOrdinal(right.lab?.labId ?? "")
|
||||
- laboratoryWorkOrdinal(left.lab?.labId ?? "");
|
||||
if (ordinalDelta !== 0) return ordinalDelta;
|
||||
return (right.startedAtUtc ?? "").localeCompare(left.startedAtUtc ?? "");
|
||||
}),
|
||||
[sessions.items],
|
||||
);
|
||||
const sourceSessions = useMemo(
|
||||
@@ -661,7 +673,9 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
|
||||
advancedResults,
|
||||
sourceSessions,
|
||||
));
|
||||
return items;
|
||||
return items.sort(
|
||||
(left, right) => laboratoryWorkOrdinal(right.label) - laboratoryWorkOrdinal(left.label),
|
||||
);
|
||||
}, [
|
||||
advancedResults,
|
||||
e28Model,
|
||||
@@ -717,13 +731,26 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
|
||||
setProfileId(firstProfile.id);
|
||||
if (firstProfile.id === "sensor-fusion") {
|
||||
const firstWork = sensorWorks[0];
|
||||
if (firstWork) setWorkId(firstWork.id);
|
||||
if (firstWork) {
|
||||
setWorkId(firstWork.id);
|
||||
initialWorkSelectedRef.current = true;
|
||||
}
|
||||
} else {
|
||||
const first = publishedWorks[0];
|
||||
if (first) setWorkId(`session:${first.id}`);
|
||||
if (first) {
|
||||
setWorkId(`session:${first.id}`);
|
||||
initialWorkSelectedRef.current = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!initialWorkSelectedRef.current) {
|
||||
const freshestWork = workOptions[0];
|
||||
if (!freshestWork) return;
|
||||
setWorkId(freshestWork.id);
|
||||
initialWorkSelectedRef.current = true;
|
||||
return;
|
||||
}
|
||||
if (!workOptions.some((work) => work.id === workId)) {
|
||||
const firstWork = workOptions[0];
|
||||
if (firstWork) setWorkId(firstWork.id);
|
||||
@@ -741,6 +768,7 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
|
||||
|
||||
const selectProfile = (next: LaboratoryProfileId) => {
|
||||
setProfileId(next);
|
||||
initialWorkSelectedRef.current = true;
|
||||
if (next === "sensor-fusion") {
|
||||
const first = sensorWorks[0];
|
||||
if (first) setWorkId(first.id);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import {
|
||||
Button,
|
||||
GlassSurface,
|
||||
Icon,
|
||||
StatusBadge,
|
||||
} from "@nodedc/ui-react";
|
||||
|
||||
@@ -24,14 +22,53 @@ function pipelineStateLabel(state: string): string {
|
||||
return "Нет live-состояния";
|
||||
}
|
||||
|
||||
function formatResourcePair(
|
||||
used: number | null | undefined,
|
||||
total: number | null | undefined,
|
||||
): string | null {
|
||||
if (
|
||||
typeof used !== "number"
|
||||
|| !Number.isFinite(used)
|
||||
|| typeof total !== "number"
|
||||
|| !Number.isFinite(total)
|
||||
|| total <= 0
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return `${formatBytes(used)} / ${formatBytes(total)}`;
|
||||
}
|
||||
|
||||
function formatCpuCapacity(
|
||||
percent: number | null | undefined,
|
||||
logicalProcessors: number | null | undefined,
|
||||
): string | null {
|
||||
if (
|
||||
typeof percent !== "number"
|
||||
|| !Number.isFinite(percent)
|
||||
|| typeof logicalProcessors !== "number"
|
||||
|| !Number.isFinite(logicalProcessors)
|
||||
|| logicalProcessors <= 0
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const used = new Intl.NumberFormat("ru-RU", {
|
||||
maximumFractionDigits: 1,
|
||||
}).format(percent / 100 * logicalProcessors);
|
||||
return `≈ ${used} / ${logicalProcessors} лог. ядра`;
|
||||
}
|
||||
|
||||
export function ComputeModulesWorkspace() {
|
||||
const { selectedContour } = useComputeContours();
|
||||
const {
|
||||
selectedContour,
|
||||
telemetryRefreshGeneration,
|
||||
} = useComputeContours();
|
||||
const legacyDiagnostic = selectedContour?.contour_id === "worker-006"
|
||||
&& selectedContour.telemetry_mode === "legacy-ssh";
|
||||
const supportsLiveTelemetry = selectedContour?.contour_id === "worker-006";
|
||||
const { telemetry, error, refresh } = useWorkerTelemetry(
|
||||
const { telemetry, error } = useWorkerTelemetry(
|
||||
(selectedContour?.telemetry_poll_interval_seconds ?? 3) * 1_000,
|
||||
supportsLiveTelemetry,
|
||||
telemetryRefreshGeneration,
|
||||
);
|
||||
const node = telemetry?.node ?? null;
|
||||
const missionCoreRuntimes = telemetry?.runtimes.filter((runtime) => !runtime.external) ?? [];
|
||||
@@ -45,6 +82,12 @@ export function ComputeModulesWorkspace() {
|
||||
const memoryPercent = node?.memory.used_percent;
|
||||
const gpuPercent = node?.gpu?.utilization_percent;
|
||||
const gpuMemoryPercent = node?.gpu?.memory_used_percent;
|
||||
const gpuMemoryUsedBytes = typeof node?.gpu?.memory_used_mib === "number"
|
||||
? node.gpu.memory_used_mib * 1024 * 1024
|
||||
: null;
|
||||
const gpuMemoryTotalBytes = typeof node?.gpu?.memory_total_mib === "number"
|
||||
? node.gpu.memory_total_mib * 1024 * 1024
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className="system-workspace compute-modules-workspace">
|
||||
@@ -63,15 +106,6 @@ export function ComputeModulesWorkspace() {
|
||||
? agentTelemetry ? "Агент доступен" : "SSH-диагностика"
|
||||
: "Нет свежих данных"}
|
||||
</StatusBadge>
|
||||
<Button
|
||||
size="compact"
|
||||
variant="secondary"
|
||||
disabled={!supportsLiveTelemetry}
|
||||
onClick={refresh}
|
||||
icon={<Icon name="refresh" size={14} />}
|
||||
>
|
||||
Обновить
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -92,24 +126,37 @@ export function ComputeModulesWorkspace() {
|
||||
<TelemetrySeries
|
||||
label="CPU"
|
||||
value={formatOptionalPercent(cpuPercent)}
|
||||
resource={formatCpuCapacity(
|
||||
cpuPercent,
|
||||
node?.cpu.logical_processors,
|
||||
)}
|
||||
ceiling={100}
|
||||
values={history.map((item) => item.cpu_percent)}
|
||||
/>
|
||||
<TelemetrySeries
|
||||
label="RAM"
|
||||
value={formatOptionalPercent(memoryPercent)}
|
||||
resource={formatResourcePair(
|
||||
node?.memory.used_bytes,
|
||||
node?.memory.total_bytes,
|
||||
)}
|
||||
ceiling={100}
|
||||
values={history.map((item) => item.memory_percent)}
|
||||
/>
|
||||
<TelemetrySeries
|
||||
label="GPU"
|
||||
value={formatOptionalPercent(gpuPercent)}
|
||||
resource={node?.gpu?.name ?? null}
|
||||
ceiling={100}
|
||||
values={history.map((item) => item.gpu_percent)}
|
||||
/>
|
||||
<TelemetrySeries
|
||||
label="VRAM"
|
||||
value={formatOptionalPercent(gpuMemoryPercent)}
|
||||
resource={formatResourcePair(
|
||||
gpuMemoryUsedBytes,
|
||||
gpuMemoryTotalBytes,
|
||||
)}
|
||||
ceiling={100}
|
||||
values={history.map((item) => item.gpu_memory_percent)}
|
||||
/>
|
||||
@@ -135,11 +182,7 @@ export function ComputeModulesWorkspace() {
|
||||
</dl>
|
||||
<dl>
|
||||
<div><dt>GPU</dt><dd>{node?.gpu?.name ?? "—"}</dd></div>
|
||||
<div><dt>VRAM занята</dt><dd>{formatBytes(
|
||||
typeof node?.gpu?.memory_used_mib === "number"
|
||||
? node.gpu.memory_used_mib * 1024 * 1024
|
||||
: null,
|
||||
)}</dd></div>
|
||||
<div><dt>VRAM занята</dt><dd>{formatBytes(gpuMemoryUsedBytes)}</dd></div>
|
||||
<div><dt>Температура</dt><dd>{
|
||||
typeof node?.gpu?.temperature_celsius === "number"
|
||||
? `${node.gpu.temperature_celsius} °C`
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import {
|
||||
Button,
|
||||
GlassSurface,
|
||||
Icon,
|
||||
StatusBadge,
|
||||
} from "@nodedc/ui-react";
|
||||
|
||||
@@ -18,13 +16,17 @@ import {
|
||||
} from "../../core/system/useWorkerTelemetry";
|
||||
|
||||
export function NetworkWorkspace() {
|
||||
const { selectedContour } = useComputeContours();
|
||||
const {
|
||||
selectedContour,
|
||||
telemetryRefreshGeneration,
|
||||
} = useComputeContours();
|
||||
const legacyDiagnostic = selectedContour?.contour_id === "worker-006"
|
||||
&& selectedContour.telemetry_mode === "legacy-ssh";
|
||||
const supportsLiveTelemetry = selectedContour?.contour_id === "worker-006";
|
||||
const { telemetry, error, refresh } = useWorkerTelemetry(
|
||||
const { telemetry, error } = useWorkerTelemetry(
|
||||
(selectedContour?.telemetry_poll_interval_seconds ?? 3) * 1_000,
|
||||
supportsLiveTelemetry,
|
||||
telemetryRefreshGeneration,
|
||||
);
|
||||
const aggregate = telemetry?.network.aggregate ?? null;
|
||||
const connected = Boolean(
|
||||
@@ -50,15 +52,6 @@ export function NetworkWorkspace() {
|
||||
<StatusBadge tone={connected ? "success" : "danger"}>
|
||||
{connected ? "Маршрут доступен" : "Нет свежих данных"}
|
||||
</StatusBadge>
|
||||
<Button
|
||||
size="compact"
|
||||
variant="secondary"
|
||||
disabled={!supportsLiveTelemetry}
|
||||
onClick={refresh}
|
||||
icon={<Icon name="refresh" size={14} />}
|
||||
>
|
||||
Обновить
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -202,6 +202,23 @@ test("data recordings keep the compact session dropdown and laboratory results s
|
||||
assert.match(laboratorySource, /ЛАБОРАТОРНАЯ РАБОТА/);
|
||||
assert.match(laboratorySource, /e28-local-surface/);
|
||||
assert.match(laboratorySource, /e29-camera-geometry/);
|
||||
assert.match(laboratorySource, /laboratoryWorkOrdinal\(right\.label\)/);
|
||||
assert.match(laboratorySource, /initialWorkSelectedRef/);
|
||||
});
|
||||
|
||||
test("recording preparation statuses share the viewer's left alignment", async () => {
|
||||
const spatialStyles = await readFile(
|
||||
new URL("../src/styles/spatial.css", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
const statusStack = spatialStyles.slice(
|
||||
spatialStyles.indexOf(".scene-operation-status-stack"),
|
||||
spatialStyles.indexOf(".scene-operation-status {"),
|
||||
);
|
||||
|
||||
assert.match(statusStack, /left:\s*0\.85rem/);
|
||||
assert.match(statusStack, /justify-items:\s*start/);
|
||||
assert.doesNotMatch(statusStack, /right:/);
|
||||
});
|
||||
|
||||
test("source and laboratory catalogs are requested as disjoint backend projections", async () => {
|
||||
|
||||
@@ -18,6 +18,9 @@ test("Worker 006 telemetry remains a bounded system feature slice", async () =>
|
||||
pipelineStages,
|
||||
networkWorkspace,
|
||||
telemetryPolling,
|
||||
telemetryContext,
|
||||
panelActions,
|
||||
contourSettingsHook,
|
||||
contourSettings,
|
||||
contourContract,
|
||||
pollIntervalContract,
|
||||
@@ -31,6 +34,9 @@ test("Worker 006 telemetry remains a bounded system feature slice", async () =>
|
||||
read("components/system/WorkerPipelineStages.tsx"),
|
||||
read("workspaces/system/NetworkWorkspace.tsx"),
|
||||
read("core/system/useWorkerTelemetry.ts"),
|
||||
read("core/system/ComputeContourContext.tsx"),
|
||||
read("components/useApplicationPanelActions.ts"),
|
||||
read("components/system/useComputeContourSettings.tsx"),
|
||||
read("components/system/ComputeContourSettingsWindow.tsx"),
|
||||
read("core/system/computeContours.ts"),
|
||||
read("core/system/telemetryPollInterval.ts"),
|
||||
@@ -49,7 +55,9 @@ test("Worker 006 telemetry remains a bounded system feature slice", async () =>
|
||||
assert.doesNotMatch(core, /@nodedc\/ui-react/);
|
||||
assert.match(computeWorkspace, /useWorkerTelemetry/);
|
||||
assert.match(computeWorkspace, /telemetry_poll_interval_seconds/);
|
||||
assert.match(computeWorkspace, />\s*Обновить\s*</);
|
||||
assert.doesNotMatch(computeWorkspace, />\s*Обновить\s*</);
|
||||
assert.match(computeWorkspace, /resource=\{formatCpuCapacity/);
|
||||
assert.match(computeWorkspace, /resource=\{formatResourcePair/);
|
||||
assert.doesNotMatch(computeWorkspace, /loading\s*\?\s*"Читаем"/);
|
||||
assert.match(computeWorkspace, /WorkerPipelineStages/);
|
||||
assert.match(pipelineStages, /измеренного времени/);
|
||||
@@ -69,9 +77,15 @@ test("Worker 006 telemetry remains a bounded system feature slice", async () =>
|
||||
);
|
||||
assert.match(networkWorkspace, /127\.0\.0\.1:8000/);
|
||||
assert.match(networkWorkspace, /telemetry_poll_interval_seconds/);
|
||||
assert.match(networkWorkspace, />\s*Обновить\s*</);
|
||||
assert.doesNotMatch(networkWorkspace, />\s*Обновить\s*</);
|
||||
assert.doesNotMatch(networkWorkspace, /loading\s*\?\s*"Читаем"/);
|
||||
assert.doesNotMatch(networkWorkspace, /8765/);
|
||||
assert.match(telemetryContext, /telemetryRefreshGeneration/);
|
||||
assert.match(telemetryContext, /refreshTelemetry/);
|
||||
assert.match(telemetryPolling, /externalRefreshGeneration/);
|
||||
assert.match(panelActions, /actions\.push\(\.\.\.systemUtilityActions\)/);
|
||||
assert.match(contourSettingsHook, /icon: "refresh"/);
|
||||
assert.match(contourSettingsHook, /icon: "settings"/);
|
||||
assert.match(contourSettings, /FieldFrame label="Операционная система"/);
|
||||
assert.match(contourSettings, /label="Интервал MQTT"/);
|
||||
assert.match(contourSettings, /value=\{telemetryPollIntervalDraft\}/);
|
||||
|
||||
Reference in New Issue
Block a user