# TactileIPC3D 架构说明

## UML (Mermaid)

```mermaid
classDiagram
    class AppBackend {
        +bool lightMode
        +string language
        +bool connected
        +SerialBackend* serial
        +DataBackend* data
        +int rangeMin
        +int rangeMax
        +QColor colorLow
        +QColor colorMid
        +QColor colorHigh
        +setLightMode(bool)
        +setLanguage(string)
        +setRangeMin(int)
        +setRangeMax(int)
        +setColorLow(QColor)
        +setColorMid(QColor)
        +setColorHigh(QColor)
    }

    class SerialConfig {
        +string portName
        +int baudRate
        +int dataBits
        +int stopBits
        +string parity
        +uint8 deviceAddress
        +DeviceMode mode
        +int pollIntervalMs
    }

    class SensorRequest {
        +uint8 functionCode
        +uint32 startAddress
        +uint16 dataLength
    }

    class SensorSpec {
        +string model
        +string version
        +int rows
        +int cols
        +float pitch
        +float dotRadius
        +float rangeMin
        +float rangeMax
    }

    class SerialBackend {
        +string portName
        +int baudRate
        +int pollIntervalMs
        +int deviceAddress
        +string mode
        +int requestFunction
        +int requestStartAddress
        +int requestLength
        +string protocol
        +bool connected
        +open()
        +close()
        +requestOnce()
        +feedBytes(bytes)
        +setTransport(transport)
    }
    class GLWidget {
        +setRange(int, int)
        +setColorLow(QColor)
        +setColorMid(QColor)
        +setColorHigh(QColor)
        +dotClicked(index, row, col, value)
    }

    class SerialManager {
        +registerProtocol(name, bundle)
        +setActiveProtocol(name)
        +activeBundle()
    }

    class ISerialTransport {
        <<interface>>
        +open(config, error)
        +close()
        +writeBytes(data, error)
    }

    class QtSerialTransport {
        +open(config, error)
        +close()
        +writeBytes(data, error)
    }

    class ISerialFormat {
        <<interface>>
        +tryParse(buffer, packet, error)
    }

    class ISerialCodec {
        <<interface>>
        +buildRequest(config, request)
        +buildGetVersionRequest(config)
        +buildGetSpecRequest(config)
    }

    class ISerialDecoder {
        <<interface>>
        +decodeFrame(packet, frame)
        +decodeSpec(packet, spec)
    }

    class PiezoresistiveAFormat
    class PiezoresistiveACodec
    class PiezoresistiveADecoder

    class DataBackend {
        +ingestFrame(frame)
        +clear()
        +exportJson(path)
        +exportCsv(path)
        +importJson(path)
        +importCsv(path)
        +startPlayback(intervalMs)
        +stopPlayback()
        +setLiveRenderCallback(cb)
        +setPlaybackRenderCallback(cb)
    }

    class DataFrame {
        +string pts
        +uint8 functionCode
        +float[] data
    }

    class PacketQueue {
        +push(packet)
        +pop()
        +clear()
        +stop()
    }

    class FrameQueue {
        +push(frame)
        +pop()
        +clear()
        +stop()
    }

    class SerialReadThread {
        +enqueueBytes(bytes)
        +setParseFunc(func)
        +start()
        +stop()
    }

    class SerialDecodeThread {
        +setDecodeFunc(func)
        +start()
        +stop()
    }

    class SerialSendWorker {
        +setTransport(transport)
        +setBuildRequestFunc(func)
        +openTransport(config)
        +closeTransport()
        +requestOnce()
    }

    AppBackend --> SerialBackend
    AppBackend --> DataBackend
    AppBackend ..> GLWidget : render config
    SerialBackend --> SerialConfig
    SerialBackend --> SensorRequest
    SerialBackend --> SensorSpec
    SerialBackend --> SerialManager
    SerialManager --> ISerialFormat
    SerialManager --> ISerialCodec
    SerialManager --> ISerialDecoder
    ISerialFormat <|.. PiezoresistiveAFormat
    ISerialCodec <|.. PiezoresistiveACodec
    ISerialDecoder <|.. PiezoresistiveADecoder
    ISerialTransport <|.. QtSerialTransport
    SerialBackend --> DataFrame
    DataBackend --> DataFrame
    SerialBackend --> PacketQueue
    SerialBackend --> FrameQueue
    SerialBackend --> SerialReadThread
    SerialBackend --> SerialDecodeThread
    SerialBackend --> SerialSendWorker
    SerialSendWorker --> ISerialTransport
    SerialReadThread --> PacketQueue
    SerialDecodeThread --> PacketQueue
    SerialDecodeThread --> FrameQueue
```

说明：线程与队列已实现，队列溢出策略通过 TODO 注释预留后续扩展。

## 数据流/线程流程 (Mermaid)

```mermaid
flowchart LR
    UI[QML 按钮/配置] -->|open/close| SB[SerialBackend]
    SB -->|openTransport| SW[SerialSendWorker]
    SW -->|open| TP[QtSerialTransport]
    SW -->|slave 模式轮询| REQ[buildRequest]
    REQ -->|writeBytes| TP
    TP -->|bytesReceived| RT[SerialReadThread]
    RT -->|tryParse| PKT[PacketQueue]
    PKT --> DT[SerialDecodeThread]
    DT -->|decodeFrame| FR[FrameQueue]
    FR --> SB
    SB -->|drainFrames| DB[DataBackend]
    DB -->|liveCallback| GL[GLWidget]
    DB -->|TODO| RP[RightPanel 可视化]
```

## 时序/函数调用 (Mermaid)

```mermaid
sequenceDiagram
    participant UI as QML
    participant SB as SerialBackend
    participant SW as SerialSendWorker
    participant TP as ISerialTransport
    participant RT as SerialReadThread
    participant DT as SerialDecodeThread
    participant DB as DataBackend
    participant GL as GLWidget

    UI->>SB: open()
    SB->>SW: openTransport(config)
    SW->>TP: open(config)
    Note over SW: slave 模式启动轮询定时器
    SW->>SW: buildRequest()
    SW->>TP: writeBytes(request)
    TP-->>SW: bytesReceived(data)
    SW-->>RT: enqueueBytes(data)
    RT->>RT: tryParse(buffer)
    RT-->>DT: PacketQueue.push(packet)
    DT->>DT: decodeFrame(packet)
    DT-->>SB: frameAvailable()
    SB->>DB: ingestFrame(frame)
    DB-->>GL: liveRenderCallback(frame)
```

## 配置流程 (Mermaid)

```mermaid
flowchart TD
    UI[QML 设置参数] -->|setPortName/ setBaudRate/ setDeviceAddress/ setMode/ setPollIntervalMs| SB[SerialBackend]
    UI -->|setRequestFunction/ setRequestStartAddress/ setRequestLength| SB
    UI -->|setProtocol| SB
    SB -->|syncSendConfig_| SW[SerialSendWorker.setConfig]
    SB -->|syncSendRequest_| SW2[SerialSendWorker.setRequest]
    SB -->|updateProtocolBindings_| RT[SerialReadThread.setParseFunc]
    SB -->|updateProtocolBindings_| DT[SerialDecodeThread.setDecodeFunc]
    SB -->|updateProtocolBindings_| SW3[SerialSendWorker.setBuildRequestFunc]
```

## 渲染/颜色映射流程 (Mermaid)

```mermaid
flowchart LR
    UI[LeftPanel 颜色映射] -->|rangeMin/rangeMax<br/>colorLow/Mid/High| AB[AppBackend]
    AB -->|rangeChanged/colorChanged| GL[GLWidget]
    GL -->|uMinV/uMaxV/uColorLow/Mid/High| SH[dots.frag]
```

## 点阵凹陷渲染（Heightmap / Dented Surface）

### 目标
- 将 `rows x cols` 点阵扩展到“长/宽各 100 倍”的显示分辨率（例如 12x7 -> 1200x700）。
- 数值越大，顶面越向内凹陷，且边缘与侧面跟随变形，保持整体感。
- 可在 OpenGL 视图内切换“普通点阵”和“凹陷热力图”两种渲染模式。

### 数据处理
1) 传感器帧数据进入 `GLWidget::submitValues()`。
2) `GLWidget::updateHeatmapData_()` 将 `values` 组装为 `CV_32F` 矩阵（rows x cols）。
3) 通过 `GlobalHelper::transToMultiMatrix(...)`：
   - 低阈值清零 -> 归一化 -> 轻度模糊 -> resize 到 `cols*100 x rows*100` -> 再模糊。
   - 仅输出“数据矩阵”，不再用于生成图像。
4) 将结果写入 `m_heightValues`，上传 `GL_R32F` 纹理。

### 凹陷算法
- 顶面位移（vertex shader）：
  - `t = clamp((v - min) / (max - min), 0..1)`
  - `h = t * dentMax`
  - `z = baseZ + h`（向内凹陷）
- 法线估算（fragment shader）：
  - 对 heightmap 相邻 texel 采样，计算梯度，再归一化为法线。
- 侧边联动：
  - `initSkirtGeometry_()` 建立边界环。
  - `updateSkirtVertices_()` 采样边界高度，更新侧边顶点，使边缘与顶面同步下沉。

### 模式切换
- `GLWidget` 内部维护 `m_useHeatmap`。
- 使用 `QPainter` 在 OpenGL 视图左上角绘制按钮（Dots / Heatmap）。
- 点击按钮切换渲染路径：
  - `Dots`：原点阵实例化圆点渲染。
  - `Heatmap`：底面 + 侧边 + 顶面凹陷渲染。

### UML (Mermaid)
```mermaid
classDiagram
    class GLWidget {
        +submitValues(values)
        +setRange(min,max)
        +setColorLow/Mid/High()
        -updateHeatmapData_()
        -uploadHeightTexture_()
        -renderHeatmap_()
        -renderSkirt_()
        -renderBase_()
    }
    class GlobalHelper {
        +transToMultiMatrix(raw,min,range,display,out)
    }
    class HeightTexture {
        +GL_R32F
        +width/height
    }
    class HeatmapMesh {
        +VAO/VBO/IBO
        +u,v grid
    }
    class SkirtMesh {
        +border uv
        +dynamic VBO
    }
    class Shaders {
        +heatmap.vert/frag
        +base.vert/frag
    }

    GLWidget --> GlobalHelper
    GLWidget --> HeightTexture
    GLWidget --> HeatmapMesh
    GLWidget --> SkirtMesh
    GLWidget --> Shaders
```

## 配置接口与步骤说明

- 设置协议：
  - `SerialBackend::setProtocol(name)`：切换协议后调用 `updateProtocolBindings_()`，更新 `ParseFunc` / `DecodeFunc` / `BuildRequestFunc`。
- 设置串口配置（config）：
  - `SerialBackend::setPortName(name)`
  - `SerialBackend::setBaudRate(rate)`
  - `SerialBackend::setDeviceAddress(addr)`（0-255）
  - `SerialBackend::setMode("master"/"slave")`
  - `SerialBackend::setPollIntervalMs(intervalMs)`（从站模式轮询周期）
  - 上述接口内部统一调用 `syncSendConfig_()`，将 `SerialConfig` 下发到 `SerialSendWorker::setConfig`。
- 设置请求参数（request）：
  - `SerialBackend::setRequestFunction(func)`
  - `SerialBackend::setRequestStartAddress(addr)`
  - `SerialBackend::setRequestLength(len)`
  - 上述接口内部调用 `syncSendRequest_()`，将 `SensorRequest` 下发到 `SerialSendWorker::setRequest`。
- 设置解码/解析器：
  - `SerialManager::registerProtocol(name, {codec, decoder, format})`
  - `SerialBackend::setProtocol(name)` 触发 `updateProtocolBindings_()`：
    - `SerialReadThread::setParseFunc(format->tryParse)`
    - `SerialDecodeThread::setDecodeFunc(decoder->decodeFrame)`
    - `SerialSendWorker::setBuildRequestFunc(codec->buildRequest)`
- 打开串口：
  - `SerialBackend::open()` -> `SerialSendWorker::openTransport(config)`，成功后在从站模式启动轮询发送。
- 渲染/颜色映射配置：
  - QML 绑定 `AppBackend::rangeMin/rangeMax` 与 `colorLow/Mid/High`（`LeftPanel` 中的颜色映射面板）。
  - `setRangeMin/Max` 发出 `rangeChanged(min, max)`，由 `GLWidget::setRange` 同步到 `uMinV/uMaxV`。
  - `setColorLow/Mid/High` 发出 `color*Changed`，由 `GLWidget::setColor*` 同步到 `uColorLow/Mid/High`。
  - `dots.frag` 归一化公式：`value01 = clamp((v - min) / (max - min))`，并用 low->mid->high 线性插值。

## 分层设计概述

- AppBackend：统一后端入口，驱动串口层与数据层，供 QML 直接绑定。
- 串口采集层：`Transport + Format + Codec + Decoder + Manager` 分层，独立于业务逻辑。
- 串口线程化：读取/解码/发送三线程 + Packet/Frame 队列，降低 UI 卡顿风险。
- 数据驱动层：负责帧缓存、数据导入导出与回放，提供渲染回调。
- 渲染配置层：`AppBackend` 提供范围/颜色参数，`GLWidget` 将其转为 shader uniform 进行颜色映射。
- UI 层：`NavBar + LeftPanel + OpenGL View + RightPanel` 的 1+3 布局。

## 类接口与成员说明（C++）

### AppBackend (`src/backend.h`)
- 作用：统一后端入口，串联 `SerialBackend` 与 `DataBackend`。
- 属性/接口：
  - `lightMode` / `language` / `connected`：UI 基础状态。
  - `serial()` / `data()`：暴露子系统实例给 QML。
  - `setLightMode(bool)` / `setLanguage(string)`。
  - `rangeMin` / `rangeMax`：颜色映射的数值范围。
  - `colorLow` / `colorMid` / `colorHigh`：颜色映射的三个基准颜色。
  - `setRangeMin(int)` / `setRangeMax(int)` / `setColorLow(QColor)` / `setColorMid(QColor)` / `setColorHigh(QColor)`。
- 成员变量：
  - `m_serial`：串口采集层对象。
  - `m_data`：数据驱动层对象。
  - `m_lightMode` / `m_language`：全局 UI 状态。
  - `m_rangeMin` / `m_rangeMax` / `m_colorLow` / `m_colorMid` / `m_colorHigh`：颜色映射参数。
- 备注：串口连接成功后会清空历史数据缓存，避免旧数据残留。
  - `rangeChanged(min, max)` 用于驱动 `GLWidget::setRange`，颜色变更通过 `color*Changed` 同步。

### SerialBackend (`src/serial/serial_backend.h`)
- 作用：串口采集层的统一控制器，负责协议选择、三线程调度与数据分发。
- 属性/接口：
  - `portName` / `baudRate` / `deviceAddress` / `mode` / `pollIntervalMs`：串口与模式配置。
  - `requestFunction` / `requestStartAddress` / `requestLength`：请求参数。
  - `protocol` / `sensorModel` / `sensorGrid`：协议与传感器规格占位。
  - `open()` / `close()` / `requestOnce()` / `feedBytes(bytes)`。
  - `setTransport(transport)`：注入真实串口传输层。
  - `requestBuilt(bytes)`：输出原始请求帧，便于调试。
- 成员变量：
  - `m_config`：串口参数配置。
  - `m_request`：请求参数。
  - `m_spec`：传感器规格占位。
  - `m_manager`：协议注册与切换。
  - `m_packetQueue` / `m_frameQueue`：包/帧队列。
  - `m_readThread` / `m_decodeThread`：读取与解码线程。
  - `m_sendWorker` / `m_sendThread`：发送线程与 worker。
  - `m_frameCallback`：解码后数据回调。
- 备注：通过 `ParseFunc` / `DecodeFunc` / `BuildRequestFunc` 绑定协议实现，模拟 “find_decodec 后回调赋值”。

