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