远程监控系统中很多设备都有自己的通讯协议如何做的兼容这些不同的协议我们系统采用的的方案是把解析逻辑做成插件每种协议一个 DLL在后台进行配置解析的时候自动匹配到合适的解析器。增加协议的时候不用改代码只要根据手册开发对应的 DLL 。一、背景1.1 业务场景协议类型应用场景数据格式RTU 协议抽油机控制器自定义帧结构Modbus RTU标准工业设备功能码 寄存器SCDMA水表/流量计188 协议变种传统做法是为每种协议硬编码解析逻辑耦合重、难维护加新协议要改核心代码还没法热更新必须重启服务。1.2 设计目标插件化每种协议独立 DLL互不影响配置驱动数据库配置启用/禁用协议热加载运行时动态加载无需重启统一接口所有协议实现同一接口二、核心架构2.1 IAnalytical 接口所有协议解析器实现IAnalytical接口// IAnalytical.cspublicinterfaceIAnalytical{// 协议标识stringAgreementId{get;set;}// 输入/输出字段列表用于配置界面ListstringInputNameList{get;}ListstringOutputNameList{get;}// 生命周期管理voidStart(GetDeviceMonitord);voidStop();// 核心方法boolJudgeAnalytical(...);// 协议识别Dictionarystring,stringAnalyticalData(...);// 数据解析stringCreateCommands(...);// 命令生成stringMaskSQL(...);// SQL 生成}2.2 协议识别机制系统收到原始数据后遍历所有已加载的协议找到匹配的解析器// HaoPuServer.cs - LogicAnalytical()privatevoidLogicAnalytical(refReceivedDataEventArgse){foreach(IAnalyticalanalyticalinthis.Agreement.Values){if(analytical.JudgeAnalytical(e.Clientid,e.RequestHex,refstartIndex,refstartLength,refisValid,refrtuAddress,refcode,refextend,refdeviceId,refdeviceName,refmonitorId,refmonitorName,referrorMsg,this.Config.monitorMana,this.Config.deviceMana)){// 找到匹配的协议跳出循环e.Agreementidanalytical.AgreementId;break;}}}每个协议的JudgeAnalytical()方法自己判断数据是否属于自己匹配成功后break返回AgreementId后续解析用对应的协议实例。三、动态加载3.1 Agreement 配置表协议信息存在数据库Agreement表里字段说明示例id协议 IDDLL001dllpathDLL 文件路径Drive\Rrs.CyyRtu.dllclassname类全名Rrs.CyyRtu.Rtudesc描述标准 RTU 协议status启用状态True/False3.2 运行时加载系统启动时遍历Agreement表动态加载启用的协议// HaoPuServer.cs - AgreementInit()privateboolAgreementInit(Agreemente){try{stringstartupPathApplication.StartupPath;// 1. 从 DLL 文件加载程序集AssemblyassemblyAssembly.LoadFile(startupPath\\e.DllPath);// 2. 创建协议解析器实例IAnalyticalanalytical(IAnalytical)assembly.CreateInstance(e.ClassName);// 3. 设置协议 IDanalytical.AgreementIde.ID;// 4. 启动协议注册回调analytical.Start(newGetDeviceMonitor(this.GetDeviceMonitor));// 5. 加入协议字典this.Agreement.Add(analytical.AgreementId,analytical);returntrue;}catch(Exceptionex){// 记录错误日志returnfalse;}}3.3 协议字典加载后的协议存在字典里键是AgreementId// HaoPuServer.csprivateDictionarystring,IAnalyticalAgreement;O(1) 查找支持运行时增删配合 lock 保证线程安全。四、协议实现示例4.1 Modbus 协议Moudbs// Rrs.Moudbs/Moudbs.cspublicclassMoudbs:IAnalytical{privatestringagreementId;publicstringAgreementId{get{returnthis.agreementId;}set{this.agreementIdvalue;}}// 输出字段列表用于配置界面下拉框publicListstringOutputNameList{get{returnnewListstring(newstring[]{小数,保留小数,冲程,冲次,位移包,载荷,电流,电压,有功功率,无功功率,功率因数,累计电量,压力,年月日时分});}}// 协议识别检查功能码是否为 0x03读保持寄存器publicboolJudgeAnalytical(...){intcode...;if(code0x03){returntrue;}returnfalse;}// 数据解析publicDictionarystring,stringAnalyticalData(Deviced,Monitorm,stringresponsehex,objectextend){// 解析 Modbus 响应数据// 返回字段名 - 字段值的字典}}4.2 SCDMA 协议水表// Rrs.SCDMA/SCdma.cspublicclassSCdma:IAnalytical{publicListstringOutputNameList{get{returnnewListstring(newstring[]{小数,井站类型,设备厂家,通讯方式,通讯协议,波特率,数据位,停止位,188电压,188流量,188水表状态,188单位});}}// 协议识别检查是否为 188 协议帧publicboolJudgeAnalytical(...){// 解析 188 协议帧头// 匹配则返回 true}}4.3 RTU 协议抽油机// Rrs.CyyRtu/Rtu.cspublicclassRtu:IAnalytical{publicListstringOutputNameList{get{returnnewListstring(newstring[]{小数,保留小数,冲程,冲次,位移包,载荷,电流,电压,曲柄销子退扣,测试类型,故障停井选项});}}}五、配置界面5.1 协议管理配置界面通过FormConfig做协议管理// FormConfig.cs - 加载协议配置privatevoidLoadDataConfig(){this.agreementMananewAgreementMana(this.IData);// ... 加载其他配置}// 启用/禁用协议privatevoidActiveConfig(){Agreementagreement(Agreement)this.bindingSource1.Current;if(!agreement.Status){// 启用协议if(this.ActiveAgreeEvent(agreement)){agreement.Statustrue;}}else{// 禁用协议if(this.ActiveAgreeEvent(agreement)){agreement.Statusfalse;}}}5.2 动态字段绑定配置界面根据协议的InputNameList和OutputNameList动态生成下拉框// FormConfig.csIAnalyticalanalytical(IAnalytical)Assembly.LoadFile(startupPath\\agreement.DllPath).CreateInstance(agreement.ClassName);// 绑定输入字段下拉框DataGridViewComboBoxColumncol1(DataGridViewComboBoxColumn)this.dataGridView1.Columns[ModifiedOutput];col1.DataSourceanalytical.InputNameList;// 绑定输出字段下拉框DataGridViewComboBoxColumncol2(DataGridViewComboBoxColumn)this.dataGridView1.Columns[ModifiedOutput];col2.DataSourceanalytical.OutputNameList;新增协议不用改配置界面代码字段列表由协议 DLL 自己定义配置界面和协议解耦。六、完整数据流关键词插件化架构协议解析多协议适配工业物联网本文基于实际项目经验编写代码已脱敏处理。如需完整源码或技术咨询欢迎私信或评论交流。
C#远程数据采集插件化解析框架设计:如何兼容 Modbus RTU/SCDMA 多种通信协议?
远程监控系统中很多设备都有自己的通讯协议如何做的兼容这些不同的协议我们系统采用的的方案是把解析逻辑做成插件每种协议一个 DLL在后台进行配置解析的时候自动匹配到合适的解析器。增加协议的时候不用改代码只要根据手册开发对应的 DLL 。一、背景1.1 业务场景协议类型应用场景数据格式RTU 协议抽油机控制器自定义帧结构Modbus RTU标准工业设备功能码 寄存器SCDMA水表/流量计188 协议变种传统做法是为每种协议硬编码解析逻辑耦合重、难维护加新协议要改核心代码还没法热更新必须重启服务。1.2 设计目标插件化每种协议独立 DLL互不影响配置驱动数据库配置启用/禁用协议热加载运行时动态加载无需重启统一接口所有协议实现同一接口二、核心架构2.1 IAnalytical 接口所有协议解析器实现IAnalytical接口// IAnalytical.cspublicinterfaceIAnalytical{// 协议标识stringAgreementId{get;set;}// 输入/输出字段列表用于配置界面ListstringInputNameList{get;}ListstringOutputNameList{get;}// 生命周期管理voidStart(GetDeviceMonitord);voidStop();// 核心方法boolJudgeAnalytical(...);// 协议识别Dictionarystring,stringAnalyticalData(...);// 数据解析stringCreateCommands(...);// 命令生成stringMaskSQL(...);// SQL 生成}2.2 协议识别机制系统收到原始数据后遍历所有已加载的协议找到匹配的解析器// HaoPuServer.cs - LogicAnalytical()privatevoidLogicAnalytical(refReceivedDataEventArgse){foreach(IAnalyticalanalyticalinthis.Agreement.Values){if(analytical.JudgeAnalytical(e.Clientid,e.RequestHex,refstartIndex,refstartLength,refisValid,refrtuAddress,refcode,refextend,refdeviceId,refdeviceName,refmonitorId,refmonitorName,referrorMsg,this.Config.monitorMana,this.Config.deviceMana)){// 找到匹配的协议跳出循环e.Agreementidanalytical.AgreementId;break;}}}每个协议的JudgeAnalytical()方法自己判断数据是否属于自己匹配成功后break返回AgreementId后续解析用对应的协议实例。三、动态加载3.1 Agreement 配置表协议信息存在数据库Agreement表里字段说明示例id协议 IDDLL001dllpathDLL 文件路径Drive\Rrs.CyyRtu.dllclassname类全名Rrs.CyyRtu.Rtudesc描述标准 RTU 协议status启用状态True/False3.2 运行时加载系统启动时遍历Agreement表动态加载启用的协议// HaoPuServer.cs - AgreementInit()privateboolAgreementInit(Agreemente){try{stringstartupPathApplication.StartupPath;// 1. 从 DLL 文件加载程序集AssemblyassemblyAssembly.LoadFile(startupPath\\e.DllPath);// 2. 创建协议解析器实例IAnalyticalanalytical(IAnalytical)assembly.CreateInstance(e.ClassName);// 3. 设置协议 IDanalytical.AgreementIde.ID;// 4. 启动协议注册回调analytical.Start(newGetDeviceMonitor(this.GetDeviceMonitor));// 5. 加入协议字典this.Agreement.Add(analytical.AgreementId,analytical);returntrue;}catch(Exceptionex){// 记录错误日志returnfalse;}}3.3 协议字典加载后的协议存在字典里键是AgreementId// HaoPuServer.csprivateDictionarystring,IAnalyticalAgreement;O(1) 查找支持运行时增删配合 lock 保证线程安全。四、协议实现示例4.1 Modbus 协议Moudbs// Rrs.Moudbs/Moudbs.cspublicclassMoudbs:IAnalytical{privatestringagreementId;publicstringAgreementId{get{returnthis.agreementId;}set{this.agreementIdvalue;}}// 输出字段列表用于配置界面下拉框publicListstringOutputNameList{get{returnnewListstring(newstring[]{小数,保留小数,冲程,冲次,位移包,载荷,电流,电压,有功功率,无功功率,功率因数,累计电量,压力,年月日时分});}}// 协议识别检查功能码是否为 0x03读保持寄存器publicboolJudgeAnalytical(...){intcode...;if(code0x03){returntrue;}returnfalse;}// 数据解析publicDictionarystring,stringAnalyticalData(Deviced,Monitorm,stringresponsehex,objectextend){// 解析 Modbus 响应数据// 返回字段名 - 字段值的字典}}4.2 SCDMA 协议水表// Rrs.SCDMA/SCdma.cspublicclassSCdma:IAnalytical{publicListstringOutputNameList{get{returnnewListstring(newstring[]{小数,井站类型,设备厂家,通讯方式,通讯协议,波特率,数据位,停止位,188电压,188流量,188水表状态,188单位});}}// 协议识别检查是否为 188 协议帧publicboolJudgeAnalytical(...){// 解析 188 协议帧头// 匹配则返回 true}}4.3 RTU 协议抽油机// Rrs.CyyRtu/Rtu.cspublicclassRtu:IAnalytical{publicListstringOutputNameList{get{returnnewListstring(newstring[]{小数,保留小数,冲程,冲次,位移包,载荷,电流,电压,曲柄销子退扣,测试类型,故障停井选项});}}}五、配置界面5.1 协议管理配置界面通过FormConfig做协议管理// FormConfig.cs - 加载协议配置privatevoidLoadDataConfig(){this.agreementMananewAgreementMana(this.IData);// ... 加载其他配置}// 启用/禁用协议privatevoidActiveConfig(){Agreementagreement(Agreement)this.bindingSource1.Current;if(!agreement.Status){// 启用协议if(this.ActiveAgreeEvent(agreement)){agreement.Statustrue;}}else{// 禁用协议if(this.ActiveAgreeEvent(agreement)){agreement.Statusfalse;}}}5.2 动态字段绑定配置界面根据协议的InputNameList和OutputNameList动态生成下拉框// FormConfig.csIAnalyticalanalytical(IAnalytical)Assembly.LoadFile(startupPath\\agreement.DllPath).CreateInstance(agreement.ClassName);// 绑定输入字段下拉框DataGridViewComboBoxColumncol1(DataGridViewComboBoxColumn)this.dataGridView1.Columns[ModifiedOutput];col1.DataSourceanalytical.InputNameList;// 绑定输出字段下拉框DataGridViewComboBoxColumncol2(DataGridViewComboBoxColumn)this.dataGridView1.Columns[ModifiedOutput];col2.DataSourceanalytical.OutputNameList;新增协议不用改配置界面代码字段列表由协议 DLL 自己定义配置界面和协议解耦。六、完整数据流关键词插件化架构协议解析多协议适配工业物联网本文基于实际项目经验编写代码已脱敏处理。如需完整源码或技术咨询欢迎私信或评论交流。