### PacketQueue / FrameQueue (`src/serial/serial_queue.h`)
- 作用：线程安全的阻塞队列，用于 Packet/Frame 的跨线程传递。
- 接口：
  - `push(item)` / `pop(item)` / `tryPop(item)` / `clear()` / `stop()` / `reset()`
  - `setMaxSize(size)`：配置队列容量。
- 备注：队列溢出策略已通过 TODO 注释预留，后续可扩展为丢弃最新/阻塞等待。

### SerialReadThread (`src/serial/serial_threads.h`)
- 作用：读取线程，接收原始字节流并根据 `ParseFunc` 解析为 packet。
- 接口：
  - `enqueueBytes(bytes)`：注入串口字节流。
  - `setParseFunc(func)`：绑定协议解析函数。
  - `start()` / `stop()`：线程控制。

### SerialDecodeThread (`src/serial/serial_threads.h`)
- 作用：解码线程，从 `PacketQueue` 解码 packet 并写入 `FrameQueue`。
- 接口：
  - `setDecodeFunc(func)`：绑定协议解码函数。
  - `start()` / `stop()`：线程控制。

### SerialSendWorker (`src/serial/serial_threads.h`)
- 作用：发送线程 worker，负责请求编码与发送，并在从站模式下轮询。
- 接口：
  - `setTransport(transport)`：注入传输层实例。
  - `setBuildRequestFunc(func)`：绑定请求编码函数。
  - `openTransport(config)` / `closeTransport()`：串口打开/关闭。
  - `requestOnce()`：触发一次请求发送。

### SerialManager (`src/serial/serial_manager.h`)
- 作用：协议包管理器，注册 `codec/decoder/format` 组合。
- 接口：
  - `registerProtocol(name, bundle)`：注册协议。
  - `setActiveProtocol(name)`：切换协议。
  - `activeBundle()`：获取当前协议绑定。
- 成员变量：
  - `m_protocols`：协议字典。
  - `m_activeName`：当前协议名。

### 协议插件化（`plugins/decoders`）
- 目标：每个协议作为独立 DLL 插件，可按需安装与动态加载。
- 关键接口：
  - `IProtocolPlugin`：暴露 `protocolName()` / `apiVersion()` / `createBundle()`。
  - `ProtocolBundle`：`codec/decoder/format` 三件套。
- 加载流程：
  - `SerialBackend` 启动时调用 `SerialManager::loadPlugins()`。
  - `SerialManager` 扫描 `appDir/plugins/decoders` 目录，按平台过滤后缀（Windows: `.dll`）。
  - 校验 `apiVersion`，创建并注册协议 bundle。
  - 通过 `m_pluginLoaders` 保持插件常驻，避免对象失效。

#### 插件 UML (Mermaid)
```mermaid
classDiagram
    class IProtocolPlugin {
        +protocolName()
        +apiVersion()
        +createBundle()
    }
    class ProtocolBundle {
        +codec
        +decoder
        +format
    }
    class SerialManager {
        +loadPlugins(dir)
        +registerProtocol()
        +setActiveProtocol()
    }
    class SerialBackend {
        +initPlugins()
    }

    SerialBackend --> SerialManager
    SerialManager --> IProtocolPlugin
    IProtocolPlugin --> ProtocolBundle
```

### ISerialTransport (`src/serial/serial_transport.h`)
- 作用：串口传输抽象层，屏蔽不同平台差异。
- 接口：
  - `open(config, error)` / `close()` / `writeBytes(data, error)`。
- 备注：实际接收数据通过 `bytesReceived` 信号上报。
- 实现：
  - `QtSerialTransport`：基于 `QSerialPort` 的默认传输实现。
  - TODO:待实现内容(在传输层支持软/硬件流控配置)

### ISerialFormat / ISerialCodec / ISerialDecoder
- 作用：协议拆分层，类比 FFmpeg 的 `format/codec/decoder`。
- ISerialFormat 接口：
  - `tryParse(buffer, packet, error)`：从字节流中提取完整包。
