diff --git a/apps/control-station/src/components/system/ComputeContourSettingsWindow.tsx b/apps/control-station/src/components/system/ComputeContourSettingsWindow.tsx index ee236f5..9401821 100644 --- a/apps/control-station/src/components/system/ComputeContourSettingsWindow.tsx +++ b/apps/control-station/src/components/system/ComputeContourSettingsWindow.tsx @@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from "react"; import { Button, FeatureSettingsWindow, + FieldFrame, Icon, Select, SettingsCard, @@ -50,6 +51,7 @@ function emptyDraft(): ComputeContourDraft { ssh_port: 22, mqtt_host: "127.0.0.1", mqtt_port: 1883, + telemetry_poll_interval_seconds: 3, }; } @@ -64,6 +66,7 @@ function draftFromContour(contour: ComputeContour | null): ComputeContourDraft { ssh_port: contour.ssh_port, mqtt_host: contour.mqtt_host, mqtt_port: contour.mqtt_port, + telemetry_poll_interval_seconds: contour.telemetry_poll_interval_seconds, }; } @@ -111,6 +114,9 @@ export function ComputeContourSettingsWindow({ && draft.ssh_port > 0 && Number.isInteger(draft.mqtt_port) && draft.mqtt_port > 0 + && Number.isInteger(draft.telemetry_poll_interval_seconds) + && draft.telemetry_poll_interval_seconds >= 1 + && draft.telemetry_poll_interval_seconds <= 60 ), [draft]); const save = async () => { @@ -194,14 +200,16 @@ export function ComputeContourSettingsWindow({ expected_node_id: event.currentTarget.value, }))} /> - setDraft((current) => ({ ...current, platform }))} + /> + + setDraft((current) => ({ + ...current, + telemetry_poll_interval_seconds: Number(event.currentTarget.value), + }))} + /> {draft.telemetry_mode === "legacy-ssh" ? ( (null); diff --git a/apps/control-station/src/workspaces/system/ComputeModulesWorkspace.tsx b/apps/control-station/src/workspaces/system/ComputeModulesWorkspace.tsx index 6a1a6ab..4d30e2a 100644 --- a/apps/control-station/src/workspaces/system/ComputeModulesWorkspace.tsx +++ b/apps/control-station/src/workspaces/system/ComputeModulesWorkspace.tsx @@ -16,7 +16,6 @@ import { import { useComputeContours } from "../../core/system/ComputeContourContext"; import { useWorkerTelemetry, - WORKER_TELEMETRY_POLL_MILLISECONDS, } from "../../core/system/useWorkerTelemetry"; function pipelineStateLabel(state: string): string { @@ -30,8 +29,8 @@ export function ComputeModulesWorkspace() { const legacyDiagnostic = selectedContour?.contour_id === "worker-006" && selectedContour.telemetry_mode === "legacy-ssh"; const supportsLiveTelemetry = selectedContour?.contour_id === "worker-006"; - const { telemetry, loading, error, refresh } = useWorkerTelemetry( - WORKER_TELEMETRY_POLL_MILLISECONDS, + const { telemetry, error, refresh } = useWorkerTelemetry( + (selectedContour?.telemetry_poll_interval_seconds ?? 3) * 1_000, supportsLiveTelemetry, ); const node = telemetry?.node ?? null; @@ -67,11 +66,11 @@ export function ComputeModulesWorkspace() { diff --git a/apps/control-station/src/workspaces/system/NetworkWorkspace.tsx b/apps/control-station/src/workspaces/system/NetworkWorkspace.tsx index 8996dae..b0fd0e0 100644 --- a/apps/control-station/src/workspaces/system/NetworkWorkspace.tsx +++ b/apps/control-station/src/workspaces/system/NetworkWorkspace.tsx @@ -15,7 +15,6 @@ import { import { useComputeContours } from "../../core/system/ComputeContourContext"; import { useWorkerTelemetry, - WORKER_TELEMETRY_POLL_MILLISECONDS, } from "../../core/system/useWorkerTelemetry"; export function NetworkWorkspace() { @@ -23,8 +22,8 @@ export function NetworkWorkspace() { const legacyDiagnostic = selectedContour?.contour_id === "worker-006" && selectedContour.telemetry_mode === "legacy-ssh"; const supportsLiveTelemetry = selectedContour?.contour_id === "worker-006"; - const { telemetry, loading, error, refresh } = useWorkerTelemetry( - WORKER_TELEMETRY_POLL_MILLISECONDS, + const { telemetry, error, refresh } = useWorkerTelemetry( + (selectedContour?.telemetry_poll_interval_seconds ?? 3) * 1_000, supportsLiveTelemetry, ); const aggregate = telemetry?.network.aggregate ?? null; @@ -54,11 +53,11 @@ export function NetworkWorkspace() { diff --git a/apps/control-station/test/systemTelemetry.test.mjs b/apps/control-station/test/systemTelemetry.test.mjs index 6b7ed00..bad34b2 100644 --- a/apps/control-station/test/systemTelemetry.test.mjs +++ b/apps/control-station/test/systemTelemetry.test.mjs @@ -17,6 +17,8 @@ test("Worker 006 telemetry remains a bounded system feature slice", async () => pipelineStages, networkWorkspace, telemetryPolling, + contourSettings, + contourContract, telemetryStyles, styles, ] = await Promise.all([ @@ -27,6 +29,8 @@ 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("components/system/ComputeContourSettingsWindow.tsx"), + read("core/system/computeContours.ts"), read("styles/system-telemetry.css"), read("styles.css"), ]); @@ -41,20 +45,27 @@ test("Worker 006 telemetry remains a bounded system feature slice", async () => assert.match(core, /share_percent/); assert.doesNotMatch(core, /@nodedc\/ui-react/); assert.match(computeWorkspace, /useWorkerTelemetry/); - assert.match(computeWorkspace, /WORKER_TELEMETRY_POLL_MILLISECONDS/); + assert.match(computeWorkspace, /telemetry_poll_interval_seconds/); + assert.match(computeWorkspace, />\s*Обновить\s*\s*Обновить\s* ComputeContour: ssh_port=22, mqtt_host="127.0.0.1", mqtt_port=1883, + telemetry_poll_interval_seconds=3, ) @@ -152,6 +155,9 @@ class ComputeContourStore: ssh_port=request.ssh_port, mqtt_host=request.mqtt_host, mqtt_port=request.mqtt_port, + telemetry_poll_interval_seconds=( + request.telemetry_poll_interval_seconds + ), revision=0, updated_at_utc=_utc_now(), ) @@ -177,6 +183,9 @@ class ComputeContourStore: "ssh_port": request.ssh_port, "mqtt_host": request.mqtt_host, "mqtt_port": request.mqtt_port, + "telemetry_poll_interval_seconds": ( + request.telemetry_poll_interval_seconds + ), "revision": current.revision + 1, "updated_at_utc": _utc_now(), } diff --git a/tests/test_compute_contour_api.py b/tests/test_compute_contour_api.py index 7c01f5b..d0af129 100644 --- a/tests/test_compute_contour_api.py +++ b/tests/test_compute_contour_api.py @@ -39,6 +39,7 @@ def test_contour_store_migrates_worker_006_as_first_configuration( assert len(contours) == 1 assert contours[0].contour_id == "worker-006" assert contours[0].telemetry_mode == "agent-mqtt" + assert contours[0].telemetry_poll_interval_seconds == 3 assert not store.path.exists() @@ -65,10 +66,12 @@ def test_contour_store_creates_and_updates_private_catalog(tmp_path: Path) -> No ssh_port=22, mqtt_host="192.0.2.5", mqtt_port=1883, + telemetry_poll_interval_seconds=1, ), ) assert updated.display_name == "Field Worker 01" + assert updated.telemetry_poll_interval_seconds == 1 assert updated.revision == 1 assert stat.S_IMODE(store.path.stat().st_mode) == 0o600 assert len(store.list_contours()) == 2 @@ -85,6 +88,7 @@ def test_contour_store_creates_and_updates_private_catalog(tmp_path: Path) -> No ssh_port=22, mqtt_host="127.0.0.1", mqtt_port=1883, + telemetry_poll_interval_seconds=3, ), )