# 数据流转 ## 1. 概念约定 - **物理设备**:真实硬件端口(当前主要是串口),由 `SerialQtDriver` 直接读写。 - **设备实例**:`SerialDevice`,负责单设备生命周期、驱动回调、上下行数据接入。 - **原始总线消息**:`RawBusMessage`,包含 `endpoint`、`protocolHint`、`payload`、`traceId`、`direction` 等基础字段。 - **流水线上下文**:`PipelineContext`,继承 `RawBusMessage`,增加 `frameKind`、`frameSeq` 等管道字段。 - **业务消息**:经过协议解析和映射后得到 `DOMMessage`(统一语义数据)。 --- ## 2. 上行主链路(串口 -> 消息总线) ### 2.1 设备初始化 `SerialDeviceManager` 完成以下动作: 1. 根据配置创建并配置 `SerialQtDriver`(波特率、校验、停止位、流控)。 2. 创建 `SerialDevice` 并注入 `DeviceInfo`(含协议提示,如 `modbus_rtu`)。 3. 通过 `bindIngress(...)` 绑定: - `IIngressPort`(当前实现为 `DriverIngressAdapter`) - `MemoryPool`(用于 payload 内存块分配) 4. 调用 `start()` 打开端口并注册读回调/错误回调。 ### 2.2 读回调进入 ingress 当串口收到字节流时,`SerialDevice::onRead(...)` 执行: 1. 从 `MemoryPool` 分配内存块并拷贝原始字节。 2. 构造 `RawBusMessage`: - `direction = Upstream` - `endpoint = 串口名` - `protocolHint = DeviceInfo.protocol` - `payload/payloadSize = 原始字节数据` - `traceId = serial::` 3. 调用 `m_ingress->push(std::move(ctx))` 进入统一入口。 4. 若内存池不足或 ingress 队列满,通过 `reportError(...)` 记录错误。 ### 2.3 ingress 适配为 PipelineContext `DriverIngressAdapter::push(...)` 负责轻量转换: 1. 新建 `PipelineContext`。 2. `pipelineCtx.raw() = std::move(ctx)` 将原始消息搬运进管道上下文。 3. 调用 `PipelineEngine::enqueueIngress(...)` 入总线处理队列。 --- ## 3. PipelineEngine 内部流转 ### 3.1 入口分流 `enqueueIngress(...)` 分两种模式: - **并行模式(parallelPipeline=true)** - 按 `endpoint` 创建/复用独立 `EndpointFramingState`(每端点一个分帧线程 + chunk 队列)。 - chunk 先进入端点队列,端点线程负责分帧并赋值 `frameSeq`。 - 完整帧进入 `m_framedQ`,由解析线程池处理。 - **单线程模式(parallelPipeline=false)** - 直接进入 `m_ingressQ`,由单 worker 串行处理。 ### 3.2 Stage 1:分帧(Framer) `dispatchFramer(...)` 的选择策略: 1. 优先根据 `protocolHint` 从插件管理器选择协议分帧插件。 2. 按 `endpoint + pluginId` 复用 framer session(保持协议状态机连续性)。 3. 若没有可用插件,降级到 `PassthroughFramer`(透传/整包策略)。 输出:`frameKind = CompleteFrame` 的帧批次。 ### 3.3 Stage 2/3/4:解析 -> 过滤 -> 校验 `runStages234(...)` 核心流程: 1. **Stage2 Parser** - 选择 `IProtocolPlugin`,复用 parser session。 - `parse(rawCtx, envelope)` 产出协议封装 `ProtocolEnvelope`。 - 选择 mapper 插件并映射为 `std::vector`。 2. **Stage3 Filter** - 对 `DOMMessage` 执行过滤规则(`ProtocolParseFilter::process`)。 3. **Stage4 Validator** - 最低校验当前为 `messageId` 非空。 4. **Transform/Enrich** - 应用动态规则(scale/offset/unit)。 - 写入 trace,补充富化字段。 说明:当前 `runStages234` 只返回布尔结果,`EnrichedMessage` 由内部生成,后续可接持久化/发布环节。 ### 3.4 出口与有序保证 - 未开启有序出口:解析成功后直接进入 `m_egressQ`。 - 开启 `orderedEgress`: - 解析线程先写入 `m_mergeQ`。 - `mergerLoop` 按 `endpoint + frameSeq` 重排。 - 使用 `nextExpected` 保证同端点有序输出。 - 超过 `maxReorderDepth` 的乱序缓存上限会丢弃并计数。 --- ## 4. 错误与背压点 主要失败点及表现: 1. **内存池分配失败**:`SerialDevice::onRead` 直接 `reportError(memory pool exhausted)`。 2. **入口队列满**:`enqueueIngress` 返回 false,计入 drop。 3. **分帧/解析插件缺失或失败**:对应帧在阶段中被丢弃。 4. **过滤/校验失败**:消息跳过,不进入下游。 5. **重排深度超限**:`mergeDrop` 增加并告警。 --- ## 5. 当前链路时序(上行) ```text SerialQtDriver(read callback) -> SerialDevice::onRead -> MemoryPool::allocate + RawBusMessage -> IIngressPort::push (DriverIngressAdapter) -> PipelineEngine::enqueueIngress -> [Framer] dispatchFramer/feedChunk -> [Stage2] parse + map -> DOMMessage -> [Stage3] filter -> [Stage4] validate -> transform/enrich -> egressQ (有序模式下先 merge 再 egress) ``` --- ## 6. 下行(现状) - `RawBusMessage.direction` 已支持 `Downstream` 语义。 - `SerialDevice` 已提供 `writePayload(...)` 写入能力。 - 当前代码中 egress 路由到物理设备的绑定仍预留(`SerialDeviceManager` 里相关逻辑已注释),后续可补齐: - egress 消息 -> `EgressRouter` -> endpoint 设备 -> `writePayload`。 --- ## 7. 维护建议 1. `traceId` 建议在全链路透传到最终发布侧,便于定位帧级问题。 2. 为 Stage2/Stage3/Stage4 增加细粒度指标(成功率、耗时、drop 原因)。 3. 文档后续可新增“协议插件开发约定”章节(framer/parser/mapper 三类插件接口对照)。