# C++ API Reference

**What you’ll do**
- C++ SDK의 public 인터페이스(헤더/클래스/동작/반환값)를 빠르게 찾아볼 수 있습니다.

**Prerequisites**
- UART 프로토콜 규격을 알고 있으면 이해가 쉽습니다: {doc}`UART Protocol Reference </page/Protocol/uart_protocol>`

**Next**
- 제어 의미/부호/단위: {doc}`Vehicle Control Model </page/Introduction/control_model>`

---

## 1. Overview

- **Scope:** `inc`에 정의된 공개 C++ API

**Public headers**
- `inc/KMC_protocol.hpp`
- `inc/KMC_serial_port.hpp`
- `inc/KMC_uart_client.hpp`
- `inc/KMC_driver.hpp`

**Namespaces**
- `KMC_HARDWARE`
- `KMC_HARDWARE::protocol`

**Units**
- velocity: $\mathrm{m/s}$
- angular rate: $\mathrm{rad/s}$
- curvature: $\mathrm{m}^{-1}$
- rate: $\mathrm{Hz}$
- voltage: $\mathrm{V}$
- current: $\mathrm{A}$
- temperature: $^\circ\mathrm{C}$
- angle: $^\circ$
- speed: $\mathrm{rpm}$
- time: $\mathrm{ms}$, $\mu\mathrm{s}$
- baud rate: $\mathrm{bps}$

---

## 2. Driver (Main Interface)

(sdk-driver-options)=
### 2.1 Driver::Options

| Field | Type | Default | Unit | Description |
| --- | --- | --- | --- | --- |
| `port` | `std::string` | `""` | - | Device path (e.g., `/dev/ttyKMC`) |
| `serial` | `SerialPortOptions` | defaults | - | Serial options (see SerialPortOptions) |
| `control_rate_hz` | `double` | `100.0` | $\mathrm{Hz}$ | 0xA5 control stream rate |
| `vehicle_speed_rate_hz` | `double` | `50.0` | $\mathrm{Hz}$ | 0xB3 speed request rate |
| `poll_battery` | `bool` | `false` | - | Enable battery polling |
| `battery_rate_hz` | `double` | `1.0` | $\mathrm{Hz}$ | Battery polling rate |
| `poll_allstate` | `bool` | `false` | - | Enable all-state polling |
| `allstate_rate_hz` | `double` | `10.0` | $\mathrm{Hz}$ | All-state polling rate |
| `allstate_motor_left` | `uint8_t` | `0` | - | Left motor ID |
| `allstate_motor_right` | `uint8_t` | `1` | - | Right motor ID |
| `command_timeout_ms` | `int` | `300` | $\mathrm{ms}$ | If no `setCommand()` update within this time, send (0,0) |
| `stop_burst_count` | `int` | `3` | - | Repeat count of (0,0) on `stop()` |
| `realtime_priority` | `int` | `-1` | - | Linux SCHED_FIFO priority (1-99), -1 disables |
| `cpu_affinity` | `int` | `-1` | - | Linux CPU affinity index, -1 disables |
| `max_queue` | `size_t` | `1024` | - | Max receive queue size (drop oldest on overflow) |

### 2.2 Driver API

- `bool start(const Options& opt)`
  - Open port, flush input, start I/O thread.
- `void stop()`
  - Stop I/O thread, send safety stop, close port.
- `bool isRunning() const`
  - I/O thread running state.
- `void setCommand(float velocity_mps, float omega_rps)`
  - Input: $v$ ($\mathrm{m/s}$), $\omega$ ($\mathrm{rad/s}$).
  - Internal: $\kappa = \omega / v$ (if $|v| \le 10^{-3}$, then $\kappa = 0$).
  - 0xA5 uses $(v,\kappa)$.
- `void setCommandCurvature(float velocity_mps, float curvature_1pm)`
  - Input: $v$ ($\mathrm{m/s}$), $\kappa$ ($\mathrm{m}^{-1}$).
- `std::optional<Message> tryPopMessage()`
  - Non-blocking queue pop.
- `bool waitPopMessage(Message& out, int timeout_ms)`
  - Blocking pop with timeout ($\mathrm{ms}$).
- `bool requestBatteryOnce(uint8_t motor_id = 0)`
  - Send one 0xAF battery request.
- `bool requestAllStateOnce(uint8_t motor_id)`
  - Send one 0xAF all-state request.

