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

- **Concurrency**: `threading.Thread` for Worker, Heartbeat, and Periodic loops.
- **Synchronization**: `threading.Lock` + `threading.Condition` for the queue.
- **Blocking API**: `concurrent.futures.Future` is returned by `_enqueue()`. `future.result()` blocks the caller until the response arrives.
- **Non-blocking mode**: `send_cmd(..., block=False)` returns the `Future` immediately, allowing the caller to compose async-style logic in a sync runtime.

### Python (Async)

- **Concurrency**: `asyncio.Task` for Worker, Heartbeat, and Periodic loops.
- **Queue**: `asyncio.Queue` with `async/await` semantics.
- **API**: All methods are `async def` and return `asyncio.Future`. Users `await` directly.

### C++

- **WebSocket Library**: `websocketpp` with Asio transport (`asio_no_tls_client`).
- **Concurrency**:
  - One Asio thread runs `client_.run()` (event loop).
  - One Worker thread dequeues and sends.
  - One Heartbeat thread, one Periodic thread.
- **Synchronization**:
  - `std::mutex` + `std::condition_variable` for the queue.
  - `std::promise<json>` / `std::future<json>` for command completion.
- **Reconnection**: Uses `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.
- **Fire-and-forget**: Obtains a `std::future` for heartbeat commands and stores them in a cleanup list to prevent `broken_promise` exceptions.

### JavaScript

- **WebSocket**: Native `WebSocket` (browser) or `ws` library (Node.js).
- **Concurrency**: Single-threaded event loop. No extra threads needed.
- **Queue**: Plain JavaScript array (`this._queue`).
- **State Machine**:
  - `_currentRequest` holds the command currently awaiting a response.
  - `_processQueue()` is called after every connection open, message receipt, and timeout.
- **Reconnection**: `setTimeout` schedules reconnect attempts with exponential backoff. The `onclose` handler automatically returns the in-flight request to the queue head.
- **Periodic Tasks**: `setInterval` every 200ms checks task deadlines and calls `_enqueue()`.

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