wip(k1): checkpoint connection recovery rewrite
Capture the current unreleased K1 connection, recovery, lifecycle, viewer, and test work as a single known-bad baseline for subsequent fixes.
This commit is contained in:
@@ -1,7 +1,245 @@
|
||||
import type { StatusTone } from "@nodedc/ui-react";
|
||||
|
||||
import type { BackendStatus } from "@mission-core/plugin-sdk";
|
||||
import type { XgridsK1Metrics } from "./api";
|
||||
import type {
|
||||
XgridsConnectionPolicyAction,
|
||||
XgridsK1Metrics,
|
||||
XgridsK1State,
|
||||
} from "./api";
|
||||
import {
|
||||
connectionPolicyAllows,
|
||||
connectionPolicyDecision,
|
||||
} from "./lifecycle";
|
||||
|
||||
const connectionPolicyReasonCopy: Record<string, string> = {
|
||||
"connection-supervisor-closed": "Контур связи K1 закрыт.",
|
||||
"supervisor-action-not-allowed": "Текущая связь с K1 не подтверждает право на эту физическую команду.",
|
||||
"network-provision-operation-active": "Предыдущая сетевая операция K1 ещё не завершена.",
|
||||
"acquisition-active": "Сетевой режим K1 нельзя менять во время активного приёма.",
|
||||
"acquisition-cleanup-pending": "Локальный приём K1 ещё завершает очистку ресурсов.",
|
||||
"local-runtime-active": "Локальный исполнительный контур K1 ещё активен.",
|
||||
"control-session-not-admissible-for-network-change": "Текущая управляющая сессия K1 ещё не допускает смену сети.",
|
||||
"network-mutation-reconciliation-required": "Результат предыдущей сетевой записи K1 не подтверждён.",
|
||||
"network-mutation-ledger-corrupt": "Журнал сетевых изменений K1 повреждён.",
|
||||
"fresh-ble-candidate-required": "Для этого действия нужен K1 из нового Bluetooth-поиска.",
|
||||
"retained-recovery-context-unavailable": "Сохранённый Bluetooth-контекст текущего K1 больше недоступен.",
|
||||
"fresh-candidate-supersedes-retained-recovery": "K1 снова виден в свежем поиске; используйте новый найденный экземпляр.",
|
||||
"reconciliation-target-not-observed": "В свежем Bluetooth-поиске не найден K1, связанный с незавершённой записью.",
|
||||
"reconciliation-target-not-retained": "Текущий серверный Bluetooth-контекст относится не к тому K1, для которого не завершена сетевая операция.",
|
||||
"current-device-context-unavailable": "Текущий K1 не подтверждён в оперативном контексте этого процесса.",
|
||||
"durable-recovery-target-unavailable": "В сохранённом серверном состоянии нет одной точной пары K1 и режима для проверки после перезапуска.",
|
||||
"fresh-candidate-supersedes-durable-recovery": "Нужный K1 снова найден свежим Bluetooth-поиском; сервер требует проверить именно свежий экземпляр.",
|
||||
"retained-recovery-supersedes-durable-recovery": "Сервер хранит более свежий контекст текущего K1.",
|
||||
"configured-endpoint-unavailable": "Нет подтверждённого сохранённого адреса K1 для безопасной проверки.",
|
||||
"configured-endpoint-probe-lifecycle-busy": "Контур подключения K1 занят другой операцией.",
|
||||
"configured-endpoint-changed-during-probe": "Адрес K1 изменился во время проверки; результат отброшен.",
|
||||
"configured-endpoint-probe-failed": "Маршрут и управляющий endpoint K1 не удалось проверить.",
|
||||
"host-path-unavailable": "На этом компьютере не подтверждён сетевой путь до K1.",
|
||||
"host-route-not-direct": "Маршрут до K1 проходит не через ожидаемую локальную сеть.",
|
||||
"endpoint-not-reachable": "Управляющий endpoint K1 сейчас недоступен.",
|
||||
"device-identity-unverified": "Идентичность подключённого K1 ещё не подтверждена.",
|
||||
"device-identity-stale": "Подтверждение идентичности K1 устарело.",
|
||||
"device-identity-mismatch": "Подключённое устройство не совпало с выбранным K1.",
|
||||
"device-identity-pin-store-corrupt": "Хранилище привязки устройства повреждено.",
|
||||
"network-provisioning-idempotency-unavailable": "Журнал сетевых намерений K1 недоступен.",
|
||||
"network-provisioning-idempotency-corrupt": "Журнал сетевых намерений K1 повреждён.",
|
||||
"network-provisioning-idempotency-invalid": "Журнал сетевых намерений K1 не прошёл проверку.",
|
||||
"network-provisioning-idempotency-operation-mismatch": "Текущая сетевая операция не совпала с сохранённым намерением.",
|
||||
"semantic-topology-store-corrupt": "Сохранённая топология K1 повреждена.",
|
||||
"control-plane-not-healthy": "Управляющий канал K1 не подтверждён.",
|
||||
"connection-lease-not-reachable": "Текущая сессия связи K1 больше не подтверждена.",
|
||||
"data-plane-stalled": "Поток данных K1 перестал обновляться.",
|
||||
"data-plane-lost": "Поток данных K1 потерян.",
|
||||
"physical-command-reconciliation-required": "Результат предыдущей физической команды K1 не подтверждён.",
|
||||
"physical-device-already-active": "Последнее подтверждённое состояние K1 — активное сканирование.",
|
||||
"physical-command-recovery-target-unavailable": "Журнал не содержит точную Bluetooth-цель для восстановления K1.",
|
||||
"physical-command-recovery-target-not-observed": "Исходный K1 пока не найден в свежем Bluetooth-поиске.",
|
||||
"physical-command-recovery-target-not-retained": "Сохранённый Bluetooth-контекст относится не к исходному K1.",
|
||||
"physical-command-recovery-target-mismatch": "Выбрано другое устройство или другой способ связи, чем в незавершённой физической сессии.",
|
||||
"physical-command-ledger-corrupt": "Журнал физических команд K1 повреждён.",
|
||||
"physical-command-ledger-unavailable": "Журнал физических команд K1 недоступен.",
|
||||
"physical-control-authority-unavailable": "Управляющая связь не позволяет безопасно отправить физический STOP.",
|
||||
"ble-runtime-restart-required": "BLE-контур требует контролируемого перезапуска.",
|
||||
"ble-runtime-cleanup-pending": "BLE-контур завершает предыдущую операцию.",
|
||||
"ble-runtime-busy": "BLE-контур занят другой операцией.",
|
||||
"k1-lifecycle-process-lease-network-owned": "Сетевой переход K1 ещё владеет исполнительным контуром.",
|
||||
"k1-lifecycle-process-lease-control-owned": "Управляющая сессия K1 ещё владеет исполнительным контуром.",
|
||||
"local-acquisition-receiver-not-active": "Активного локального приёмника сейчас нет.",
|
||||
"physical-stop-is-authoritative": "Доступна подтверждённая физическая остановка K1; локальная очистка не должна её подменять.",
|
||||
"action-not-implemented": "Это действие не реализовано и не может быть выполнено.",
|
||||
"connection-reconfiguration-lifecycle-busy": "Другое действие подключения ещё не завершено.",
|
||||
"connection-reconfiguration-bridge-only": "Это действие доступно только для подключения Bridge.",
|
||||
"connection-reconfiguration-current-device-unavailable": "Нет точной привязки текущего устройства для изменения сети.",
|
||||
"connection-reconfiguration-active": "Сначала завершите или отмените текущий выбор устройства или сети.",
|
||||
"connection-reconfiguration-required-device-not-observed": "Исходное устройство не найдено в текущем Bluetooth-поиске.",
|
||||
"connection-reconfiguration-not-active": "Активного изменения устройства или сети уже нет.",
|
||||
"connection-reconfiguration-process-lease-busy": "Другой локальный процесс ещё управляет подключением устройства.",
|
||||
"connection-reconfiguration-acquisition-changed": "Состояние приёма изменилось во время подготовки подключения.",
|
||||
"connection-reconfiguration-revision-conflict": "Выбор устройства или сети уже изменился в другой вкладке.",
|
||||
"connection-reconfiguration-binding-conflict": "Активное подключение изменилось до выполнения действия.",
|
||||
"connection-reconfiguration-fresh-scan-required": "Для этого действия нужен новый Bluetooth-поиск.",
|
||||
"connection-reconfiguration-discovery-conflict": "Результаты Bluetooth-поиска относятся к предыдущему действию.",
|
||||
"connection-reconfiguration-target-mismatch": "Изменение сети разрешено только для исходного устройства.",
|
||||
"acquisition-start-operation-active": "Запуск приёма ещё не завершён.",
|
||||
"control-session-state-unsafe": "Управляющий диалог ещё не достиг безопасного состояния ожидания.",
|
||||
};
|
||||
|
||||
const connectionPolicyNextActionCopy: Record<string, string> = {
|
||||
"wait-for-operation": "Дождитесь завершения текущей операции и обновите состояние.",
|
||||
"diagnose-network-ledger": "Не отправляйте новые команды и проверьте журнал сетевой операции.",
|
||||
"diagnose-physical-command-ledger": "Не повторяйте команду; сначала проверьте журнал физических команд.",
|
||||
"restart-ble-runtime": "Контролируемо перезапустите локальный BLE-контур и обновите состояние.",
|
||||
"scan-ble": "Выполните свежий поиск Bluetooth-устройств.",
|
||||
"observe-fresh-device-network": "Дождитесь автоматического восстановления связи с выбранным K1.",
|
||||
"observe-current-device-network": "Дождитесь автоматического восстановления связи с тем же K1.",
|
||||
"observe-configured-device-network": "Дождитесь автоматического восстановления сохранённого подключения K1.",
|
||||
"recover-current-device-network": "Нажмите «Подключиться заново».",
|
||||
"inspect-host-network": "Проверьте активную локальную сеть и маршрут этого компьютера.",
|
||||
"probe-endpoint": "Дождитесь обновления подключения K1.",
|
||||
"verify-control-device-info": "Подключитесь заново к выбранному K1.",
|
||||
"select-connection-intent": "Выберите способ подключения и заново подтвердите текущий K1.",
|
||||
"start-acquisition": "Повторно откройте финальное подтверждение физического START.",
|
||||
"stop-acquisition": "Остановите K1 через подтверждённую физическую остановку.",
|
||||
"stop-local-receiver": "Завершите локальный приём; физическое состояние K1 проверьте вручную.",
|
||||
"retire-unavailable-physical-target": "Явно исключите недоступный прежний K1 перед новым выбором.",
|
||||
"manual-recovery-required": "Автоматически безопасного продолжения нет; проверьте состояние K1 вручную.",
|
||||
"cancel-reconfiguration": "Отмените текущий выбор и вернитесь к обычному восстановлению подключения.",
|
||||
};
|
||||
|
||||
export interface ConnectionPolicyOperatorGuidance {
|
||||
reason: string;
|
||||
nextAction: string;
|
||||
}
|
||||
|
||||
const connectionModeSelectionReasonCopy: Record<string, string> = {
|
||||
"connection-mode-selection-physical-state-unsafe":
|
||||
"Предыдущая физическая команда K1 осталась без подтверждённого результата. Поэтому способ подключения пока нельзя изменить.",
|
||||
"connection-mode-selection-control-state-unsafe":
|
||||
"Текущий управляющий процесс K1 ещё не завершён. После его завершения способ подключения снова станет доступен.",
|
||||
"connection-mode-selection-lifecycle-busy":
|
||||
"Текущее действие подключения ещё завершается. После него способ подключения снова станет доступен.",
|
||||
"connection-reconfiguration-active":
|
||||
"Сначала завершите или отмените текущий выбор устройства или сети.",
|
||||
};
|
||||
|
||||
const physicalRetirementReasonCopy: Record<string, string> = {
|
||||
"physical-command-retirement-operation-conflict":
|
||||
"Сейчас завершается другая операция с физическим состоянием устройства. Дождитесь её завершения и обновите состояние.",
|
||||
"physical-command-retirement-state-unsafe":
|
||||
"Состояние предыдущей физической команды изменилось. Обновите состояние перед новым выбором устройства.",
|
||||
"physical-command-retirement-not-required":
|
||||
"Предыдущая физическая команда уже разрешена или больше не удерживает выбор устройства. Обновите состояние и продолжите обычное подключение.",
|
||||
"physical-command-target-retired":
|
||||
"Предыдущее устройство уже выведено из текущего контура. Можно сразу выполнить новый явный Bluetooth-поиск.",
|
||||
"network-provision-operation-active":
|
||||
"Сейчас завершается подключение устройства к сети. Дождитесь результата перед выбором другого устройства.",
|
||||
"connection-reconfiguration-active":
|
||||
"Сначала завершите или отмените текущее изменение устройства или сети.",
|
||||
"control-local-retirement-pending":
|
||||
"Управляющая сессия ещё освобождает локальные ресурсы. Дождитесь завершения и обновите состояние.",
|
||||
"acquisition-active":
|
||||
"Сканирование ещё активно. Сначала остановите его и дождитесь подтверждённого завершения.",
|
||||
"acquisition-cleanup-pending":
|
||||
"Локальный приём ещё освобождает ресурсы после остановки. Дождитесь завершения.",
|
||||
"acquisition-start-operation-active":
|
||||
"Запуск сканирования ещё не завершён. Дождитесь его результата перед сменой устройства.",
|
||||
"acquisition-stop-operation-active":
|
||||
"Остановка сканирования ещё не завершена. Дождитесь её результата перед сменой устройства.",
|
||||
"local-runtime-active":
|
||||
"Локальный поток устройства ещё активен или завершается. Дождитесь перехода в состояние ожидания.",
|
||||
"control-session-state-unsafe":
|
||||
"Управляющая сессия устройства ещё не завершена. Дождитесь её закрытия и обновите состояние.",
|
||||
"ble-runtime-busy":
|
||||
"Bluetooth занят другой операцией устройства. Дождитесь её завершения; новый поиск автоматически не запустится.",
|
||||
"ble-runtime-cleanup-pending":
|
||||
"Bluetooth ещё завершает предыдущую операцию. Дождитесь освобождения соединения и обновите состояние.",
|
||||
"ble-runtime-restart-required":
|
||||
"Локальный Bluetooth-контур требует контролируемого перезапуска. Команды устройству не отправлялись.",
|
||||
"k1-lifecycle-process-lease-active":
|
||||
"Другой локальный процесс ещё завершает действие с устройством. Дождитесь его завершения и обновите состояние.",
|
||||
"physical-command-ledger-corrupt":
|
||||
"Журнал физической команды повреждён. Не выбирайте другое устройство до проверки журнала.",
|
||||
};
|
||||
|
||||
const physicalReopenReasonCopy: Record<string, string> = {
|
||||
...physicalRetirementReasonCopy,
|
||||
"physical-command-reconciliation-reopen-not-required":
|
||||
"Это устройство больше не требует возврата из предыдущего выбора. Обновите состояние и продолжите обычное подключение.",
|
||||
"physical-command-reconciliation-reopen-target-not-observed":
|
||||
"Предыдущее устройство не найдено в последнем Bluetooth-поиске. Обновите поиск после проверки питания K1.",
|
||||
"physical-command-reconciliation-reopen-target-not-connectable":
|
||||
"Предыдущее устройство найдено, но сейчас не принимает Bluetooth-подключение. Проверьте питание K1 и повторите явный поиск.",
|
||||
"physical-command-reconciliation-reopen-candidate-ambiguous":
|
||||
"Последний Bluetooth-поиск не подтвердил один точный экземпляр предыдущего K1. Повторите поиск рядом только с нужным устройством.",
|
||||
"physical-command-reconciliation-reopen-operation-conflict":
|
||||
"Другая операция уже меняет локальное состояние предыдущего устройства. Дождитесь её завершения и обновите поиск.",
|
||||
"device-calibration-read-active":
|
||||
"Сейчас читается калибровка устройства. Дождитесь завершения проверки перед повторным использованием K1.",
|
||||
};
|
||||
|
||||
/** Human explanation for a backend-disabled topology selector. */
|
||||
export function connectionModeSelectionGuidance(
|
||||
state: XgridsK1State | null | undefined,
|
||||
): string | null {
|
||||
const selection = state?.connection_lifecycle?.mode_selection;
|
||||
if (!selection) return null;
|
||||
if (selection?.allowed === true) return null;
|
||||
const reasonCode = selection?.reason_codes.find((code) => code.trim().length > 0);
|
||||
return reasonCode
|
||||
? connectionModeSelectionReasonCopy[reasonCode]
|
||||
?? "Способ подключения пока недоступен, потому что состояние K1 не позволяет безопасно изменить его."
|
||||
: "Способ подключения пока недоступен, потому что состояние K1 не позволяет безопасно изменить его."
|
||||
}
|
||||
|
||||
/** Human explanation for a currently unavailable local-only device escape. */
|
||||
export function physicalRetirementGuidance(
|
||||
state: XgridsK1State | null | undefined,
|
||||
): string | null {
|
||||
const retirement = (
|
||||
state?.physical_command
|
||||
?? state?.application_control_session?.physical_command
|
||||
?? null
|
||||
)?.operator_retirement;
|
||||
if (!retirement || retirement.allowed === true) return null;
|
||||
const reasonCode = retirement.reason_codes.find((code) => code.trim().length > 0);
|
||||
return reasonCode
|
||||
? physicalRetirementReasonCopy[reasonCode]
|
||||
?? "Выбор другого устройства пока небезопасен. Обновите состояние после завершения текущей операции."
|
||||
: "Выбор другого устройства пока небезопасен. Обновите состояние после завершения текущей операции.";
|
||||
}
|
||||
|
||||
/** Human explanation for a backend-disabled exact retired-device reopen. */
|
||||
export function physicalReopenGuidance(
|
||||
state: XgridsK1State | null | undefined,
|
||||
): string | null {
|
||||
const reopen = state?.physical_command?.operator_reconciliation_reopen;
|
||||
if (!reopen || reopen.allowed === true) return null;
|
||||
const reasonCode = reopen.reason_codes.find((code) => code.trim().length > 0);
|
||||
return reasonCode
|
||||
? physicalReopenReasonCopy[reasonCode]
|
||||
?? "Повторная проверка предыдущего K1 сейчас небезопасна. Дождитесь завершения текущей операции и обновите поиск."
|
||||
: "Повторная проверка предыдущего K1 сейчас небезопасна. Обновите поиск после завершения текущей операции.";
|
||||
}
|
||||
|
||||
export function connectionPolicyOperatorGuidance(
|
||||
state: XgridsK1State | null | undefined,
|
||||
action: XgridsConnectionPolicyAction,
|
||||
): ConnectionPolicyOperatorGuidance | null {
|
||||
if (connectionPolicyAllows(state, action)) return null;
|
||||
const decision = connectionPolicyDecision(state, action);
|
||||
const reasonCode = decision?.reason_codes.find((code) => code.trim().length > 0);
|
||||
const recommendedAction = state?.connection_policy?.recommended_action?.trim();
|
||||
return {
|
||||
reason: reasonCode
|
||||
? connectionPolicyReasonCopy[reasonCode]
|
||||
?? "Система временно запретила действие до восстановления подтверждённого состояния."
|
||||
: "Подтверждённая политика действия ещё не получена.",
|
||||
nextAction: recommendedAction
|
||||
? connectionPolicyNextActionCopy[recommendedAction]
|
||||
?? "Обновите состояние подключения и следуйте рекомендованному безопасному действию."
|
||||
: "Обновите состояние подключения перед новым действием.",
|
||||
};
|
||||
}
|
||||
|
||||
const phaseLabels: Record<string, string> = {
|
||||
idle: "Ожидание",
|
||||
|
||||
Reference in New Issue
Block a user