LLUAV SDK Architecture

This document describes the internal architecture of the LLUAV SDK across Python, C++, and JavaScript implementations.

Design Goals

  1. Strict Request/Response Serialization: The drone firmware processes one command at a time and returns exactly one response. The SDK enforces this via a local FIFO command queue.
  2. Resilience: Network interruptions are common in UAV operations. The SDK automatically reconnects with exponential backoff and re-authenticates.
  3. Low Latency for Urgent Commands: While commands are queued, the queue is lightweight and the worker thread is always ready to dispatch.
  4. Resource Efficiency: Heartbeats and periodic tasks share the same queue as user commands, avoiding duplicate WebSocket frames.

High-Level Component Diagram

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                        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)                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Core Modules

1. Connection Manager

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).

2. Command Queue

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.

3. Heartbeat Keeper

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.

4. Periodic Task Scheduler

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.

5. Timeout & Retry Manager

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"

Language-Specific Implementation Notes

Python (Synchronous)

Python (Async)

C++

JavaScript

Thread/Task Safety Matrix

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

Data Flow Example: takeoff()

  1. User calls client.takeoff() โ†’ internally calls send_cmd("takeoff").
  2. _enqueue("takeoff") creates a QueuedCommand with deadline = now + 10s and appends it to the queue. Returns a Future.
  3. Worker wakes up (or is already running), sees the new command at queue head.
  4. Worker checks connection โ†’ connected, proceed.
  5. Worker sends "takeoff" over WebSocket.
  6. Worker blocks on recv() (or awaits ws.recv() / waits on CV).
  7. Server receives, processes, and sends back JSON response.
  8. Worker parses JSON, sees ret == 0, sets Future result.
  9. Future.get() / await returns the JSON dict to the user.
  10. Worker loops back to process the next queue item.

Data Flow Example: Connection Drop During Flight

  1. Worker sends command C and waits for response.
  2. TCP/WebSocket connection drops. recv() throws / onclose fires.
  3. Worker marks connection dead.
  4. Command C stays at queue head, retry_count incremented.
  5. Worker enters reconnect loop (exponential backoff).
  6. Reconnection succeeds; auto-login completes.
  7. Worker re-sends command C.
  8. Response arrives; Future is fulfilled.
  9. Queue continues normally.