Test server
This commit is contained in:
parent
43c120df4c
commit
1e2a276126
3 changed files with 317 additions and 6 deletions
40
README.md
40
README.md
|
|
@ -47,6 +47,46 @@ python3 test_server.py --speedup 200 --start 5900 # сразу перед ра
|
||||||
Эмулирует полный профиль полёта (подъём 5 м/с → разрыв на 30 км → спуск) в
|
Эмулирует полный профиль полёта (подъём 5 м/с → разрыв на 30 км → спуск) в
|
||||||
точном формате пакетов бэкенда. Подключаться на `ws://localhost:8765/`.
|
точном формате пакетов бэкенда. Подключаться на `ws://localhost:8765/`.
|
||||||
|
|
||||||
|
## Демо бок-о-бок с leaflet_svelte (через Django, как в проде)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scripts/demo-stack.sh # Django :8000 + leaflet :5173 + дэшборд :5174 + станция
|
||||||
|
scripts/demo-stack.sh restart-flight # полёт заново: рестарт станции + чистка истории в БД
|
||||||
|
scripts/demo-stack.sh stop
|
||||||
|
```
|
||||||
|
|
||||||
|
Станция летит по `mock_prediction.json` из корня репозиториев (реальный
|
||||||
|
предикт) со случайным дрейфом до ±5 км. Каждый запуск/`restart-flight`
|
||||||
|
начинает полёт с нуля; после него нажми «Отключиться/Подключиться» в обоих
|
||||||
|
окнах, чтобы сбросить старые графики. Темп: `SPEEDUP=60 scripts/demo-stack.sh`.
|
||||||
|
|
||||||
|
Скрипт также поднимает **фейковый predictor на :8080** (захардкоженный адрес
|
||||||
|
`TawhiriClient` в Django), который отдаёт тот же `mock_prediction.json` —
|
||||||
|
поэтому «Рассчитать» в leaflet возвращает ровно ту траекторию, по которой
|
||||||
|
летит станция. Порядок для оверлея прогноза в слежении: `/predict` →
|
||||||
|
«Рассчитать» → перейти на «Слежение» **кликом в навбаре** (результат прогноза
|
||||||
|
живёт в памяти SPA и не переживает перезагрузку страницы) → выбрать рабочую
|
||||||
|
область в «Сравнении с прогнозом». Если на :8080 уже работает настоящий
|
||||||
|
predictor — скрипт его не трогает и прогнозы идут через него.
|
||||||
|
|
||||||
|
График отклонений сопоставляет точки по времени: при ускоренном полёте
|
||||||
|
(speedup > 1) времена телеметрии и прогноза расходятся — для честного
|
||||||
|
сравнения ставь дату запуска в форме «сейчас» и `SPEEDUP=1`.
|
||||||
|
|
||||||
|
Скрипт (адаптация leaflet_svelte/scripts/run-stack.sh под новые пути) сам
|
||||||
|
создаёт спутник и токен, запускает `test_server.py --push` как наземную
|
||||||
|
станцию и печатает UUID спутника. Дальше:
|
||||||
|
|
||||||
|
- **leaflet**: http://localhost:5173/track → логин demo/demo → вставить UUID →
|
||||||
|
«Подключиться»;
|
||||||
|
- **дэшборд**: http://localhost:5174/ → адрес
|
||||||
|
`ws://localhost:8000/api/ws/satellite/<UUID>/telemetry/` → «Подключиться»
|
||||||
|
(адрес запоминается в localStorage; история за прошедший полёт подтянется
|
||||||
|
по REST автоматически).
|
||||||
|
|
||||||
|
Оба окна читают один и тот же поток из Django. Если фронты уже запущены
|
||||||
|
руками — скрипт их не трогает, просто вставь UUID в оба окна.
|
||||||
|
|
||||||
## Пре-флайт аудит форматов
|
## Пре-флайт аудит форматов
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
159
scripts/demo-stack.sh
Executable file
159
scripts/demo-stack.sh
Executable file
|
|
@ -0,0 +1,159 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Boot the side-by-side demo: one synthetic ground station feeding BOTH UIs
|
||||||
|
# through the real Django backend (production topology).
|
||||||
|
#
|
||||||
|
# test_server.py --push ──▶ Django :8000 ──▶ leaflet_svelte :5173 (/track)
|
||||||
|
# └──▶ telemetry-dashboard :5174
|
||||||
|
#
|
||||||
|
# The station flies along mock_prediction.json (the real predict) with a
|
||||||
|
# bounded random drift, if that file exists at the repo root.
|
||||||
|
#
|
||||||
|
# Adapted from leaflet_svelte/scripts/run-stack.sh (paths updated after the
|
||||||
|
# repo move; DJANGO_ENV is NOT set so Django uses the local sqlite db).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# scripts/demo-stack.sh # start everything
|
||||||
|
# scripts/demo-stack.sh restart-flight # начать полёт заново (станция + чистка истории)
|
||||||
|
# scripts/demo-stack.sh stop # stop everything
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT=/home/anton/stratoflights
|
||||||
|
BACKEND_DIR=$ROOT/stratoflights
|
||||||
|
LEAFLET_DIR=$ROOT/leaflet_svelte
|
||||||
|
DASH_DIR=$ROOT/telemetry-dashboard
|
||||||
|
TRAJECTORY=$ROOT/mock_prediction.json
|
||||||
|
PID_DIR=/tmp/strato-demo
|
||||||
|
SPEEDUP=${SPEEDUP:-20}
|
||||||
|
|
||||||
|
provision() { # prints "SAT TOKEN"
|
||||||
|
(cd "$BACKEND_DIR" && python3 - <<'EOF'
|
||||||
|
import os, django
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'stratoflights.settings')
|
||||||
|
django.setup()
|
||||||
|
from stratoflights_api.models import Satellite, User
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
|
u = User.objects.get(username='demo')
|
||||||
|
sat, _ = Satellite.objects.get_or_create(name='demo-balloon', user=u)
|
||||||
|
u.satellites.add(sat)
|
||||||
|
token, _ = Token.objects.get_or_create(user=u)
|
||||||
|
print(sat.id, token.key)
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
wipe_history() {
|
||||||
|
(cd "$BACKEND_DIR" && python3 - <<'EOF'
|
||||||
|
import os, django
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'stratoflights.settings')
|
||||||
|
django.setup()
|
||||||
|
from stratoflights_api.models import TelemetryPacket
|
||||||
|
n, _ = TelemetryPacket.objects.filter(satellite__name='demo-balloon').delete()
|
||||||
|
print(f"история очищена: {n} пакетов")
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
kill_station() {
|
||||||
|
pkill -f 'test_server.py --push' 2>/dev/null && sleep 1 || true
|
||||||
|
rm -f "$PID_DIR/station.pid"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_station() { # $1=SAT $2=TOKEN
|
||||||
|
local traj_args=()
|
||||||
|
[[ -f "$TRAJECTORY" ]] && traj_args=(--trajectory "$TRAJECTORY")
|
||||||
|
echo "starting synthetic ground station (speedup x$SPEEDUP)"
|
||||||
|
cd "$DASH_DIR"
|
||||||
|
python3 -u test_server.py --push ws://localhost:8000 \
|
||||||
|
--satellite "$1" --token "$2" \
|
||||||
|
--interval 1 --speedup "$SPEEDUP" "${traj_args[@]}" \
|
||||||
|
> /tmp/strato-demo-station.log 2>&1 &
|
||||||
|
echo $! > "$PID_DIR/station.pid"
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
stop)
|
||||||
|
for f in "$PID_DIR"/*.pid; do
|
||||||
|
[[ -f "$f" ]] && kill "$(cat "$f")" 2>/dev/null && echo "stopped $(basename "$f" .pid)"
|
||||||
|
rm -f "$f"
|
||||||
|
done
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
restart-flight)
|
||||||
|
mkdir -p "$PID_DIR"
|
||||||
|
kill_station
|
||||||
|
wipe_history
|
||||||
|
read -r SAT TOKEN < <(provision)
|
||||||
|
start_station "$SAT" "$TOKEN"
|
||||||
|
echo "полёт перезапущен с нуля. Нажми Отключиться/Подключиться в обоих окнах,"
|
||||||
|
echo "чтобы сбросить старые графики (история в БД уже чистая)."
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
mkdir -p "$PID_DIR"
|
||||||
|
|
||||||
|
# --- fake predictor on :8080 (TawhiriClient's hardcoded port) ------------------
|
||||||
|
# Serves the mock_prediction trajectory, so «Запустить прогноз» in leaflet
|
||||||
|
# returns exactly the flight the demo station is flying.
|
||||||
|
if ! ss -tlnp 2>/dev/null | grep -q ':8080'; then
|
||||||
|
echo "starting fake predictor on :8080"
|
||||||
|
FAKE_TAWHIRI_TRAJECTORY="$TRAJECTORY" \
|
||||||
|
python3 -u "$LEAFLET_DIR/tests/e2e/fake_tawhiri.py" 8080 \
|
||||||
|
> /tmp/strato-demo-predictor.log 2>&1 &
|
||||||
|
echo $! > "$PID_DIR/predictor.pid"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Django (sqlite dev settings) --------------------------------------------
|
||||||
|
# Restart if it's ours (pid file) so env changes (CSRF origins) apply.
|
||||||
|
if [[ -f "$PID_DIR/django.pid" ]] && kill -0 "$(cat "$PID_DIR/django.pid")" 2>/dev/null; then
|
||||||
|
kill "$(cat "$PID_DIR/django.pid")" 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
if ! ss -tlnp 2>/dev/null | grep -q ':8000'; then
|
||||||
|
echo "starting Django on :8000"
|
||||||
|
cd "$BACKEND_DIR"
|
||||||
|
DEBUG=True \
|
||||||
|
ALLOWED_HOSTS=localhost,127.0.0.1 \
|
||||||
|
CSRF_TRUSTED_ORIGINS="http://localhost:5173,http://localhost:5174,http://localhost:8000,http://127.0.0.1:5173,http://127.0.0.1:5174,http://127.0.0.1:8000" \
|
||||||
|
python3 manage.py runserver 0.0.0.0:8000 > /tmp/strato-demo-django.log 2>&1 &
|
||||||
|
echo $! > "$PID_DIR/django.pid"
|
||||||
|
sleep 3
|
||||||
|
else
|
||||||
|
echo "ВНИМАНИЕ: :8000 занят не нашим Django — если ловишь CSRF-ошибку, останови его и перезапусти этот скрипт"
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -r SAT TOKEN < <(provision)
|
||||||
|
|
||||||
|
# --- synthetic ground station (push mode) -------------------------------------
|
||||||
|
# всегда стартуем полёт с нуля: старую станцию гасим, историю чистим
|
||||||
|
kill_station
|
||||||
|
wipe_history
|
||||||
|
start_station "$SAT" "$TOKEN"
|
||||||
|
|
||||||
|
# --- leaflet_svelte -----------------------------------------------------------
|
||||||
|
if ! ss -tlnp 2>/dev/null | grep -q ':5173'; then
|
||||||
|
echo "starting leaflet_svelte on :5173"
|
||||||
|
cd "$LEAFLET_DIR"
|
||||||
|
VITE_USE_MOCK_API=false VITE_API_BASE_URL=/api VITE_API_PROXY_TARGET=http://localhost:8000 \
|
||||||
|
npm run dev -- --port 5173 --strictPort > /tmp/strato-demo-leaflet.log 2>&1 &
|
||||||
|
echo $! > "$PID_DIR/leaflet.pid"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- telemetry-dashboard ------------------------------------------------------
|
||||||
|
if ! ss -tlnp 2>/dev/null | grep -q ':5174'; then
|
||||||
|
echo "starting telemetry-dashboard on :5174"
|
||||||
|
cd "$DASH_DIR"
|
||||||
|
VITE_DEFAULT_WS_URL="ws://localhost:8000/api/ws/satellite/$SAT/telemetry/" \
|
||||||
|
npm run dev -- --port 5174 --strictPort > /tmp/strato-demo-dash.log 2>&1 &
|
||||||
|
echo $! > "$PID_DIR/dash.pid"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 2
|
||||||
|
echo
|
||||||
|
echo "--- demo stack ready ---"
|
||||||
|
echo " satellite UUID: $SAT"
|
||||||
|
echo " leaflet (карта): http://localhost:5173/track (login: demo/demo, ID спутника выше)"
|
||||||
|
echo " дэшборд (приборы): http://localhost:5174/ (ws://localhost:8000/api/ws/satellite/$SAT/telemetry/)"
|
||||||
|
echo
|
||||||
|
echo "перезапуск полёта: scripts/demo-stack.sh restart-flight"
|
||||||
|
echo "logs: /tmp/strato-demo-*.log stop: scripts/demo-stack.sh stop"
|
||||||
124
test_server.py
124
test_server.py
|
|
@ -11,14 +11,24 @@ Usage:
|
||||||
|
|
||||||
--speedup compresses flight time: 20 means one wall-clock second advances the
|
--speedup compresses flight time: 20 means one wall-clock second advances the
|
||||||
flight by 20 s (a 3 h flight replays in ~9 min).
|
flight by 20 s (a 3 h flight replays in ~9 min).
|
||||||
|
|
||||||
|
Push mode (--push): instead of serving, connect to the Django backend as a
|
||||||
|
ground station and push the same synthetic flight there. Both leaflet_svelte
|
||||||
|
(tracking) and this dashboard can then read the shared stream from
|
||||||
|
ws://<host>/api/ws/satellite/<uuid>/telemetry/:
|
||||||
|
|
||||||
|
python3 test_server.py --push ws://localhost:8000 \
|
||||||
|
--satellite <uuid> --token <drf-token> --speedup 20
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import bisect
|
||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
import websockets
|
import websockets
|
||||||
|
|
||||||
|
|
@ -28,14 +38,51 @@ GROUND_ALT = 200.0
|
||||||
BASE_LAT, BASE_LON = 61.66, 129.38
|
BASE_LAT, BASE_LON = 61.66, 129.38
|
||||||
START_WALL = time.time()
|
START_WALL = time.time()
|
||||||
|
|
||||||
|
# Random lat/lon drift around the trajectory (metres): each packet takes a
|
||||||
|
# random step; past DRIFT_MAX_M the next step is forced back toward zero.
|
||||||
|
DRIFT_STEP_M = 150.0
|
||||||
|
DRIFT_MAX_M = 5000.0
|
||||||
|
|
||||||
def sample(flight_s: float) -> dict:
|
TRAJ = None # [(t_s, lat, lon, alt), ...] from --trajectory, else synthetic
|
||||||
|
_TRAJ_TIMES = []
|
||||||
|
_drift = {"n": 0.0, "e": 0.0}
|
||||||
|
|
||||||
|
|
||||||
|
def load_trajectory(path):
|
||||||
|
"""stratoflights/Tawhiri prediction JSON -> [(sec_from_launch, lat, lon, alt)]."""
|
||||||
|
d = json.load(open(path))
|
||||||
|
stages = (d.get("result") or d)["prediction"]
|
||||||
|
pts = [p for s in stages for p in s["trajectory"]]
|
||||||
|
t0 = datetime.fromisoformat(pts[0]["datetime"].replace("Z", "+00:00"))
|
||||||
|
traj = []
|
||||||
|
for p in pts:
|
||||||
|
t = (datetime.fromisoformat(p["datetime"].replace("Z", "+00:00")) - t0).total_seconds()
|
||||||
|
traj.append((t, p["latitude"], p["longitude"], p["altitude"]))
|
||||||
|
return traj
|
||||||
|
|
||||||
|
|
||||||
|
def traj_at(flight_s):
|
||||||
|
"""Interpolated (lat, lon, alt, landed) along TRAJ."""
|
||||||
|
if flight_s <= TRAJ[0][0]:
|
||||||
|
_, lat, lon, alt = TRAJ[0]
|
||||||
|
return lat, lon, alt, False
|
||||||
|
if flight_s >= TRAJ[-1][0]:
|
||||||
|
_, lat, lon, alt = TRAJ[-1]
|
||||||
|
return lat, lon, alt, True
|
||||||
|
i = bisect.bisect_right(_TRAJ_TIMES, flight_s)
|
||||||
|
t0, la0, lo0, al0 = TRAJ[i - 1]
|
||||||
|
t1, la1, lo1, al1 = TRAJ[i]
|
||||||
|
f = (flight_s - t0) / (t1 - t0)
|
||||||
|
return la0 + f * (la1 - la0), lo0 + f * (lo1 - lo0), al0 + f * (al1 - al0), False
|
||||||
|
|
||||||
|
|
||||||
|
def synth_at(flight_s):
|
||||||
|
"""Fallback synthetic profile: 5 m/s ascent to 30 km, parachute descent."""
|
||||||
ascent_end = (BURST_ALT - GROUND_ALT) / ASCENT_MS
|
ascent_end = (BURST_ALT - GROUND_ALT) / ASCENT_MS
|
||||||
if flight_s < ascent_end:
|
if flight_s < ascent_end:
|
||||||
alt = GROUND_ALT + ASCENT_MS * flight_s
|
alt = GROUND_ALT + ASCENT_MS * flight_s
|
||||||
vs_phase = "ascent"
|
landed = False
|
||||||
else:
|
else:
|
||||||
# parachute descent, faster in thin air
|
|
||||||
t = flight_s - ascent_end
|
t = flight_s - ascent_end
|
||||||
alt = BURST_ALT
|
alt = BURST_ALT
|
||||||
dt, step = 0.0, 5.0
|
dt, step = 0.0, 5.0
|
||||||
|
|
@ -44,7 +91,26 @@ def sample(flight_s: float) -> dict:
|
||||||
alt -= rate * step
|
alt -= rate * step
|
||||||
dt += step
|
dt += step
|
||||||
alt = max(alt, GROUND_ALT)
|
alt = max(alt, GROUND_ALT)
|
||||||
vs_phase = "descent" if alt > GROUND_ALT else "landed"
|
landed = alt <= GROUND_ALT
|
||||||
|
lat = BASE_LAT + flight_s * 1.2e-5
|
||||||
|
lon = BASE_LON + flight_s * 6e-5
|
||||||
|
return lat, lon, alt, landed
|
||||||
|
|
||||||
|
|
||||||
|
def drift_step():
|
||||||
|
for k in ("n", "e"):
|
||||||
|
d = random.uniform(-DRIFT_STEP_M, DRIFT_STEP_M)
|
||||||
|
if abs(_drift[k]) > DRIFT_MAX_M:
|
||||||
|
d = -math.copysign(abs(d), _drift[k]) # forced step back
|
||||||
|
_drift[k] += d
|
||||||
|
|
||||||
|
|
||||||
|
def sample(flight_s: float) -> dict:
|
||||||
|
lat, lon, alt, landed = traj_at(flight_s) if TRAJ else synth_at(flight_s)
|
||||||
|
drift_step()
|
||||||
|
lat += _drift["n"] / 111320.0
|
||||||
|
lon += _drift["e"] / (111320.0 * math.cos(math.radians(lat)))
|
||||||
|
vs_phase = "landed" if landed else "flight"
|
||||||
|
|
||||||
alt_noise = alt + random.uniform(-8, 8)
|
alt_noise = alt + random.uniform(-8, 8)
|
||||||
pressure = 101325.0 * math.exp(-alt / 8400.0)
|
pressure = 101325.0 * math.exp(-alt / 8400.0)
|
||||||
|
|
@ -64,8 +130,8 @@ def sample(flight_s: float) -> dict:
|
||||||
"id": "test",
|
"id": "test",
|
||||||
# flight-time timestamp so derived vertical speed is realistic under --speedup
|
# flight-time timestamp so derived vertical speed is realistic under --speedup
|
||||||
"timestamp": int(START_WALL + flight_s),
|
"timestamp": int(START_WALL + flight_s),
|
||||||
"lat": BASE_LAT + flight_s * 1.2e-5 + random.uniform(-1e-5, 1e-5),
|
"lat": round(lat, 7),
|
||||||
"lon": BASE_LON + flight_s * 6e-5 + random.uniform(-1e-5, 1e-5),
|
"lon": round(lon, 7),
|
||||||
"alt": round(alt_noise, 1),
|
"alt": round(alt_noise, 1),
|
||||||
"payload": {
|
"payload": {
|
||||||
"utc": time.strftime("%H:%M:%S", utc) + ".00",
|
"utc": time.strftime("%H:%M:%S", utc) + ".00",
|
||||||
|
|
@ -102,6 +168,33 @@ async def handler(ws, interval: float, speedup: float, start_s: float = 0.0):
|
||||||
print(f"client disconnected after {n} packets")
|
print(f"client disconnected after {n} packets")
|
||||||
|
|
||||||
|
|
||||||
|
async def push(base_url, satellite, token, interval, speedup, start_s):
|
||||||
|
"""Ground-station mode: push the synthetic flight into the Django backend."""
|
||||||
|
base = base_url.rstrip("/").replace("http://", "ws://").replace("https://", "wss://")
|
||||||
|
url = f"{base}/api/ws/station/{satellite}/telemetry/?token={token}"
|
||||||
|
n = 0
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
async with websockets.connect(url) as ws:
|
||||||
|
print(f"pushing to {url.split('?')[0]}")
|
||||||
|
|
||||||
|
async def log_errors():
|
||||||
|
async for msg in ws:
|
||||||
|
print(f" server: {msg}")
|
||||||
|
|
||||||
|
reader = asyncio.ensure_future(log_errors())
|
||||||
|
while True:
|
||||||
|
flight_s = start_s + (time.time() - START_WALL) * speedup
|
||||||
|
await ws.send(json.dumps(sample(flight_s)))
|
||||||
|
n += 1
|
||||||
|
if n % 25 == 0:
|
||||||
|
print(f" sent {n} packets, flight time {flight_s/60:.1f} min")
|
||||||
|
await asyncio.sleep(interval)
|
||||||
|
except (OSError, websockets.WebSocketException) as e:
|
||||||
|
print(f"push link down ({e}); retrying in 3 s")
|
||||||
|
await asyncio.sleep(3)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
ap = argparse.ArgumentParser()
|
ap = argparse.ArgumentParser()
|
||||||
ap.add_argument("--port", type=int, default=8765)
|
ap.add_argument("--port", type=int, default=8765)
|
||||||
|
|
@ -109,8 +202,27 @@ async def main():
|
||||||
ap.add_argument("--speedup", type=float, default=20.0, help="flight seconds per wall second")
|
ap.add_argument("--speedup", type=float, default=20.0, help="flight seconds per wall second")
|
||||||
ap.add_argument("--start", type=float, default=0.0,
|
ap.add_argument("--start", type=float, default=0.0,
|
||||||
help="flight seconds to skip at connect (5900 ≈ just before burst)")
|
help="flight seconds to skip at connect (5900 ≈ just before burst)")
|
||||||
|
ap.add_argument("--push", metavar="URL", help="push to Django backend instead of serving")
|
||||||
|
ap.add_argument("--satellite", help="satellite UUID (push mode)")
|
||||||
|
ap.add_argument("--token", help="DRF auth token (push mode)")
|
||||||
|
ap.add_argument("--trajectory", metavar="JSON",
|
||||||
|
help="fly along a stratoflights/Tawhiri prediction JSON "
|
||||||
|
"(с рандомным дрейфом ±5 км) вместо встроенного профиля")
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
if args.trajectory:
|
||||||
|
global TRAJ, _TRAJ_TIMES
|
||||||
|
TRAJ = load_trajectory(args.trajectory)
|
||||||
|
_TRAJ_TIMES = [p[0] for p in TRAJ]
|
||||||
|
print(f"trajectory: {len(TRAJ)} pts, {_TRAJ_TIMES[-1]/60:.0f} min, "
|
||||||
|
f"launch {TRAJ[0][1]:.5f},{TRAJ[0][2]:.5f}")
|
||||||
|
|
||||||
|
if args.push:
|
||||||
|
if not args.satellite or not args.token:
|
||||||
|
ap.error("--push requires --satellite and --token")
|
||||||
|
await push(args.push, args.satellite, args.token, args.interval, args.speedup, args.start)
|
||||||
|
return
|
||||||
|
|
||||||
async with websockets.serve(
|
async with websockets.serve(
|
||||||
lambda ws: handler(ws, args.interval, args.speedup, args.start), "localhost", args.port
|
lambda ws: handler(ws, args.interval, args.speedup, args.start), "localhost", args.port
|
||||||
):
|
):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue