DataOut.cs完整解析 核心内容构造函数自动全局注册—new DataOut(key)立即写入Solution.Ins.QueueDic[key] this这意味着 DataOut 是跨 Project 全局共享的实例任何流程通过同一个 Key 都能访问到10 个 DefineXXXQueue 方法— 在m_DataQueueList中创建类型化ListT容器同时在m_DataTypeList记录类型字符串。调用顺序决定槽位索引支持 double/int/string/bool/HImage 及其数组共 10 种类型查询三方法—GetQueueCount()槽位总数、GetDataType(index)类型字符串供 DataIn 做类型校验、GetDataQueue(index)返回object调用者自行强转ExeModule空实现— DataOut 本身不做任何操作只是被动容器。读取操作由调用者如 Plugin.DataOut完成两套队列系统对比— DataOut 多槽位类型化队列 vs ECommunacation 的字符串 FIFO 队列二者完全独立、用途不同属性假生效—IsLimitLength/IsWait/IsDeleteData等属性值不强制执行全靠调用者自觉遵守6 个隐患— 构造函数副作用key 冲突静默覆盖、ExeModule 空实现、Clear()中ListListImage类型错误应为ListHImage、10 个 Define 方法重复代码、ListT无限增长无内存保护、行为属性不强制执行完整数据流— 从 Plugin.DataOut.Loaded 注册 → Define 槽位 → DataIn 写入 → Plugin.DataOut 读取 → AddOutputParam 输出的端到端链路DataOut.cs 完整解析 — 跨模块数据出队引擎文件:Services\DataOut.cs(230行)继承:ModuleBase— 本身可作为流程节点使用角色: 定义类型化队列槽位, 供 DataIn 写入, 供下游模块读取配套:DataIn.cs(入队端) /Plugin.DataOut(我们创建的 UI 插件)1. 它解决什么问题DataOut 是一个类型化多槽位队列容器。它不是简单的 FIFO — 而是一个数组, 每个槽位有自己的类型和独立的ListT实例, DataIn 按槽位索引写入, 下游模块按槽位索引读取。DataOut (QueueKey Q1) 槽位[0] Listdouble ← DataIn 写入 → Listdouble.Add(3.14) 槽位[1] Listdouble ← DataIn 写入 → Listdouble.Add(6.28) 槽位[2] Listint ← DataIn 写入 → Listint.Add(100) 槽位[3] Liststring ← DataIn 写入 → Liststring.Add(OK) 槽位[4] Listbool ← DataIn 写入 → Listbool.Add(true)2. 构造函数 — 自动注册到全局字典publicDataOut(stringqueueKey){QueueKeyqueueKey;Solution.Ins.QueueDic[QueueKey]this;// ★ 注册到全局字典Solution.Ins.QueueSignDic[QueueKey]newAutoResetEvent(false);// ★ 创建信号量}关键: 构造时QueueKey作为 Key 注册到Solution.Ins.QueueDic— 这意味着DataOut 是跨 Project 全局共享的。任何流程通过同一个QueueKey都能访问到同一个 DataOut 实例。3. 源码结构 (230行)DataOut : ModuleBase │ ├── ★ 构造 → 全局注册 │ ├── 属性 (队列行为控制) │ ├── QueueKey ← 队列标识 │ ├── IsLimitLength ← 是否限制长度 │ ├── LimitLength ← 最大长度 (默认1) │ ├── IsWait ← 是否阻塞等待 (未在本类实现, 供调用者判断) │ ├── IsDeleteData ← 读取后是否删除 (未在本类实现, 供调用者判断) │ └── IsIgnoreError ← 是否忽略空数据错误 │ ├── 内部容器 │ ├── m_DataQueueList ← ObservableCollectionobject 每个元素是一个 ListT │ └── m_DataTypeList ← ObservableCollectionstring 对应每个槽位的类型字符串 │ ├── ★ 10 个 DefineXXXQueue 方法 (定义槽位) │ ├── DefineIntQueue() → new Listint() │ ├── DefineDoubleQueue() → new Listdouble() │ ├── DefineStringQueue() → new Liststring() │ ├── DefineBoolQueue() → new Listbool() │ ├── DefineIntListQueue() → new ListListint() │ ├── DefineDoubleListQueue()→ new ListListdouble() │ ├── DefineStringListQueue()→ new ListListstring() │ ├── DefineBoolListQueue() → new ListListbool() │ ├── DefineHImageQueue() → new ListHImage() │ └── DefineHImageListQueue()→ new ListListHImage() │ ├── 查询方法 │ ├── GetQueueCount() → 槽位总数 │ ├── GetDataType(index) → 指定槽位的类型字符串 │ └── GetDataQueue(index) → 指定槽位的 ListT (返回 object) │ ├── Clear() → 清空所有槽位 唤醒等待者 └── ExeModule() → 空实现 (return true)4. 10 个 DefineXXXQueue — 槽位定义每个DefineXXXQueue方法做两件事:创建类型化容器 记录类型名:publicvoidDefineDoubleQueue(){m_DataQueueList.Add(newListdouble());// 容器: 存放 double 值m_DataTypeList.Add(double);// 类型名: 供 DataIn 做类型匹配}publicvoidDefineIntListQueue(){m_DataQueueList.Add(newListListint());// 容器: 存放 int[] 数组m_DataTypeList.Add(int[]);// 类型名: int[]}调用顺序决定槽位索引:DefineDoubleQueue() → 槽位[0] Listdouble DefineDoubleQueue() → 槽位[1] Listdouble DefineIntQueue() → 槽位[2] Listint DefineStringQueue() → 槽位[3] Liststring DefineBoolQueue() → 槽位[4] Listbool DefineBoolQueue() → 槽位[5] Listbool5. 查询方法 — 供 DataIn 和下游读取// 获取槽位总数publicintGetQueueCount()m_DataQueueList.Count;// 获取第 index 个槽位的类型字符串publicstringGetDataType(intindex)m_DataTypeList[index];// 获取第 index 个槽位的 ListT 容器 (返回 object, 调用者自行强转)publicobjectGetDataQueue(intindex)m_DataQueueList[index];下游读取示例(来自我们的 Plugin.DataOut):// 槽位[0] ListdoubleListdoubledList(Listdouble)dataOut.GetDataQueue(0);// 取最后一个值doublevaldList.Last();// 或 dList[dList.Count - 1]// 是否出队后删除?if(IsDeleteData)dList.RemoveAt(dList.Count-1);6. Clear() — 清空队列publicvoidClear(){lock(this)// ★ 加锁: 清空时阻止 DataIn 写入{// 按 10 种类型逐个清空foreach(variteminm_DataQueueList){if(itemisListboolboolList)boolList.Clear();elseif(itemisListintintList)intList.Clear();elseif(itemisListdoubledoubleList)doubleList.Clear();elseif(itemisListstringstringList)stringList.Clear();// ... 数组类型同理 ...elseif(itemisListListImageimgList)imgList.Clear();}// 清空后唤醒等待者Solution.Ins.QueueSignDic[QueueKey].Set();}}注意:Clear只清空槽位内容,不删除槽位本身—m_DataTypeList保持不变, 下次 DataIn 写入时类型校验仍然有效。7. ExeModule() — 空实现publicoverrideboolExeModule(){returntrue;// DataOut 本身不执行任何操作}DataOut 的ExeModule是空实现 — 因为读取操作由调用者 (如 Plugin.DataOut) 完成, DataOut 只是一个被动容器。把它放在流程中主要是为了定义槽位(通过构造和 Plugin.DataOut 的 Loaded 事件), 而非运行时执行。8. DataOut vs ECommunacation 的两套队列系统项目中存在两套完全独立的队列系统, 容易混淆:维度DataOut 队列ECommunacation 队列用途流程间传递类型化数据收发通讯字符串容器ListT[](多槽位)Queuestring(单队列 FIFO)写入方DataIn (类型化写入)通讯事件回调 (字符串写入)读取方任意模块 (通过索引)ReceiveStr 插件 (Dequeue)信号量QueueSignDic[key]m_RecStrSignal类型10 种 (double/int/string/bool/HImage 数组)仅 string全局注册QueueDic[key] this属于 ECommunacation 实例, 不在全局字典9. 完整数据流 — 以 Plugin.DataOut 为例启动 (Plugin.DataOut.Loaded) │ ├→ new JGTechVision.Services.DataOut(QueueMerged) │ └→ QueueDic[QueueMerged] this ← ★ 全局注册 │ └→ DefineDoubleQueue() × 2 DefineIntQueue() DefineStringQueue() DefineBoolQueue() × 2 └→ 6 个槽位就绪 ════════ 运行中 ════════ DataIn (Project_A).ExeModule() │ ├→ dataOut QueueDic[QueueMerged] ├→ lock(dataOut) ├→ Listdouble list dataOut.GetDataQueue(0) ← 取槽位[0] ├→ list.Add(3.14) ← 写入 └→ QueueSignDic[QueueMerged].Set() ← 唤醒 Plugin.DataOut (Project_Main).ExeModule() │ ├→ dataOut QueueDic[QueueMerged] ├→ lock(dataOut) ├→ Listdouble list dataOut.GetDataQueue(0) ← 取槽位[0] ├→ double val list.Last() ← 读取 ├→ if (IsDeleteData) list.RemoveAt(list.Count-1) ← 可选出队 └→ AddOutputParam(数据1, double, val) ← 输出10. IsLimitLength 与 LimitLength 的使用这两个属性不在 DataOut 类本身生效— 而是由调用者 (DataIn 或 Plugin.DataOut) 在写入/读取时主动判断:// DataIn 写入时的限长保护if(outQueue.IsLimitLengthdList.CountoutQueue.LimitLength)dList.RemoveAt(0);// 超出限制 → 移除最旧的数据dList.Add(newValue);// 追加新数据LimitLength 1时队列退化为只保留最新值, 类似一个可读写的变量而非缓冲区。11. 设计分析优点多槽位类型安全: 每个槽位有明确类型, 写入时做类型校验全局共享: 通过Solution.Ins.QueueDic跨 Project 访问自描述:m_DataTypeList记录了每个槽位的类型, 调用者可以动态查询双容器同步:m_DataQueueList(数据) m_DataTypeList(类型) 始终保持同步信号驱动:QueueSignDic[key].Set()支持阻塞等待隐患问题说明构造函数有副作用new DataOut(key)会立即注册到全局字典 — 如果 key 冲突会静默覆盖旧实例ExeModule空实现继承ModuleBase但执行体为空, 放在流程中无实际效果Clear()中 HImage 类型错误item is ListListImage— 应该是ListHImage, 但用了System.Drawing.Image10 个 Define 方法重复可以用泛型方法DefineQueueT(string typeName)消除无容量上限ListT无限增长, 没有全局内存保护IsWait/IsLimitLength不生效这些属性值靠调用者自觉遵守, 本类不强制执行文档说明: 基于 DataOut.cs (230行) 源码静态分析生成。与 DataIn.cs 配对使用, 共同构成跨模块的多槽位类型化数据队列系统。注意与 ECommunacation 的字符串队列区别。当前版本 2026-06-10。