广东湘恒智能科技有限公司
主营产品: 西门子PLC代理商,plc变频器,伺服电机,人机界面,触摸屏,线缆,DP接头
SIEMENS西门子福建省福州市(授权)电机一级代理商——西门子华南总代理

对于上位机而言,快速的高质量交付依然极为重要。在上位机项目中,通信毫无疑问是极为重要的一个模块。不同的项目需要的变量不一样,连接的设备类型和数量也不一样。那么对于上位机的通信这块,我们如何才能快速的高质量完成和设备的数据交互?


图1 C#上位机项目实例网络图

有的项目只需要对接一台plc,有的项目可能需要对接多台。有的PLC可能只需要访问几个变量,而有的PLC可能需要访问几百、数千甚至上万变量。那种用Modbus RTU或者一个串口自定义协议连接一个小I/O模块的Demo在现实中几乎没有,毫无实际意义。既然要学上位机,那么肯定奔着高水准去。有能力挑战大项目才算真正掌握了C#上位机。

说了这么多,那么如何合理地规划数据交互模块?我觉得最重要的一点就是实现变量与驱动的解耦。多设备、多变量的项目,如果不能做到变量和驱动的解耦,那么编程与调试工作量将非常大。

关于变量与驱动的解耦我们有两种方案,方案一用于我们重构的C#上位机项目实例中。方案二技术难度更大一点,用于我们的C#上位机框架HwLib.AdvSCADA中。今天我们要介绍的是方案一。在方案一中,每种驱动拥有一个独立的线程。它根据我们传递进来的报文要求发送数据读取指令到设备。然后再将读取到的报文返回。在返回报文时通过事件通知的方式调用解析方法,将报文解析到对应的变量。在UI层中或者其它模块中,我们只需要关联这些变量名即可。这样我们可以将工作重心放在业务逻辑上,不再需要关注驱动。

在我们的C#上位机项目实例中的早期版本其实变量和驱动也并没有实现解耦。在这次版本大升级过程中。我们重构了驱动模块。下面以这次重构来简单介绍了我们的方案一。这次重构,我们的C#上位机实例集成了两种通信协议,分别是S7和Modbus。以Modbus为例,我们首先基于EasyModbus做了二次封装。对外提供了读取与写入两个公开方法,另外增加了两个事件,分别是连接状态改变事件和数据更新事件。










//需要读取的报文信息,包含了寄存器区域、起始地址和长度等信息public List<ReadMsg> ReadMsgList = newList<ReadMsg>();//需要写入数据的变量信息public Dictionary<string, TagModel> WriteTagsList = new Dictionary<string, TagModel>();//返回的报文数据public Dictionary<string, int[]> RstData = new Dictionary<string, int[]>();public bool IsConnected { get; set; }public event EventHandler StatusChanged;public event EventHandler DataUpdate;

驱动类封装后放在DAL层。无论我们后续其它项目中有多少设备或者仪表,我们都不再需要修改这个类。而只需要在BLL层中实例化这个类并传递变量信息即可。Meter是我们在BLL中创建的一个类,用于根据仪表数量实例化驱动并管理变量。在其它UI或者模块中,我们只需要关联这里的变量就行了。












Meter2 = new ModbusTCP("192.168.1.12");//订阅事件//UI界面上显示通信状态Meter2.StatusChanged += Meter2_StatusChanged;//数据更新时间,用于解析报文Meter2.DataUpdate += Meter2_DataUpdate;//读写变量信息ByteList2(Meter2);Meter2.WriteTagsList = TagsMeter2;//通信启动Meter2.Start();

其中订阅状态改变事件是为了在标题栏显示连接状态。


图2 设备状态显示

在方法ByteList中,我们会传递请求的报文信息到驱动。驱动类根据我们传递的报文到PLC中请求数据。











private static void ByteList2(ModbusTCP mdb) {     ModbusTCP.ReadMsg msg = new ModbusTCP.ReadMsg();     msg.Id = "1";     msg.RgstArea = 3;     msg.StartAddress = 1;     msg.Count = 10;
     mdb.ReadMsgList.Add(msg); }

报文解析方法会在驱动类ModbusTCP的数据更新时触发。它会对驱动类ModbusTCP返回的byte[]类型报文解析并关联到变量。这样我们在UI层或者其它模块就可以直接使用变量名称来获取数据了。至于UI层,我们可以用定时器,也可以用订阅事件的方法来刷新数据。

由于刷新数据并不占用多少资源,所以我们在UI界面采用了定时器更新。





private void timer1_Tick(object sender, EventArgs e){    labPower.Text = Meter.TagsMeter1["Power"].Value.ToString();}

该方案实现了变量和驱动的解耦。对于不同项目的设备和变量的不同,我们只需要在BLL层根据工艺要求调整变量即可,而不用再修改驱动层。这种方式极大地提高了工程效率。


展开全文
相关产品
拨打电话 微信咨询 发送询价