C#远程数据采集插件化解析框架设计:如何兼容 Modbus RTU/SCDMA 多种通信协议?
·
远程监控系统中,很多设备都有自己的通讯协议,如何做的兼容这些不同的协议?我们系统采用的的方案是把解析逻辑做成插件,每种协议一个 DLL,在后台进行配置,解析的时候自动匹配到合适的解析器。增加协议的时候,不用改代码,只要根据手册开发对应的 DLL 。
一、背景
1.1 业务场景
| 协议类型 | 应用场景 | 数据格式 |
|---|---|---|
| RTU 协议 | 抽油机控制器 | 自定义帧结构 |
| Modbus RTU | 标准工业设备 | 功能码 + 寄存器 |
| SCDMA | 水表/流量计 | 188 协议变种 |
传统做法是为每种协议硬编码解析逻辑,耦合重、难维护,加新协议要改核心代码,还没法热更新,必须重启服务。
1.2 设计目标
- 插件化:每种协议独立 DLL,互不影响
- 配置驱动:数据库配置启用/禁用协议
- 热加载:运行时动态加载,无需重启
- 统一接口:所有协议实现同一接口
二、核心架构
2.1 IAnalytical 接口
所有协议解析器实现 IAnalytical 接口:
// IAnalytical.cs
public interface IAnalytical
{
// 协议标识
string AgreementId { get; set; }
// 输入/输出字段列表(用于配置界面)
List<string> InputNameList { get; }
List<string> OutputNameList { get; }
// 生命周期管理
void Start(GetDeviceMonitor d);
void Stop();
// 核心方法
bool JudgeAnalytical(...); // 协议识别
Dictionary<string, string> AnalyticalData(...); // 数据解析
string CreateCommands(...); // 命令生成
string MaskSQL(...); // SQL 生成
}
2.2 协议识别机制
系统收到原始数据后,遍历所有已加载的协议,找到匹配的解析器:
// HaoPuServer.cs - LogicAnalytical()
private void LogicAnalytical(ref ReceivedDataEventArgs e)
{
foreach (IAnalytical analytical in this.Agreement.Values)
{
if (analytical.JudgeAnalytical(
e.Clientid, e.RequestHex,
ref startIndex, ref startLength,
ref isValid, ref rtuAddress, ref code,
ref extend, ref deviceId, ref deviceName,
ref monitorId, ref monitorName,
ref errorMsg, this.Config.monitorMana, this.Config.deviceMana))
{
// 找到匹配的协议,跳出循环
e.Agreementid = analytical.AgreementId;
break;
}
}
}
每个协议的 JudgeAnalytical() 方法自己判断数据是否属于自己,匹配成功后 break,返回 AgreementId,后续解析用对应的协议实例。
三、动态加载
3.1 Agreement 配置表
协议信息存在数据库 Agreement 表里:
| 字段 | 说明 | 示例 |
|---|---|---|
id |
协议 ID | DLL001 |
dllpath |
DLL 文件路径 | Drive\Rrs.CyyRtu.dll |
classname |
类全名 | Rrs.CyyRtu.Rtu |
desc |
描述 | 标准 RTU 协议 |
status |
启用状态 | True/False |
3.2 运行时加载
系统启动时遍历 Agreement 表,动态加载启用的协议:
// HaoPuServer.cs - AgreementInit()
private bool AgreementInit(Agreement e)
{
try
{
string startupPath = Application.StartupPath;
// 1. 从 DLL 文件加载程序集
Assembly assembly = Assembly.LoadFile(startupPath + "\\" + e.DllPath);
// 2. 创建协议解析器实例
IAnalytical analytical = (IAnalytical)assembly.CreateInstance(e.ClassName);
// 3. 设置协议 ID
analytical.AgreementId = e.ID;
// 4. 启动协议(注册回调)
analytical.Start(new GetDeviceMonitor(this.GetDeviceMonitor));
// 5. 加入协议字典
this.Agreement.Add(analytical.AgreementId, analytical);
return true;
}
catch (Exception ex)
{
// 记录错误日志
return false;
}
}
3.3 协议字典
加载后的协议存在字典里,键是 AgreementId:
// HaoPuServer.cs
private Dictionary<string, IAnalytical> Agreement;
O(1) 查找,支持运行时增删,配合 lock 保证线程安全。
四、协议实现示例
4.1 Modbus 协议(Moudbs)
// Rrs.Moudbs/Moudbs.cs
public class Moudbs : IAnalytical
{
private string agreementId;
public string AgreementId
{
get { return this.agreementId; }
set { this.agreementId = value; }
}
// 输出字段列表(用于配置界面下拉框)
public List<string> OutputNameList
{
get
{
return new List<string>(new string[] {
"小数", "保留小数", "冲程", "冲次",
"位移包", "载荷", "电流", "电压",
"有功功率", "无功功率", "功率因数",
"累计电量", "压力", "年月日时分"
});
}
}
// 协议识别:检查功能码是否为 0x03(读保持寄存器)
public bool JudgeAnalytical(...)
{
int code = ...;
if (code == 0x03)
{
return true;
}
return false;
}
// 数据解析
public Dictionary<string, string> AnalyticalData(Device d, Monitor m, string responsehex, object extend)
{
// 解析 Modbus 响应数据
// 返回字段名 -> 字段值的字典
}
}
4.2 SCDMA 协议(水表)
// Rrs.SCDMA/SCdma.cs
public class SCdma : IAnalytical
{
public List<string> OutputNameList
{
get
{
return new List<string>(new string[] {
"小数", "井站类型", "设备厂家", "通讯方式",
"通讯协议", "波特率", "数据位", "停止位",
"188电压", "188流量", "188水表状态", "188单位"
});
}
}
// 协议识别:检查是否为 188 协议帧
public bool JudgeAnalytical(...)
{
// 解析 188 协议帧头
// 匹配则返回 true
}
}
4.3 RTU 协议(抽油机)
// Rrs.CyyRtu/Rtu.cs
public class Rtu : IAnalytical
{
public List<string> OutputNameList
{
get
{
return new List<string>(new string[] {
"小数", "保留小数", "冲程", "冲次",
"位移包", "载荷", "电流", "电压",
"曲柄销子退扣", "测试类型", "故障停井选项"
});
}
}
}
五、配置界面
5.1 协议管理
配置界面通过 FormConfig 做协议管理:
// FormConfig.cs - 加载协议配置
private void LoadDataConfig()
{
this.agreementMana = new AgreementMana(this.IData);
// ... 加载其他配置
}
// 启用/禁用协议
private void ActiveConfig()
{
Agreement agreement = (Agreement)this.bindingSource1.Current;
if (!agreement.Status)
{
// 启用协议
if (this.ActiveAgreeEvent(agreement))
{
agreement.Status = true;
}
}
else
{
// 禁用协议
if (this.ActiveAgreeEvent(agreement))
{
agreement.Status = false;
}
}
}
5.2 动态字段绑定
配置界面根据协议的 InputNameList 和 OutputNameList 动态生成下拉框:
// FormConfig.cs
IAnalytical analytical = (IAnalytical)Assembly.LoadFile(
startupPath + "\\" + agreement.DllPath
).CreateInstance(agreement.ClassName);
// 绑定输入字段下拉框
DataGridViewComboBoxColumn col1 = (DataGridViewComboBoxColumn)this.dataGridView1.Columns["ModifiedOutput"];
col1.DataSource = analytical.InputNameList;
// 绑定输出字段下拉框
DataGridViewComboBoxColumn col2 = (DataGridViewComboBoxColumn)this.dataGridView1.Columns["ModifiedOutput"];
col2.DataSource = analytical.OutputNameList;
新增协议不用改配置界面代码,字段列表由协议 DLL 自己定义,配置界面和协议解耦。
六、完整数据流

关键词:插件化架构,协议解析,多协议适配,工业物联网
本文基于实际项目经验编写,代码已脱敏处理。如需完整源码或技术咨询,欢迎私信或评论交流。
更多推荐
所有评论(0)