- ISerialCodec 接口：
  - `buildRequest(config, request)`：生成请求帧。
  - `buildGetVersionRequest(config)`：// TODO:待实现内容(构建获取版本号的请求帧)
  - `buildGetSpecRequest(config)`：// TODO:待实现内容(构建获取传感器规格的请求帧)
- ISerialDecoder 接口：
  - `decodeFrame(packet, frame)`：解码回复帧。
  - `decodeSpec(packet, spec)`：// TODO:待实现内容(解析规格回复帧并填充 SensorSpec)

### Piezoresistive A 协议 (`src/serial/piezoresistive_a_protocol.*`)
- `PiezoresistiveAFormat`：包解析与 CRC-8/ITU 校验。
- `PiezoresistiveACodec`：构建请求帧。
- `PiezoresistiveADecoder`：解析回复帧为 `DataFrame`。
- 备注：数据以小端 `uint16` 解析为 float，若协议变更可在此调整。

### DataBackend (`src/data_backend.h`)
- 作用：数据驱动层，负责帧缓存、导入导出与回放。
- 接口：
  - `ingestFrame(frame)`：实时采集数据入口。
  - `clear()`：清空缓存。
  - `exportJson(path)` / `exportCsv(path)`。
  - `importJson(path)` / `importCsv(path)`。
  - `startPlayback(intervalMs)` / `stopPlayback()`。
  - `setLiveRenderCallback(cb)` / `setPlaybackRenderCallback(cb)`：渲染回调占位。
- 成员变量：
  - `m_frames`：帧数据容器。
  - `m_playbackTimer` / `m_playbackIndex`：回放控制。

### GLWidget (`src/glwidget.h`)
- 作用：OpenGL 渲染窗口，显示传感器点阵与背景。
- 接口：
  - `setRange(int minV, int maxV)`：设置 `uMinV/uMaxV`。
  - `setColorLow(QColor)` / `setColorMid(QColor)` / `setColorHigh(QColor)`：设置 `uColorLow/uColorMid/uColorHigh`。
- 信号：
  - `dotClicked(index, row, col, value)`：鼠标点击某个点时发出索引与数据值。
- 备注：
  - 颜色映射在 `dots.frag` 内完成，低/中/高三段线性插值。
  - 拾取使用屏幕投影 + 半径阈值，便于 RightPanel 订阅点击事件做曲线展示。

### DataFrame (`src/data_frame.h`)
- 字段：
  - `pts`：`yyyyMMddhhmmsszzz` 时间戳。
  - `functionCode`：功能码。
  - `data`：传感器数据。

### SerialConfig / SensorRequest / SensorSpec (`src/serial/serial_types.h`)
- `SerialConfig`：串口基础参数配置。
- `SensorRequest`：请求参数（功能码、起始地址、读取长度）。
- `SensorSpec`：传感器规格占位（型号/网格/量程等）。

## 协议结构说明（压阻 A 型）

- Request 起始符：`0x55AA`（小端 -> `AA 55`）。
- Reply 起始符：`0x55AA`（小端 -> `AA 55`）。
- 数据长度：从 `data[4]` 到 payload 末尾（不含 CRC）。
- CRC：CRC-8/ITU（多项式 0x07，初始值 0x00）。

## QML 层级与组件职责

### 总体布局（`qml/content/App.qml`）
- 顶部 `NavBar`：标题、连接状态、明暗切换、语言选择。
- 中部三栏：
  - 左侧 `LeftPanel`：串口连接、采样参数、规格与显示控制。
  - 中间 OpenGL 视图：由 C++ `GLWidget` 挂载显示 3D 传感器数据，并提供点点击信号。
  - 右侧 `RightPanel`：折线趋势、指标卡片、会话信息。

### NavBar（`qml/content/NavBar.qml`）
- Title：软件名称显示。
- 连接指示：绿/红状态灯 + 文本。
- `Switch`：Light/Dark 切换。
- `ComboBox`：语言选择。

### LeftPanel（`qml/content/LeftPanel.qml`）
- 连接设置：
  - COM 端口、波特率。
  - 模式选择（主站/从站）。
  - 设备地址输入（十六进制 `0x01` 形式）。
  - 采样周期（从站模式可用）。
