Files
XYParser/XYParserDataFlow.md
2026-06-09 22:28:52 +08:00

432 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# XYParser 数据流与接口时序说明
## 接口时序图
### 3.1 64导初始化连接阶段
```mermaid
%%{init: {'theme': 'default', 'sequence': {'diagramMarginX': 80, 'diagramMarginY': 30, 'actorMargin': 80, 'width': 220, 'height': 80, 'messageMargin': 35}}}%%
sequenceDiagram
participant Dev as 64导EEG采集设备
participant Host as 上位机
participant Lib as XYParser库
Dev-->>Host: 设备连接成功
Host->>Lib: parser64 = XYParser_CreateParser(64)
Host->>Lib: XYParser_SetAdcParams(parser64, 4.5, 6.0)
Host->>Lib: XYParser_SetSampleRate(parser64, 250)
Host->>Lib: XYParser_SetBypassChecksum(parser64, 1)
Host->>Lib: gain_cmd_size = XYParser_Get64GainSampleRateCommandSize()
Lib-->>Host: gain_cmd_size
Host->>Lib: gain_cmd_bytes = XYParser_Serialize64GainSampleRateCommand(6, 250, gain_cmd_buf, gain_cmd_size)
Lib-->>Host: gain_cmd_bytes
Host->>Dev: 下发采样率250、增益6命令
```
### 3.2 64导阻抗阶段
```mermaid
%%{init: {'theme': 'default', 'sequence': {'diagramMarginX': 80, 'diagramMarginY': 30, 'actorMargin': 80, 'width': 220, 'height': 80, 'messageMargin': 35}}}%%
sequenceDiagram
participant Dev as 64导EEG采集设备
participant Host as 上位机
participant Lib as XYParser库
Host->>Lib: XYParser_SetImpedanceDetection(parser64, 1)
Host->>Lib: impedance_gain_cmd_size = XYParser_Get64GainSampleRateCommandSize()
Lib-->>Host: impedance_gain_cmd_size
Host->>Lib: impedance_gain_cmd_bytes = XYParser_Serialize64GainSampleRateCommand(24, 250, impedance_gain_cmd_buf, impedance_gain_cmd_size)
Lib-->>Host: impedance_gain_cmd_bytes
Host->>Dev: 下发采样率250、增益24命令
Host->>Lib: impedance_cmd_size = XYParser_Get64ImpedanceCommandSize()
Lib-->>Host: impedance_cmd_size
Host->>Lib: open_impedance_bytes = XYParser_Serialize64ImpedanceCommand(1, impedance_cmd_buf, impedance_cmd_size)
Lib-->>Host: open_impedance_bytes
Host->>Dev: 下发阻抗开启命令
loop 持续获取阻抗
Dev-->>Host: 原始EEG字节流
Host->>Lib: frame_count = XYParser_Feed(parser64, raw_data, raw_size, frame_summaries, max_frames)
Lib-->>Host: frame_count + frame_summaries
Host->>Lib: impedance_count = XYParser_ReadImpedance(parser64, impedance_summaries, max_impedance)
Lib-->>Host: impedance_count + impedance_summaries
end
Host->>Lib: close_impedance_bytes = XYParser_Serialize64ImpedanceCommand(0, impedance_cmd_buf, impedance_cmd_size)
Lib-->>Host: close_impedance_bytes
Host->>Dev: 下发阻抗关闭命令
Host->>Lib: restore_gain_cmd_size = XYParser_Get64GainSampleRateCommandSize()
Lib-->>Host: restore_gain_cmd_size
Host->>Lib: restore_gain_cmd_bytes = XYParser_Serialize64GainSampleRateCommand(6, 250, restore_gain_cmd_buf, restore_gain_cmd_size)
Lib-->>Host: restore_gain_cmd_bytes
Host->>Dev: 下发采样率250、增益6命令
Host->>Lib: XYParser_SetImpedanceDetection(parser64, 0)
```
### 3.3 64导算法阶段
```mermaid
%%{init: {'theme': 'default', 'sequence': {'diagramMarginX': 80, 'diagramMarginY': 30, 'actorMargin': 80, 'width': 220, 'height': 80, 'messageMargin': 35}}}%%
sequenceDiagram
participant Dev as 64导EEG采集设备
participant Host as 上位机
participant Lib as XYParser库
participant Algo as 算法
Host->>Lib: XYParser_SetWelchDetection(parser64, 1)
loop 持续采集
Dev-->>Host: 原始EEG字节流
Host->>Lib: frame_count = XYParser_Feed(parser64, raw_data, raw_size, frame_summaries, max_frames)
Lib-->>Host: frame_count + frame_summaries
Host->>Lib: value_count = XYParser_GetAlgorithmDataValueCount()
Lib-->>Host: value_count
Host->>Lib: ok = XYParser_ConvertSampleFramesToAlgorithmData(frame_summary, algorithm_input_data)
Lib-->>Host: ok + algorithm_input_data
Host->>Algo: 输入算法数据 algorithm_input_data
Algo-->>Host: 算法输出数据 algorithm_output_bytes
Host->>Lib: alg_frame_count = XYParser_FeedAlgorithmData(parser64, algorithm_output_bytes, algorithm_output_size, algorithm_frames, max_algorithm_frames)
Lib-->>Host: alg_frame_count + algorithm_frames
Host->>Lib: welch_count = XYParser_ReadWelch(parser64, welch_summaries, max_welch)
Lib-->>Host: welch_count + welch_summaries
end
opt 结束时处理尾数据
Host->>Lib: flushed = XYParser_FlushAlgorithmData(parser64, tail_frame_summary)
Lib-->>Host: flushed + tail_frame_summary
Host->>Lib: welch_count = XYParser_ReadWelch(parser64, welch_summaries, max_welch)
Lib-->>Host: welch_count + welch_summaries
end
Host->>Lib: XYParser_DestroyParser(parser64)
```
### 3.4 64导训练打标阶段
```mermaid
%%{init: {'theme': 'default', 'sequence': {'diagramMarginX': 80, 'diagramMarginY': 30, 'actorMargin': 80, 'width': 220, 'height': 80, 'messageMargin': 35}}}%%
sequenceDiagram
participant Port as 64导EEG设备串口
participant Host as 上位机
participant Lib as XYParser库
participant Time as 时间
Host->>Lib: trigger_cmd_size = XYParser_GetTriggerCommandSize()
Lib-->>Host: trigger_cmd_size
Host->>Lib: train0_bytes = XYParser_SerializeTriggerCommand(XYPARSER_TRIGGER_TRAIN_0, trigger_cmd_buf, trigger_cmd_size)
Lib-->>Host: train0_bytes
Host->>Port: 发送 TRAIN_0 打标
Host->>Time: 开始训练计时
Time-->>Host: 训练时间到
Host->>Lib: train1_bytes = XYParser_SerializeTriggerCommand(XYPARSER_TRIGGER_TRAIN_1, trigger_cmd_buf, trigger_cmd_size)
Lib-->>Host: train1_bytes
Host->>Port: 发送 TRAIN_1 打标
```
### 3.5 8导初始化连接阶段
```mermaid
%%{init: {'theme': 'default', 'sequence': {'diagramMarginX': 80, 'diagramMarginY': 30, 'actorMargin': 80, 'width': 220, 'height': 80, 'messageMargin': 35}}}%%
sequenceDiagram
participant Dev as 8导EEG采集设备
participant Host as 上位机
participant Lib as XYParser库
Dev-->>Host: 设备连接成功
Host->>Lib: parser8 = XYParser_CreateParser(8)
Host->>Lib: XYParser_SetAdcParams(parser8, vref, gain)
Host->>Lib: XYParser_SetSampleRate(parser8, sample_rate)
Host->>Lib: XYParser_SetBypassChecksum(parser8, 1)
```
### 3.6 8导阻抗阶段
```mermaid
%%{init: {'theme': 'default', 'sequence': {'diagramMarginX': 80, 'diagramMarginY': 30, 'actorMargin': 80, 'width': 220, 'height': 80, 'messageMargin': 35}}}%%
sequenceDiagram
participant Dev as 8导EEG采集设备
participant Host as 上位机
participant Lib as XYParser库
Host->>Lib: XYParser_SetImpedanceDetection(parser8, 1)
Host->>Lib: impedance_cmd_size = XYParser_Get8ChImpedanceCommandSize()
Lib-->>Host: impedance_cmd_size
Host->>Lib: open_impedance_bytes = XYParser_Serialize8ChImpedanceCommand(1, impedance_cmd_buf, impedance_cmd_size)
Lib-->>Host: open_impedance_bytes
Host->>Dev: 下发阻抗开启命令
loop 持续获取阻抗
Dev-->>Host: 原始EEG字节流
Host->>Lib: frame_count = XYParser_Feed(parser8, raw_data, raw_size, frame8_summaries, max_frames)
Lib-->>Host: frame_count + frame8_summaries
Host->>Lib: impedance_count = XYParser_ReadImpedance(parser8, impedance_summaries, max_impedance)
Lib-->>Host: impedance_count + impedance_summaries
end
Host->>Lib: close_impedance_bytes = XYParser_Serialize8ChImpedanceCommand(0, impedance_cmd_buf, impedance_cmd_size)
Lib-->>Host: close_impedance_bytes
Host->>Dev: 下发阻抗关闭命令
Host->>Lib: XYParser_SetImpedanceDetection(parser8, 0)
```
### 3.7 8导算法阶段
```mermaid
%%{init: {'theme': 'default', 'sequence': {'diagramMarginX': 80, 'diagramMarginY': 30, 'actorMargin': 80, 'width': 220, 'height': 80, 'messageMargin': 35}}}%%
sequenceDiagram
participant Dev as 8导EEG采集设备
participant Host as 上位机
participant Lib as XYParser库
participant Algo as 算法
Host->>Lib: XYParser_SetWelchDetection(parser8, 1)
loop 持续采集
Dev-->>Host: 原始EEG字节流
Host->>Lib: frame_count = XYParser_Feed(parser8, raw_data, raw_size, frame8_summaries, max_frames)
Lib-->>Host: frame_count + frame8_summaries
Host->>Lib: converted = XYParser_Convert8ChFramesTo64Ch(frame8_summary, 1, frame64_summary, 1)
Lib-->>Host: converted + frame64_summary
Host->>Lib: value_count = XYParser_GetAlgorithmDataValueCount()
Lib-->>Host: value_count
Host->>Lib: ok = XYParser_ConvertSampleFramesToAlgorithmData(frame64_summary, algorithm_input_data)
Lib-->>Host: ok + algorithm_input_data
Host->>Algo: 输入算法数据 algorithm_input_data
Algo-->>Host: 算法输出数据 algorithm_output_bytes
Host->>Lib: alg_frame_count = XYParser_FeedAlgorithmData(parser8, algorithm_output_bytes, algorithm_output_size, algorithm_frames, max_algorithm_frames)
Lib-->>Host: alg_frame_count + algorithm_frames
Host->>Lib: welch_count = XYParser_ReadWelch(parser8, welch_summaries, max_welch)
Lib-->>Host: welch_count + welch_summaries
end
opt 结束时处理尾数据
Host->>Lib: flushed = XYParser_FlushAlgorithmData(parser8, tail_frame_summary)
Lib-->>Host: flushed + tail_frame_summary
Host->>Lib: welch_count = XYParser_ReadWelch(parser8, welch_summaries, max_welch)
Lib-->>Host: welch_count + welch_summaries
end
Host->>Lib: XYParser_DestroyParser(parser8)
```
### 3.8 8导转64导导联映射关系
8导 workflow 在送入算法前,会先调用 `XYParser_Convert8ChFramesTo64Ch` 将 8 导帧扩展为 64 导帧。
- 8 个输入通道按固定导联位置写入 64 导 summary
- 未覆盖到的其余 56 个 64 导导联全部补 `0`
- `trigger type``trigger index` 原样透传
映射图如下:
```text
8ch[0] -> PO5
8ch[1] -> POZ
8ch[2] -> PO6
8ch[3] -> PO7
8ch[4] -> O1
8ch[5] -> OZ
8ch[6] -> O2
8ch[7] -> PO8
others -> 0
```
也可以理解为下面这张对应表:
| 8导索引 | 8导写入到的64导导联 |
| --- | --- |
| 0 | PO5 |
| 1 | POZ |
| 2 | PO6 |
| 3 | PO7 |
| 4 | O1 |
| 5 | OZ |
| 6 | O2 |
| 7 | PO8 |
转换过程示意:
```text
XYParser_Feed(8导原始数据)
-> frame8_summary
-> XYParser_Convert8ChFramesTo64Ch
-> frame64_summary
-> XYParser_ConvertSampleFramesToAlgorithmData
-> algorithm_input_data
```
代码依据:
- `XYParser_Convert8ChFramesTo64Ch`
### 3.9 2导初始化连接阶段
```mermaid
%%{init: {'theme': 'default', 'sequence': {'diagramMarginX': 80, 'diagramMarginY': 30, 'actorMargin': 80, 'width': 220, 'height': 80, 'messageMargin': 35}}}%%
sequenceDiagram
participant Dev as 2导EEG采集设备
participant Host as 上位机
participant Lib as XYParser库
Dev-->>Host: 设备连接成功
Host->>Lib: parser2 = XYParser_CreateParser(2)
Host->>Lib: XYParser_SetAdcParams(parser2, 2.42, 6.0)
Note over Host,Lib: 2导默认 vref = 2.42,与 8/64 导不同
Host->>Lib: XYParser_SetSampleRate(parser2, 250)
Host->>Lib: XYParser_SetBypassChecksum(parser2, 1)
Host->>Lib: gain_cmd_size = XYParser_Get2ChGainSampleRateCommandSize()
Lib-->>Host: gain_cmd_size
Host->>Lib: gain_cmd_bytes = XYParser_Serialize2ChGainSampleRateCommand(6, 250, gain_cmd_buf, gain_cmd_size)
Lib-->>Host: gain_cmd_bytes
Host->>Dev: 下发采样率250、增益6命令
```
### 3.10 2导阻抗阶段
```mermaid
%%{init: {'theme': 'default', 'sequence': {'diagramMarginX': 80, 'diagramMarginY': 30, 'actorMargin': 80, 'width': 220, 'height': 80, 'messageMargin': 35}}}%%
sequenceDiagram
participant Dev as 2导EEG采集设备
participant Host as 上位机
participant Lib as XYParser库
Host->>Lib: XYParser_SetImpedanceDetection(parser2, 1)
Host->>Lib: XYParser_SetAdcParams(parser2, 2.42, 24.0)
Note over Host,Lib: 2导阻抗阶段仍使用 vref = 2.42
Host->>Lib: impedance_gain_cmd_size = XYParser_Get2ChGainSampleRateCommandSize()
Lib-->>Host: impedance_gain_cmd_size
Host->>Lib: impedance_gain_cmd_bytes = XYParser_Serialize2ChGainSampleRateCommand(24, 250, impedance_gain_cmd_buf, impedance_gain_cmd_size)
Lib-->>Host: impedance_gain_cmd_bytes
Host->>Dev: 下发采样率250、增益24命令
Host->>Lib: impedance_cmd_size = XYParser_Get2ChImpedanceCommandSize()
Lib-->>Host: impedance_cmd_size
Host->>Lib: open_impedance_bytes = XYParser_Serialize2ChImpedanceCommand(1, impedance_cmd_buf, impedance_cmd_size)
Lib-->>Host: open_impedance_bytes
Host->>Dev: 下发阻抗开启命令
loop 持续获取阻抗
Dev-->>Host: 原始EEG字节流
Host->>Lib: frame_count = XYParser_Feed(parser2, raw_data, raw_size, frame2_summaries, max_frames)
Lib-->>Host: frame_count + frame2_summaries
Host->>Lib: impedance_count = XYParser_ReadImpedance(parser2, impedance_summaries, max_impedance)
Lib-->>Host: impedance_count + impedance_summaries
end
Host->>Lib: close_impedance_bytes = XYParser_Serialize2ChImpedanceCommand(0, impedance_cmd_buf, impedance_cmd_size)
Lib-->>Host: close_impedance_bytes
Host->>Dev: 下发阻抗关闭命令
Host->>Lib: restore_gain_cmd_size = XYParser_Get2ChGainSampleRateCommandSize()
Lib-->>Host: restore_gain_cmd_size
Host->>Lib: restore_gain_cmd_bytes = XYParser_Serialize2ChGainSampleRateCommand(6, 250, restore_gain_cmd_buf, restore_gain_cmd_size)
Lib-->>Host: restore_gain_cmd_bytes
Host->>Dev: 下发采样率250、增益6命令
Host->>Lib: XYParser_SetAdcParams(parser2, 2.42, 6.0)
Host->>Lib: XYParser_SetImpedanceDetection(parser2, 0)
```
### 3.11 2导转64导导联映射关系
2导 workflow 在送入算法前,会先调用 `XYParser_Convert2ChFramesTo64Ch` 将 2 导帧扩展为 64 导帧。
- 2 个输入通道按固定导联位置写入 64 导 summary
- 未覆盖到的其余 62 个 64 导导联全部补 `0`
- `trigger type``trigger index` 原样透传
映射图如下:
```text
2ch[0] -> FP1
2ch[1] -> FP2
others -> 0
```
也可以理解为下面这张对应表:
| 2导索引 | 2导写入到的64导导联 |
| --- | --- |
| 0 | FP1 |
| 1 | FP2 |
转换过程示意:
```text
XYParser_Feed(2导原始数据)
-> frame2_summary
-> XYParser_Convert2ChFramesTo64Ch
-> frame64_summary
-> XYParser_ConvertSampleFramesToAlgorithmData
-> algorithm_input_data
```
代码依据:
- `XYParser_Convert2ChFramesTo64Ch`
- `Convert2ChSummaryTo64ChSummary`
- `k2ChLeadMap = { FP1, FP2 }`
### 3.12 2导 workflow 时序图
2导 demo 的完整链路与 64 导 workflow 保持一致,只是前端输入是 2 导,送算法前会先补成 64 导。
```mermaid
%%{init: {'theme': 'default', 'sequence': {'diagramMarginX': 80, 'diagramMarginY': 30, 'actorMargin': 80, 'width': 220, 'height': 80, 'messageMargin': 35}}}%%
sequenceDiagram
participant Dev as 2导EEG采集设备
participant Host as 上位机
participant Lib as XYParser库
participant Algo as 算法
Host->>Lib: XYParser_SetWelchDetection(parser2, 1)
loop 持续采集
Dev-->>Host: 原始EEG字节流
Host->>Lib: frame_count = XYParser_Feed(parser2, raw_data, raw_size, frame2_summaries, max_frames)
Lib-->>Host: frame_count + frame2_summaries
Host->>Lib: converted = XYParser_Convert2ChFramesTo64Ch(frame2_summary, 1, frame64_summary, 1)
Lib-->>Host: converted + frame64_summary
Host->>Lib: value_count = XYParser_GetAlgorithmDataValueCount()
Lib-->>Host: value_count
Host->>Lib: ok = XYParser_ConvertSampleFramesToAlgorithmData(frame64_summary, algorithm_input_data)
Lib-->>Host: ok + algorithm_input_data
Host->>Algo: 输入算法数据 algorithm_input_data
Algo-->>Host: 算法输出数据 algorithm_output_bytes
Host->>Lib: alg_frame_count = XYParser_FeedAlgorithmData(parser2, algorithm_output_bytes, algorithm_output_size, algorithm_frames, max_algorithm_frames)
Lib-->>Host: alg_frame_count + algorithm_frames
Host->>Lib: welch_count = XYParser_ReadWelch(parser2, welch_summaries, max_welch)
Lib-->>Host: welch_count + welch_summaries
end
opt 结束时处理尾数据
Host->>Lib: flushed = XYParser_FlushAlgorithmData(parser2, tail_frame_summary)
Lib-->>Host: flushed + tail_frame_summary
Host->>Lib: welch_count = XYParser_ReadWelch(parser2, welch_summaries, max_welch)
Lib-->>Host: welch_count + welch_summaries
end
Host->>Lib: XYParser_DestroyParser(parser2)
```
时序步骤如下:
1. `XYParser2Demo` 通过 TCP 接收 2 导原始数据。
2. `XYParser_Feed(handle=2)` 解析出 `frame2_summary`
3. `XYParser_Convert2ChFramesTo64Ch``FP1/FP2` 写入 64 导 summary其余导联补 `0`
4. `XYParser_ConvertSampleFramesToAlgorithmData` 将 64 导 summary 打平成算法输入。
5. ZMQ 将 64 通道 payload 发给算法服务端。
6. 算法服务端回 64 通道结果。
7. `XYParser_FeedAlgorithmData` 将算法回包喂回 parser。
8. Welch/PSD 输出 `peakHz``peakPsd`、各 band 能量等结果。
如果打开阻抗流程,则 2 导还会额外穿插以下控制阶段:
- 发送 2 导阻抗开启命令
- 发送 `250Hz / 24增益`
- 打开 parser 阻抗开关
- 读取并打印阻抗
- 阻抗结束后发送 `250Hz / 6增益`
- 发送 2 导阻抗关闭命令
- 关闭 parser 阻抗开关
- 恢复 2->64->算法->Welch 主链路
- `Convert2ChSummaryTo64ChSummary`
- 2导映射表 `k2ChLeadMap`