feat(k1): stabilize LAB bridge and isolate onboard device integration
This commit is contained in:
@@ -215,8 +215,8 @@ def default_environment_settings() -> EnvironmentSettingsDocument:
|
||||
description=(
|
||||
"Свести все сенсоры в синхронную и управляемую операторскую картину."
|
||||
),
|
||||
primary_workspace_id="spatial-scene",
|
||||
secondary_workspace_id="cameras",
|
||||
primary_workspace_id="cameras",
|
||||
secondary_workspace_id="world-map",
|
||||
background=background.model_copy(),
|
||||
),
|
||||
missions=EnvironmentPage(
|
||||
@@ -372,10 +372,24 @@ def _upgrade_environment(payload: object) -> EnvironmentSettingsDocument:
|
||||
raise ValueError("environment document must be an object")
|
||||
schema_version = payload.get("schema_version")
|
||||
if schema_version == "missioncore.operator-environment/v1":
|
||||
return _upgrade_v1_environment(payload)
|
||||
if schema_version == "missioncore.operator-environment/v2":
|
||||
return _upgrade_v2_environment(payload)
|
||||
return EnvironmentSettingsDocument.model_validate(payload)
|
||||
document = _upgrade_v1_environment(payload)
|
||||
elif schema_version == "missioncore.operator-environment/v2":
|
||||
document = _upgrade_v2_environment(payload)
|
||||
else:
|
||||
document = EnvironmentSettingsDocument.model_validate(payload)
|
||||
# The owner moved the local live scene to LAB. Retire only its old
|
||||
# Control shortcuts, preserving custom copy, backgrounds and other links.
|
||||
page = document.pages.observation
|
||||
if page.primary_workspace_id == "spatial-scene":
|
||||
page.primary_workspace_id = (
|
||||
page.secondary_workspace_id
|
||||
if page.secondary_workspace_id not in (None, "spatial-scene")
|
||||
else "cameras"
|
||||
)
|
||||
page.secondary_workspace_id = None
|
||||
elif page.secondary_workspace_id == "spatial-scene":
|
||||
page.secondary_workspace_id = None
|
||||
return document
|
||||
|
||||
|
||||
class EnvironmentSettingsStore:
|
||||
|
||||
@@ -136,3 +136,52 @@ def sensor_operation(
|
||||
return operation(fleet, vehicle_id, operation_id)
|
||||
except PairingError as error:
|
||||
raise HTTPException(404, str(error)) from None
|
||||
|
||||
|
||||
@router.get("/{vehicle_id}/devices/enrollment")
|
||||
def enrollment_state(
|
||||
vehicle_id: str, response: Response, fleet: Annotated[FleetRegistry, Depends(local_operator)]
|
||||
):
|
||||
response.headers["Cache-Control"] = "no-store"
|
||||
try:
|
||||
with fleet.lock:
|
||||
row = fleet.find(vehicle_id)
|
||||
return {
|
||||
**row.get("device_enrollment", {"available": False}),
|
||||
"node_id": row["node_id"],
|
||||
"name": row["name"],
|
||||
"fresh": fleet.public(row)["connectivity"] == "online",
|
||||
}
|
||||
except PairingError as error:
|
||||
raise HTTPException(404, str(error)) from None
|
||||
|
||||
|
||||
@router.post("/{vehicle_id}/devices/enrollment/operations")
|
||||
def enrollment_submit(
|
||||
vehicle_id: str,
|
||||
body: dict,
|
||||
response: Response,
|
||||
fleet: Annotated[FleetRegistry, Depends(local_operator)],
|
||||
):
|
||||
response.headers["Cache-Control"] = "no-store"
|
||||
try:
|
||||
return fleet.device_enrollment.submit(fleet, vehicle_id, body)
|
||||
except (PairingError, ValueError, TypeError):
|
||||
# Validation diagnostics must never echo a supplied credential.
|
||||
raise HTTPException(
|
||||
409, "Запрос подключения не принят. Обновите БК и проверьте параметры сети."
|
||||
) from None
|
||||
|
||||
|
||||
@router.get("/{vehicle_id}/devices/enrollment/operations/{operation_id}")
|
||||
def enrollment_operation(
|
||||
vehicle_id: str,
|
||||
operation_id: str,
|
||||
response: Response,
|
||||
fleet: Annotated[FleetRegistry, Depends(local_operator)],
|
||||
):
|
||||
response.headers["Cache-Control"] = "no-store"
|
||||
try:
|
||||
return fleet.device_enrollment.operation(fleet, vehicle_id, operation_id)
|
||||
except PairingError as error:
|
||||
raise HTTPException(404, str(error)) from None
|
||||
|
||||
@@ -18,6 +18,7 @@ _EXTRA_FIELDS: Final = (
|
||||
"connection_mode",
|
||||
"error_category",
|
||||
"error_code",
|
||||
"transport_error_code",
|
||||
"reason_code",
|
||||
"failed_phase",
|
||||
"dialogue_stage",
|
||||
@@ -32,6 +33,20 @@ _EXTRA_FIELDS: Final = (
|
||||
"device_write_confirmed",
|
||||
"ble_att_error_code",
|
||||
"ble_att_error_name",
|
||||
"resolved_write_mode",
|
||||
"max_write_without_response_size",
|
||||
"mtu_size",
|
||||
"frame_length",
|
||||
"bridge_frame_length",
|
||||
"quick_connect_frame_length",
|
||||
"scan_elapsed_ms",
|
||||
"scanner_start_ms",
|
||||
"initial_window_ms",
|
||||
"first_candidate_ms",
|
||||
"scan_extended",
|
||||
"candidate_count",
|
||||
"likely_k1_candidate_count",
|
||||
"discovery_generation",
|
||||
"helper_stage",
|
||||
"helper_elapsed_ms",
|
||||
"status_reconciliation",
|
||||
@@ -102,6 +117,26 @@ class ScannerDiagnosticJsonFormatter(logging.Formatter):
|
||||
value = getattr(record, field, None)
|
||||
if value is not None and isinstance(value, (str, int, bool)):
|
||||
document[field] = value
|
||||
if record.exc_info is not None:
|
||||
exception_type, _exception, traceback = record.exc_info
|
||||
if exception_type is not None:
|
||||
document["exception_type"] = exception_type.__name__[:128]
|
||||
if traceback is not None:
|
||||
while traceback.tb_next is not None:
|
||||
traceback = traceback.tb_next
|
||||
code = traceback.tb_frame.f_code
|
||||
# Preserve the failure location, never exception text, locals,
|
||||
# source lines or absolute paths that may contain private data.
|
||||
document["exception_site"] = (
|
||||
f"{Path(code.co_filename).name}:{traceback.tb_lineno}:{code.co_name}"
|
||||
)[:256]
|
||||
properties = getattr(record, "write_characteristic_properties", None)
|
||||
if isinstance(properties, (list, tuple)) and all(
|
||||
isinstance(value, str)
|
||||
and value in {"read", "write", "write-without-response", "notify", "indicate"}
|
||||
for value in properties
|
||||
):
|
||||
document["write_characteristic_properties"] = sorted(set(properties))
|
||||
return json.dumps(document, ensure_ascii=False, separators=(",", ":"))
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user