- 采样参数：功能码、起始地址、读取长度。
- 传感器规格：协议名、型号、网格规格占位。
- 颜色映射：
  - 数值范围（min/max）。
  - 低/中/高三色选择（`ColorDialog`）。
- 显示控制：显示网络/坐标轴、回放与导出入口。

### RightPanel（`qml/content/RightPanel.qml`）
- Live Trend：折线趋势图示例。
- Metrics：峰值、RMS、均值、Delta 等指标卡片。
- Session：帧数、回放状态。
- LiveTrendCard：封装 `SparklinePlot`（C++ QQuickItem）用于趋势绘制，数据接入留有 TODO。

## 预留扩展（实现建议）

- 串口线程化已落地：
  - `SerialReadThread` 读取字节流 -> `PacketQueue`。
  - `SerialDecodeThread` 解包/解码 -> `FrameQueue`。
  - `SerialSendWorker` 从站模式下定时发送请求。
- 队列溢出策略：`SerialQueue` 中已预留 TODO 注释，可扩展为阻塞等待或丢弃最新。
- 回调扩展：
  - `DataBackend` 的渲染回调用于自定义 OpenGL/曲线刷新逻辑。
- 可视化接入：
  - `main.cpp` 与 `qml/content/RightPanel.qml` 已标注 // TODO:待实现内容(将实时帧推送到右侧曲线/指标)。
- 协议扩展：
  - `buildGetVersionRequest` / `buildGetSpecRequest` / `decodeSpec` 需实现，已保留 // TODO:待实现内容。


## 数据导出进度

- 已完成
  - `DataBackend::exportJson(path)` / `exportCsv(path)` 已实现导出。
  - `DataBackend::importJson(path)` / `importCsv(path)` 已实现导入。
  - `qml/content/SaveAsExportDialog.qml` 提供路径、文件名、格式、方式选择，并通过 `saveTo(...)` 抛出导出参数。
  - 导出对话框以独立 `Window` 方式呈现（ApplicationModal），避免在 `QQuickWidget` 内被裁剪。
- 待开发
  - 将 `saveTo` 与 `DataBackend` 导出接口打通，并补充失败提示。
  - 覆盖/追加/压缩（zip）策略与文件存在检测流程。
  - `xlsx` 导出实现与导出图标资源补齐（TODO）。

## TODO 汇总

- 代码/界面
  - `main.cpp:106` 将 frame 数据分发给右侧曲线/指标的 QML 接口。
  - `qml/content/RightPanel.qml:40` 用 DataBackend 输出的帧更新折线图/指标。
  - `src/serial/serial_queue.h:28` 指定队列溢出策略（丢弃最新/丢弃最旧/阻塞等待）。
  - `src/serial/serial_qt_transport.cpp:72` 根据 SerialConfig 扩展软件/硬件流控配置。
  - `src/serial/serial_codec.h:17` 构建获取版本号的请求帧。
  - `src/serial/serial_codec.h:19` 构建获取传感器规格的请求帧。
  - `src/serial/serial_decoder.h:18` 解析规格回复帧并填充 SensorSpec。
  - `src/serial/piezoresistive_a_protocol.cpp:126` 压阻 A 型版本号查询请求帧。
  - `src/serial/piezoresistive_a_protocol.cpp:132` 压阻 A 型规格查询请求帧。
  - `src/serial/piezoresistive_a_protocol.cpp:222` 解析压阻 A 型规格回复并写入 SensorSpec。
