@startuml seq-telemetry title Слежение: загрузка истории и приём телеметрии в реальном времени autonumber actor "Пользователь" as user participant "TelemetryPanel" as ui participant "TelemetryStore\n(.svelte.ts)" as store participant "telemetryApi" as tapi participant "client.ts" as client participant "Backend REST" as rest participant "Backend\nWebSocket" as ws participant "track/+page\n($effect)" as page participant "MapLibre\n(карта)" as map user -> ui : ввод UUID, «Подключиться» ui -> store : connect(id) activate store store -> store : проверка UUID регулярным выражением alt UUID невалиден store -> store : status = 'error' store --> ui : (выход без соединения) end store -> store : disconnect()\nзакрыть прежний WS, очистить points store -> store : status = 'connecting' == Загрузка истории (некритическая) == store -> tapi : fetchHistory(id) tapi -> client : get('/{id}/telemetry/') client -> rest : GET /api/{id}/telemetry/?from&till rest --> client : RawTelemetryPacket[] | { results } client --> tapi : массив пакетов tapi --> store : RawTelemetryPacket[] store -> store : reverse() → хронологический порядок,\npoints = [...history] note right of store : сбой сети → console.warn,\nподключение не прерывается == WebSocket-соединение == store -> tapi : buildWsUrl(id) tapi --> store : ws(s)://host/api/ws/satellite/{id}/telemetry/ store -> ws : new WebSocket(url) ws --> store : onopen → status = 'connected' loop каждый новый пакет ws --> store : onmessage(JSON) store -> store : RawPacket → TelemetryPoint\n(unix-сек → ISO 8601),\npoints = [...points, point] store --> page : реактивное изменение points (Svelte $state) page -> map : scene.clear() page -> map : addLine(трек) + addMarker(старт)\n+ plotAnimatedMarker(текущая точка) page -> map : fitBounds() — однократно (fittedBounds) end == Отключение == user -> ui : «Отключиться» ui -> store : disconnect() store -> ws : close() ws --> store : onclose → status = 'idle' store -> store : points = [], satelliteId = null deactivate store @enduml