This document describes the internal architecture of the LLUAV SDK across Python, C++, and JavaScript implementations.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ User Application โ
โ (calls takeoff(), get_state(), add_periodic_task(), etc.) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ High-Level API Layer โ
โ (typed wrappers: takeoff โ send_cmd("takeoff", "")) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Command Queue Manager โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ QueuedCmd 1 โโโ QueuedCmd 2 โโโ QueuedCmd N โ โ
โ โ + promise โ โ + promise โ โ + promise โ โ
โ โ + deadline โ โ + deadline โ โ + deadline โ โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Worker Thread/Task โ
โ while running: โ
โ 1. peek queue head โ
โ 2. check deadline (timeout) โ
โ 3. ensure connection (reconnect if needed) โ
โ 4. ws.send(cmd) โ
โ 5. ws.recv() โโโ blocks until response โ
โ 6. parse JSON, validate ret โ
โ 7. fulfill promise / resolve future โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ WebSocket Transport โ
โ (ws://<host>:53001, text frames) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Responsibilities:
- Open/close the WebSocket.
- Track connection state (connected flag).
- On disconnect: trigger auto-reconnect if enabled.
- On reconnect: re-send login if password is configured.
Reconnection Strategy:
delay = reconnect_interval (default 1s)
while not connected and not shutdown:
try connect
if password: login
if success: return true
sleep(delay)
delay = min(delay * 2, max_reconnect_interval) # exponential backoff
All queued commands are preserved during reconnection. If a command is in-flight when the connection drops, it is returned to the head of the queue for retry (up to max_retries).
Why a queue? - The firmware is strictly request/response. Sending two commands back-to-back without waiting for the first response causes the responses to be indistinguishable. - A local queue decouples the user thread from network latency.
Queue Behavior:
- FIFO: Commands are processed in insertion order.
- Deadline: Each command carries a deadline = now + timeout. Expired commands are rejected immediately without sending.
- Retry: On send failure, timeout, or connection drop mid-flight, the command is retried (max 3 by default).
- Fire-and-forget: Heartbeats and periodic tasks are enqueued like normal commands but their results are discarded. This guarantees they never interrupt an in-flight user command from the wire perspective.
Purpose: Prevent NAT/firewall session timeouts and detect half-open connections early.
Behavior:
- Runs on a timer (default 5s).
- Only enqueues a heartbeat command when the user queue is empty.
- Uses a lightweight command such as state or help.
- If the heartbeat itself fails, the connection is marked dead and reconnection begins.
Purpose: Allow users to register recurring operations (e.g., telemetry polling) without managing their own timers.
Behavior:
- Maintains a map of name โ (interval, cmd, args, last_run).
- Scans tasks every 200ms.
- When a task is due, it calls _enqueue(cmd, args) just like a user command.
- Because it shares the same queue, periodic tasks are naturally rate-limited by queue depth and respect the one-in-flight rule.
Per-command timeout: - Default: 10 seconds (configurable per client and per command). - Timer starts when the command reaches the head of the queue, not at enqueue time.
Retry logic:
if send fails or recv times out:
retry_count += 1
if retry_count <= max_retries:
keep at queue head and retry after 500ms
else:
reject with "Max retries exceeded"
threading.Thread for Worker, Heartbeat, and Periodic loops.threading.Lock + threading.Condition for the queue.concurrent.futures.Future is returned by _enqueue(). future.result() blocks the caller until the response arrives.send_cmd(..., block=False) returns the Future immediately, allowing the caller to compose async-style logic in a sync runtime.asyncio.Task for Worker, Heartbeat, and Periodic loops.asyncio.Queue with async/await semantics.async def and return asyncio.Future. Users await directly.websocketpp with Asio transport (asio_no_tls_client).client_.run() (event loop).std::mutex + std::condition_variable for the queue.std::promise<json> / std::future<json> for command completion.client_.start_perpetual() to keep the Asio thread alive across reconnects. A dedicated login_promise handles reconnection authentication without interfering with the normal command queue.std::future for heartbeat commands and stores them in a cleanup list to prevent broken_promise exceptions.WebSocket (browser) or ws library (Node.js).this._queue)._currentRequest holds the command currently awaiting a response._processQueue() is called after every connection open, message receipt, and timeout.setTimeout schedules reconnect attempts with exponential backoff. The onclose handler automatically returns the in-flight request to the queue head.setInterval every 200ms checks task deadlines and calls _enqueue().| Operation | Python Sync | Python Async | C++ | JS |
|---|---|---|---|---|
| Enqueue command | Thread-safe | Event-loop safe | Thread-safe | Event-loop safe |
| Queue pop | Worker thread only | Worker task only | Worker thread only | Event loop only |
| WS send | Worker thread only | Worker task only | Worker thread โ Asio thread | Event loop only |
| WS recv | Worker thread only | Worker task only | Message handler โ CV notify | onmessage callback |
| Reconnect | Worker thread | Worker task | Worker thread | setTimeout callback |
| Periodic enqueue | Periodic thread | Periodic task | Periodic thread | setInterval callback |
takeoff()client.takeoff() โ internally calls send_cmd("takeoff")._enqueue("takeoff") creates a QueuedCommand with deadline = now + 10s and appends it to the queue. Returns a Future."takeoff" over WebSocket.recv() (or awaits ws.recv() / waits on CV).ret == 0, sets Future result.Future.get() / await returns the JSON dict to the user.C and waits for response.recv() throws / onclose fires.C stays at queue head, retry_count incremented.C.