```{admonition} 명령 입력 매핑
:class: note

- UART 명령 입력(프레임 ID: `0xA5`) payload는 $(v,\kappa)$로 고정입니다.
- `setCommand(v, omega)`는 내부에서 $\kappa=\omega/v$로 변환해 전송합니다($|v| \le 10^{-3}$이면 $\kappa=0$).
```

### 2.3 Driver I/O Loop Behavior (Implementation Detail)

- 0xA5 at `control_rate_hz`.
- 0xB3 at `vehicle_speed_rate_hz`.
- 0xAF auxiliary/diagnostic requests when `poll_battery` / `poll_allstate` enabled.
- Parsed frames are pushed to the queue.
- If `command_timeout_ms` expires, (0,0) is sent instead of the last command.

---

## 3. Data Structures (Messages)

`Message` type:
```
std::variant<VehicleSpeed, BatteryVoltage, AllState, AfResponse>
```

### VehicleSpeed

| Field | Type | Unit | Description |
| --- | --- | --- | --- |
| `mps` | `float` | $\mathrm{m/s}$ | Vehicle speed |

### BatteryVoltage

| Field | Type | Unit | Description |
| --- | --- | --- | --- |
| `volt` | `float` | $\mathrm{V}$ | Battery voltage |

### AllState

| Field | Type | Unit | Description |
| --- | --- | --- | --- |
| `id` | `uint32_t` | - | Motor ID |
| `position_deg` | `float` | $^\circ$ | Motor position |
| `speed_rpm` | `float` | $\mathrm{rpm}$ | Mechanical speed |
| `current_A` | `float` | $\mathrm{A}$ | Current |
| `temperature_C` | `float` | $^\circ\mathrm{C}$ | Temperature |
| `error_code` | `uint32_t` | - | Error bitmask |
| `reserved_0` | `float` | - | Reserved |
| `reserved_1` | `float` | - | Reserved |
| `reserved_2` | `float` | - | Reserved |

### AfResponse (Raw 0xAF)

| Field | Type | Unit | Description |
| --- | --- | --- | --- |
| `motor_id` | `uint8_t` | - | Response motor ID |
| `rw` | `uint8_t` | - | RW_READ / RW_WRITE |
| `ids` | `std::vector<uint8_t>` | - | ID list |
| `data_f32` | `std::vector<float>` | - | float32 LE decode |
| `data_u32` | `std::vector<uint32_t>` | - | Same bytes as u32 LE |

---

## 4. UartClient (Low-Level)

Thread safety: internal mutex.

### Open/Close

- `bool open(const std::string& port_name, int baudrate = 115200)`
- `bool open(const std::string& port_name, const SerialPortOptions& opts)`
- `void close()`
- `bool isOpen() const`
- `void flushInput()`

### Main Interface (명령 입력/속도 요청)

- `bool sendPcControl(float velocity_mps, float curvature_1pm)`
  - Input: $v$ ($\mathrm{m/s}$), $\kappa$ ($\mathrm{m}^{-1}$).
  - 명령 입력 프레임을 전송합니다. (프로토콜: `0xA5`, 응답 없음)
- `bool requestVehicleSpeed()`
  - 속도 요청을 전송합니다(프로토콜: `0xB3`, 1 byte). 응답은 비동기입니다.

### Auxiliary/Diagnostic (보조 명령)

- `bool requestAllState(uint8_t motor_id)`
- `bool requestBatteryVoltage(uint8_t motor_id = 0)`
- `bool writeSpeedERPM(uint8_t motor_id, float erpm)`
  - Input: electrical RPM.
- `bool writeServoPulseUs(uint8_t motor_id, float pulse_us)`
  - Input: pulse width ($\mu\mathrm{s}$).
- `bool systemReset(uint8_t motor_id)`

### Polling

- `std::optional<Message> poll(int timeout_ms)`
  - `timeout_ms = 0` is non-blocking.
  - Parse internal RX buffer; read from OS buffer if needed.

### Parsing Rules

- Sync on `0xAF` or `0xB3` header.
- `0xB3`: fixed 5 bytes, return `VehicleSpeed`.
- `0xAF`: parse to `AfResponse`, then map to `BatteryVoltage` or `AllState` on known patterns.

---

## 5. SerialPort (Internal)

### 5.1 SerialPortOptions

| Field | Type | Default | Unit | Description |
| --- | --- | --- | --- | --- |
| `baudrate` | `int` | `115200` | $\mathrm{bps}$ | UART baud rate |
| `hw_flow_control` | `bool` | `true` | - | RTS/CTS enabled |
| `rts_always_on` | `bool` | `true` | - | Keep RTS asserted |

