feat(k1): stabilize LAB bridge and isolate onboard device integration

This commit is contained in:
DCCONSTRUCTIONS
2026-09-07 10:31:33 +03:00
parent 020a878915
commit 63ea2bed67
115 changed files with 13700 additions and 988 deletions
@@ -3,13 +3,14 @@ import { Button } from "@nodedc/ui-react";
import type { XgridsConnectionAttempt } from "../api";
import { hostFailureDiagnosticPresentation } from "../hostDiagnosticPresentation";
import { STATION_WIFI_FAILURE_MESSAGES } from "../networkFailurePresentation";
const connectionAttemptStageLabels: Record<string, string> = {
accepted: "Запрос принят",
"scan-selection-admitted": "Результат выбран",
"host-wifi-profile-preflight": "Подготовка профиля Wi‑Fi",
"device-ap-activation": "Подготовка локальной сети",
"ble-provisioning-write": "Передаются настройки сети",
"ble-provisioning-write": "Подключение K1 к Wi‑Fi",
"ble-write-dispatched": "Настройки переданы",
"status-observing": "Ожидание ответа",
"device-topology-applied": "Целевая сеть подтверждена",
@@ -62,6 +63,8 @@ export function attemptNextActionLabel(
return "Проверить управление без изменения сети";
case "start-acquisition":
return "Готово к запуску приёма";
case "stop-acquisition":
return "Остановить текущий приём";
case "stop-local-receiver":
return "Завершить только локальный приём";
case "retire-unavailable-physical-target":
@@ -74,6 +77,9 @@ export function attemptNextActionLabel(
}
const publicConnectionErrorLabels: Readonly<Record<string, string>> = {
...STATION_WIFI_FAILURE_MESSAGES,
BleakGATTProtocolError:
"K1 ответил ошибкой Bluetooth. Подключение к Wi‑Fi не подтверждено; проверьте состояние устройства или начните новое подключение.",
"network-provision-discovery-generation-conflict":
"Результат Bluetooth-поиска устарел до отправки. Настройки устройства не изменялись; выполните новый поиск.",
"connection-mode-draft-revision-conflict":
@@ -121,6 +127,7 @@ export function K1OperatorError({
onClear: () => void;
}) {
const structured = hostFailureDiagnosticPresentation(diagnostic);
const stationFailure = STATION_WIFI_FAILURE_MESSAGES[attempt?.public_error_code ?? ""];
const [diagnosticCopied, setDiagnosticCopied] = useState(false);
const copyDiagnosticBundle = async () => {
if (!attempt?.diagnostic_bundle || !navigator.clipboard) return;
@@ -137,7 +144,7 @@ export function K1OperatorError({
>
<span className="error-banner__dot" aria-hidden="true" />
<div className="error-banner__copy">
<strong>{title}</strong>
<strong>{stationFailure ? "Не удалось подключить K1 к Wi‑Fi" : title}</strong>
<p>{publicConnectionErrorLabel(attempt, structured)}</p>
{recoveryActions ? (
<div className="error-banner__recovery-actions">
@@ -31,6 +31,7 @@ import type {
} from "../api";
import { profileSelectionForConnectionMode } from "../compatibility";
import {
BLE_DISCOVERY_TIMEOUT_SECONDS,
DEFAULT_CONNECTION_MODE,
connectionModeOptions,
type ConnectionMode,
@@ -66,10 +67,12 @@ import {
type ReadOnlyConnectionObservationTarget,
} from "../lifecycle";
import { finiteMetric } from "../presentation";
import { STATION_WIFI_FAILURE_MESSAGES } from "../networkFailurePresentation";
import type { XgridsK1Controller } from "../runtimeContext";
import { K1OperatorError } from "./K1OperatorError";
import {
connectionActionAuthoritySnapshot,
networkProvisionFailureMessage,
type BleDiscoverySubmitResult,
type ConnectionActionAuthoritySnapshot,
} from "../useXgridsK1Runtime";
@@ -1866,7 +1869,7 @@ export function K1ProvisioningPipeline({
const connectionRecoveryRequired = connectionRecoveryIsRequired(
connectionRecoveryKey,
escapedConnectionRecoveryKey,
) && !(searchRequested && !searchDisplayActive);
) && (connectionAttemptFailed || !(searchRequested && !searchDisplayActive));
const presentedConnectionRecoveryRequired = !livePreparationPending && (
connectionRecoveryRequired || connectionRecoveryVerificationPending
);
@@ -2089,7 +2092,7 @@ export function K1ProvisioningPipeline({
const startedAt = Date.now();
const updateCountdown = () => {
const elapsedSeconds = Math.floor((Date.now() - startedAt) / 1_000);
setScanSecondsRemaining(Math.max(0, 6 - elapsedSeconds));
setScanSecondsRemaining(Math.max(0, BLE_DISCOVERY_TIMEOUT_SECONDS - elapsedSeconds));
};
updateCountdown();
const timer = window.setInterval(updateCountdown, 250);
@@ -2350,7 +2353,7 @@ export function K1ProvisioningPipeline({
let scanResult: BleDiscoverySubmitResult | null = null;
try {
if (!runtimeActionIsCurrent(actionFence)) return;
scanResult = await scanWithResult({ durationSeconds: 6 });
scanResult = await scanWithResult({ durationSeconds: BLE_DISCOVERY_TIMEOUT_SECONDS });
if (
!localScenarioActionEpochIsCurrent(
actionEpoch,
@@ -2573,12 +2576,12 @@ export function K1ProvisioningPipeline({
}
const freshStartAllowed = result.intentDisposition === "release";
const failureMessage = freshStartAllowed
const failureMessage = networkProvisionFailureMessage(observedOperation) ?? (freshStartAllowed
&& provisioningFailureRequiresFreshCandidate(result.failureReasonCode)
? "Результат Bluetooth-поиска устарел до отправки. Настройки не отправлены; выполните явный повторный поиск."
: freshStartAllowed
? "Подключение не началось. Настройки удалены; после устранения причины начните новый явный поиск."
: "Результат отправки неизвестен. Повторное применение заблокировано, чтобы не отправить BLE-команду дважды; дождитесь, пока система определит безопасное продолжение.";
: "Подключение не подтверждено. Проверьте состояние K1 или начните новое подключение; автоматического повтора не было.");
setConnectionAttemptPresentation((current) => (
current?.idempotencyKey === idempotencyKey
? {
@@ -3203,7 +3206,9 @@ export function K1ProvisioningPipeline({
disabled={isBusy || modeResetInFlight}
onClick={() => void changeDesiredConnectionMode(connectionMode)}
>
{savedBridgeNetworkSetupRequired
{STATION_WIFI_FAILURE_MESSAGES[connectionRecoveryAttempt?.public_error_code ?? ""]
? "Указать сеть Wi‑Fi заново"
: savedBridgeNetworkSetupRequired
? "Подключить K1 к общей сети"
: "Подключить новый K1"}
</Button>
@@ -3256,7 +3261,7 @@ export function K1ProvisioningPipeline({
title="Подключение"
status={
searchActive
? `Поиск · ${scanSecondsRemaining ?? 6} с`
? `Поиск · до ${scanSecondsRemaining ?? BLE_DISCOVERY_TIMEOUT_SECONDS} с`
: connectionAttemptSettling
? "Устройство выбрано"
: presentedPhysicalRecoveryRequired
@@ -3316,7 +3321,7 @@ export function K1ProvisioningPipeline({
>
<ActivityIndicator />
<strong>
Поиск Bluetooth · {scanSecondsRemaining ?? 6} с
Поиск Bluetooth · до {scanSecondsRemaining ?? BLE_DISCOVERY_TIMEOUT_SECONDS} с
</strong>
</div>
) : networkRecoveryRequired