- 文档/流程图标注
  - `docs/ARCHITECTURE.md:187` 队列溢出策略 TODO 预留说明。
  - `docs/ARCHITECTURE.md:205` 数据流图 RightPanel 可视化 TODO。
  - `docs/ARCHITECTURE.md:323` 队列溢出策略 TODO 备注。
  - `docs/ARCHITECTURE.md:363` 传输层软/硬件流控配置 TODO。
  - `docs/ARCHITECTURE.md:371` buildGetVersionRequest TODO。
  - `docs/ARCHITECTURE.md:372` buildGetSpecRequest TODO。
  - `docs/ARCHITECTURE.md:375` decodeSpec TODO。
  - `docs/ARCHITECTURE.md:449` LiveTrendCard 数据接入 TODO。
  - `docs/ARCHITECTURE.md:457` SerialQueue 溢出策略 TODO。
  - `docs/ARCHITECTURE.md:461` main.cpp/RightPanel.qml 实时帧推送 TODO。
  - `docs/ARCHITECTURE.md:463` buildGetVersion/buildGetSpec/decodeSpec TODO。
  - `docs/ARCHITECTURE.md:520` 更新记录提及可视化 TODO。
  - `docs/ARCHITECTURE.md:522` 更新记录提及队列溢出 TODO。
- 测试/第三方
  - `test/onlygl/stb_image.h:1276` move stbi__convert_format to here.
  - `test/onlygl/stb_image.h:1302` move stbi__convert_format16 to here.
  - `test/onlygl/stb_image.h:1303` special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision.
  - `test/onlygl/stb_image.h:5898` tga_x_origin @TODO。
  - `test/onlygl/stb_image.h:5899` tga_y_origin @TODO。
- 其他/说明
  - `Prompt.md:60` 可视化部分流出 TODO。
  - `Prompt.md:72` 接口预留位置标注 TODO。
  - `Prompt.md:73` 文档和 TODO 说明。
  - `serial/doc/Doxyfile:603` Doxygen TODO 列表说明。
  - `serial/doc/Doxyfile:607` GENERATE_TODOLIST 开关。

## 更新记录

- 2026-01-20：协议解码插件化（DLL），SerialBackend 启动时动态加载 `plugins/decoders`，安装器支持按组件选择协议插件。
- 2026-01-11：新增 `QtSerialTransport`（`QSerialPort` 传输实现）并设为默认传输；补齐点选拾取逻辑；新增数据流/时序图并补充可视化 TODO 说明。
- 2026-01-12：`LeftPanel` 新增颜色映射参数；`AppBackend`/`GLWidget` 增加颜色与范围接口，shader 使用三色渐变映射数据值。
- 2026-01-11：补充串口配置流程图与配置接口说明（协议/参数/解码器绑定/打开流程）。
- 2026-01-05：新增串口三线程流水线（读/解码/发送）与 Packet/Frame 队列，更新协议起始符说明，补充队列溢出 TODO 与线程组件文档。
- 2026-01-05：CollapsiblePanel 组件改为跟随 `backend.lightMode` 切换暗色主题配色。
- 2026-01-05：`QSplitter` 句柄配色跟随明暗模式并缩窄，消除 darkmode 下的白色缝隙。
- 2026-01-05：`QQuickWidget` clearColor 跟随明暗模式，避免面板圆角处漏出白底。
- 2026-01-05：`QQuickWidget` 在 `setSource` 后重设 `backend` 上下文属性，修复 QML 访问空指针报错。
- 2026-01-05：`QQuickWidget` 改为通过 `engine()->rootContext()` 注入 `backend`，避免上下文被重建导致为空。
- 2026-01-05：改为每个 `QQuickWidget` 引擎注入 `Backend` 上下文属性，避免多引擎使用单例导致的报错。
- 2026-01-05：调整 `SerialSendWorker` 生命周期清理方式，避免跨线程 moveToThread 警告。
- 2026-01-06：多个 `QQuickWidget` 共享同一个 `QQmlEngine`，统一注入 `Backend/backend` 上下文属性以稳定访问。
- 2026-01-06：统一 QML 使用 `backend` 上下文属性名称，避免大写标识被当成类型解析导致取值为空。
- 2026-01-06：为每个 `QQuickWidget` 的 `rootContext()` 重复绑定 `backend`，避免上下文被重建时丢失。
- 2026-01-06：`QQuickWidget` 改用 `QQmlComponent + QQmlContext` 创建根对象并 `setContent`，确保 `backend` 上下文稳定注入。
- 2026-01-07：`GLWidget` 增加点阵点击拾取信号 `dotClicked`，文档补充 OpenGL 交互说明。