### 5.2 SerialPort API

- `bool open(const std::string& port_name, const SerialPortOptions& opts)`
- `bool open(const std::string& port_name, int baudrate)`
- `void close()`
- `bool isOpen() const`
- `void flushInput()`
- `int writeAll(const uint8_t* data, size_t len)`
  - Returns bytes written; -1 on error.
- `int readSome(uint8_t* data, size_t max_len, int timeout_ms)`
  - Returns bytes read; 0 on timeout; -1 on error.

Thread safety: no internal synchronization; guarded by `UartClient` mutex.

---

## 6. Protocol (Frames and Constants)

### Frame IDs (Header Bytes)

| ID | Meaning |
| --- | --- |
| `0xA5` | PC control command frame (velocity + curvature) |
| `0xB3` | Vehicle speed request/response frame |
| `0xAF` | Auxiliary/diagnostic/utility read/write frame |

### Constants

Packet headers (1 byte):

| Name | Value | Description |
| --- | --- | --- |
| `HEADER_GENERAL` | `0xAF` | Auxiliary/diagnostic/utility read/write |
| `HEADER_PC_CONTROL` | `0xA5` | Control command |
| `HEADER_VEHICLE_SPEED` | `0xB3` | Speed request/response |

Read/Write flags:

| Name | Value | Description |
| --- | --- | --- |
| `RW_READ` | `0x00` | Read request |
| `RW_WRITE` | `0x01` | Write request and read response |

Supported 0xAF IDs:

| Name | Value | Description |
| --- | --- | --- |
| `ID_SYSTEM_RESET` | `0x00` | Device reset |
| `ID_SPEED` | `0x03` | Speed command (ERPM) |
| `ID_SERVO_PULSE` | `0x05` | Servo pulse width ($\mu\mathrm{s}$) |
| `ID_ALL_STATE` | `0x06` | All-state |
| `ID_BATTERY_VOLTAGE` | `0x07` | Battery voltage |

Size limits:

| Name | Value | Description |
| --- | --- | --- |
| `MAX_IDS` | `16` | Max IDs per 0xAF frame |
| `ALL_STATE_FIELD_COUNT` | `9` | All-state field count |

### Frame Formats (little-endian)

All float values are float32 little-endian.

`0xA5` control frame:

```
[0]  HEADER_PC_CONTROL (0xA5)
[1]  velocity_mps (float32 LE, m/s)
[5]  curvature_1pm (float32 LE, m^-1)
```

`0xB3` request/response:

- Request: single byte `HEADER_VEHICLE_SPEED`.
- Response: 5 bytes `header + float32 speed`.

```
[0]  HEADER_VEHICLE_SPEED (0xB3)
[1]  speed_mps (float32 LE, m/s)
```

`0xAF` (auxiliary/diagnostic read/write):

```
[0]  HEADER_GENERAL (0xAF)
[1]  motor_id
[2]  RW_READ or RW_WRITE
[3]  n_id
[4..] ids[0..n_id-1]
[4+n_id..] data_f32[0..n_id-1] (only when RW_WRITE)
```

Notes:

- Read requests: `RW_READ`, no data.
- Read responses: `RW_WRITE`, include data.

### Helpers

- `appendU8(out, v)`
- `appendU32LE(out, v)`
- `readU32LE(p)`
- `appendFloatLE(out, value)`
- `readFloatLE(p)`

---

## 7. Notes

- All floats are float32 little-endian.
- Writes are best-effort; no ACKs.

---

## 8. Minimal Usage

Driver (v, omega):

```cpp
KMC_HARDWARE::Driver drv;
KMC_HARDWARE::Driver::Options opt;
opt.port = "/dev/ttyKMC";
opt.control_rate_hz = 100.0;
drv.start(opt);
drv.setCommand(0.5f, 0.2f); // v=0.5 m/s, omega=0.2 rad/s
```

Driver (v, curvature):

```cpp
drv.setCommandCurvature(0.5f, 0.4f); // k = 0.4 m^-1
```

UartClient:

```cpp
KMC_HARDWARE::UartClient client;
client.open("/dev/ttyKMC", 115200);
client.sendPcControl(0.5f, 0.4f); // v, curvature
client.requestVehicleSpeed();
if (auto msg = client.poll(50)) {
  if (auto* vs = std::get_if<KMC_HARDWARE::VehicleSpeed>(&*msg)) {
    // use vs->mps
  }
}
```
