前言


本文将使用一个NuGet公开的组件技术来实现一个ModBus TCP的服务器端数据引擎,方便的实现接收来自各种设备的数据。并且该服务器模拟真实的设备,包含了数据池功能,可以接受来自任何支持Modbus tcp的客户端进行读写数据。C#实现的客户端类请参考下面这篇文章:http://www.cnblogs.com/dathlin/p/7885368.html  可以进行一些客户端服务器的联合调试。

nuget地址:https://www.nuget.org/packages/HslCommunication/            

github地址:https://github.com/dathlin/HslCommunication                                 如果喜欢可以star或是fork,还可以打赏支持。

该项目在github上开源,有兴趣的可以查看源代码,如果有经济基础,可以对作者进行打赏。关于更多如何创建Modbus服务器的详细细节,可以参照源代码里面的服务器测试项目

https://github.com/dathlin/HslCommunication

在Visual Studio 中的NuGet管理器中可以下载安装,也可以直接在NuGet控制台输入下面的指令安装:

Install-Package HslCommunication

NuGet安装教程  http://www.cnblogs.com/dathlin/p/7705014.html

下载地址


此处提供一个服务器的Demo软件,下载解压就可以直接运行,这个Demo的源代码也在上面的示例,界面如下:后续新的版本可能会有点小区别

https://github.com/dathlin/HslCommunication/raw/master/Download/ModbusTcpServer.zip   这个地址的服务器软件永远都是最新的,会不停的更新。

如果您需要一个测试的客户端,包括了tcp和rtu的都可以,下载下面的Demo即可,支持各种配置信息

HslCommunicationDemo.zip

随便聊聊


使用本组件可以快速搭建一个高性能的MODBUS TCP总站,当我们一个上位机需要读取100台西门子PLC设备(此处只是举个例子,凡是都是使用Modbus tcp的都是一样的)的时候,你采用服务器主动去请求100台设备的机制对性能来说是个极大的考验,如果开100个线程去轮询100台设备,那么性能损失将是非常大的,更不用说再增加设备,如果搭建Modbus tcp服务器,就可以完美的解决性能问题,因为连接的压力将会平均分摊给每一台PLC,服务器端只要新增一个时间戳就可以知道客户端有没有连接上。

我们在100台PLC里都增加发送Modbus tcp方法,将数据发送到服务器的ip和端口上去,服务器根据站号来区分设备。这样就可以搭建一个高性能总站。

关于数据池


本服务器端拥有两个数据池,线圈数据池和寄存器数据池,任何客户端包括服务器本身都可以对数据池进行读写数据,比如PLC1将所有的数据发送到寄存器地址0-99上,PLC2将数据发送到100-199上。

  • 线圈数据池         模拟了真实的数据读写,自动解析指令并返回数据
  • 离散输入数据池    服务器端允许读写,对客户端来说仅仅支持读,功能码02
  • 寄存器数据池     模拟了真实的数据读写
  • 输入寄存器数据池   服务器端允许读写,对客户端来说仅仅支持读,功能码04

四个数据池的地址范围都是0-65535,起始地址是从0开始

Reference


ModBus组件所有的功能类都在 HslCommunication.ModBus命名空间,所以再使用之前先添加

using HslCommunication.ModBus;

How to Use


如果想快速的搭建一个Modbus-Tcp的服务器,只要2行代码即可,实例化,启动,而下面的例子稍微复杂了一点,额外配置了日志记录器,绑定了一个数据接收的方法,每当客户端进行数据交互,就会触发,可用于实现自定义功能的Modbus-Tcp服务器

        private HslCommunication.ModBus.ModbusTcpServer busTcpServer;private void button1_Click( object sender, EventArgs e ){if(!int.TryParse(textBox2.Text,out int port)){MessageBox.Show( "端口输入不正确!" );return;}try{busTcpServer = new HslCommunication.ModBus.ModbusTcpServer( );busTcpServer.LogNet = new HslCommunication.LogNet.LogNetSingle( "logs.txt" );busTcpServer.LogNet.BeforeSaveToFile += LogNet_BeforeSaveToFile;busTcpServer.OnDataReceived += BusTcpServer_OnDataReceived;busTcpServer.ServerStart( port );button1.Enabled = false;panel2.Enabled = true;}catch (Exception ex){MessageBox.Show( ex.Message );}}private void BusTcpServer_OnDataReceived( byte[] modbus ){if (InvokeRequired){BeginInvoke( new Action<byte[]>( BusTcpServer_OnDataReceived ), modbus );return;}textBox1.AppendText( "接收数据:" + HslCommunication.BasicFramework.SoftBasic.ByteToHexString(modbus) + Environment.NewLine );}/// <summary>/// 当有日志记录的时候,触发,将日志信息也在主界面进行输出/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void LogNet_BeforeSaveToFile( object sender, HslCommunication.LogNet.HslEventArgs e ){if(InvokeRequired){BeginInvoke( new Action<object, HslCommunication.LogNet.HslEventArgs>( LogNet_BeforeSaveToFile ), sender, e );return;}textBox1.AppendText( e.HslMessage.ToString( ) + Environment.NewLine );}

RTU支持


当客户端进行连接了一个串口线后,也可以方便的让服务器的支持同时支持串口访问,

busTcpServer.StartSerialPort( "Com3" );

默认的串口参数是9600波特率。8位数据位,无奇偶校验,1位停止位,当然也可以自行指定波特率

busTcpServer.StartSerialPort( "Com3",9600 );

还可以传入一个初始化的委托方法,可以实现任意的支持。

创建数据订阅


当客户端进行发送读写指定时,如果你使用了OnDataReceived事件,都会触发,当然可能这并不是你需要的,如果我们相对一个地址上的数据进行监视,也就当有客户端写入的时候触发,或者是当这个数据更改了的时候触发,那么我们可以创建自己的数据订阅器:

        private void button2_Click( object sender, EventArgs e ){// 点击数据监视ModBusMonitorAddress monitorAddress = new ModBusMonitorAddress( );monitorAddress.Address = ushort.Parse( textBox6.Text );monitorAddress.OnChange += MonitorAddress_OnChange;monitorAddress.OnWrite += MonitorAddress_OnWrite;busTcpServer.AddSubcription( monitorAddress );button2.Enabled = false;}private void MonitorAddress_OnWrite( ModBusMonitorAddress monitor, short value ){// 当有客户端写入时就触发}private void MonitorAddress_OnChange( ModBusMonitorAddress monitor, short befor, short after ){// 当该地址的值更改的时候触发if(InvokeRequired){BeginInvoke( new Action<ModBusMonitorAddress, short, short>( MonitorAddress_OnChange ), monitor, befor, after );return;}textBox9.Text = after.ToString( );label11.Text = "写入时间:" + DateTime.Now.ToString( ) + " 修改前:" + befor + " 修改后:" + after;}

当然,你可以只使用其中的一种数据订阅,比如上述的操作,一旦有客户端写入地址0x01的数据(无论是写入一个寄存器还是批量),那么马上会触发如下的方法。注意,从服务器自身写入的数据不会触发,所有的订阅都可以关联同一个方法,根据地址的不同来区分。

特别说明:

服务器只负责接受Modbus TCP协议的数据,无论客户端发了读写指令,都会触发接收事件。在服务器端有一个数据池,存储了线圈数据和寄存器数据,模拟了一个真实的设备,允许客户端使用Modbus tcp协议对服务器进行数据读写,并返回真实的数据,如果设备往这个服务器的寄存器地址写了100,那么服务器端或者其他客户端去读取寄存器100地址的值的时候,那么也是100.

下面演示了一些简单的数据读写,用于服务器端进行操作的。

            bool Coil100 = busTcpServer.ReadCoil( "100" );                  // 读线圈100的值bool[] Coil100_109 = busTcpServer.ReadCoil( "100", 10 );        // 读线圈数组short Short100 = busTcpServer.ReadInt16( "100" );       // 读取寄存器值ushort UShort100 = busTcpServer.ReadUInt16( "100" );    // 读取寄存器ushort值int Int100 = busTcpServer.ReadInt32( "100" );             // 读取寄存器int值uint UInt100 = busTcpServer.ReadUInt32( "100" );          // 读取寄存器uint值float Float100 = busTcpServer.ReadFloat( "100" );       // 读取寄存器Float值long Long100 = busTcpServer.ReadInt64( "100" );          // 读取寄存器long值ulong ULong100 = busTcpServer.ReadUInt64( "100" );       // 读取寄存器ulong值double Double100 = busTcpServer.ReadDouble( "100" );    // 读取寄存器double值busTcpServer.WriteCoil( "100", true );              // 写线圈的通断busTcpServer.Write( "100", (short)5 );      // 写入short值busTcpServer.Write( "100", (ushort)45678 ); // 写入ushort值busTcpServer.Write( "100", 12345667 );      // 写入int值busTcpServer.Write( "100", (uint)12312312 );// 写入uint值busTcpServer.Write( "100", 123.456f );      // 写入float值busTcpServer.Write( "100", 1231231231233L );// 写入long值busTcpServer.Write( "100", 1212312313UL );  // 写入ulong值busTcpServer.Write( "100", 123.456d );      // 写入double值

读写离散量:

 bool value_100 = busTcpServer.ReadDiscrete( "100" ); // 读取地址100的离散量bool[] value_100_109 =  busTcpServer.ReadDiscrete( "100", 10  ); // 读取数据busTcpServer.WriteDiscrete( "100", true);    // 地址100为true
busTcpServer.WriteDiscrete( "100", new bool[]{true,true}); // 地址100-101为true

  

读写输入寄存器:

输入寄存器的读写方式和寄存器的是一致的,只是地址改一下就好了,也即使用富地址的方式,举个例子,写输入寄存器地址100为123

 busTcpServer.Write( "x=4;100", (short)123 );

之前写100,现在改成"x=4;123"   x=4也即是使用功能码04,那么之前寄存器的地址100等效于"x=3;100"

其他格式的数据参照这个即可。

过滤客户端:

支持对客户端的IP地址过滤,禁止不信任的Ip登录

modbusTcpServer.SetTrustedIpAddress( new List<string>( "192.168.0.100", "192.168.0.101") );

如果要移除限制,重新恢复所有的客户端登录,那么按照如下操作

modbusTcpServer.SetTrustedIpAddress( null );

主动连接客户端:

本服务器支持主动连接客户端,然后再进行数据交互,这种情况在有些工业上应用还是很广泛的

OperateResult connect = busTcpServer.ConnectHslAlientClient( "192.168.0.111", "10000", "12345678901" );if (connect.IsSuccess){MessageBox.Show( "连接成功!" );}else{MessageBox.Show( "连接失败!原因:" + connect.Message );}}

需要制定对方的IP地址,端口号,以及唯一识别码,目前使用的注册包协议为HslAlien协议,过几天专门写一篇这个协议的博文,如果需要连接其他定制的客户端,请联系作者进行定制。

注意:

数据串的第7个字节为Modbus的站号信息,如下界面是FF,也即255,可以以此来区分不同的设备发来的数据信息,所以此处一个服务器实例挂的最大客户端数为256台设备,前两个字节为消息的头序列,如果设备可以固定消息头,用这个来标识设备的话,就可以区分65536台设备。

创作不易,感谢打赏:

参考链接:

如果你对MODBUS TCP不熟悉,那么请参照如下地址,我就是参照该地址的博客开发的代码:

http://blog.csdn.net/thebestleo/article/details/52269999

转载于:https://www.cnblogs.com/dathlin/p/7782315.html

C# 开发ModBus的服务器程序 实现ModBus数据总站 搭建自定义的Modbus服务器 同时支持tcp和rtu...相关推荐

  1. 安卓服务器又维护了,数据互通|安卓部分区服服务器数据互通维护公告

    原标题:数据互通|安卓部分区服服务器数据互通维护公告 亲爱的球队经理人: 为了提供更好的游戏体验,同时也给各位玩家提供更多交流和互动的机会,<中超风云>预计于4月25日14点进行安卓平台服 ...

  2. 性能追击:万字长文30+图揭秘8大主流服务器程序线程模型 | Node.js,Apache,Nginx,Netty,Redis,Tomcat,MySQL,Zuul

    本文为<高性能网络编程游记>的第六篇"性能追击:万字长文30+图揭秘8大主流服务器程序线程模型". 最近拍的照片比较少,不知道配什么图好,于是自己画了一个,凑合着用,让 ...

  3. 性能追击:30+图详解8大主流服务器程序线程模型展示

    看大佬如何用30+图片揭秘8大主流服务器程序线程模型: 最近拍的照片比较少,不知道配什么图好,于是自己画了一个,凑合着用,让大家见笑了. 本文我们来探索一下主流的各种应用服务器的网络处理模型,看看大家 ...

  4. 性能追击:万字长文 30+ 图 8 大主流服务器程序线程模型展示

    看大佬如何用 30+图片揭秘 8 大主流服务器程序线程模型: 最近拍的照片比较少,不知道配什么图好,于是自己画了一个,凑合着用,让大家见笑了. 本文我们来探索一下主流的各种应用服务器的网络处理模型,看 ...

  5. 用于开发语音 AI 应用程序的 GPU 加速 SDK

    NVIDIA Riva 简介:用于开发语音 AI 应用程序的 GPU 加速 SDK 语音 AI 用于多种应用,包括联络中心的座席助理以增强人类座席的能力.智能虚拟助理 (IVA) 的语音界面以及视频会 ...

  6. Web服务器程序解释请求消息并作出响应

    图6.7展示了服务器程序的工作过程,这个过程不仅限于Web服务器,对于各种服务器程序都是共通的,收发数据的过程也是大同小异的.各种服务器程序的不同点在于图中(b)客户端通信部分的第一行调用read后面 ...

  7. 威纶通触摸屏与温控器进行MODBUS通信并通过宏指令将数据发送给PLC的具体方法

    威纶通触摸屏与温控器进行MODBUS通信并通过宏指令将数据发送给PLC的具体方法 温控器参数设置: RTU协议.从站站号地址.波特率9600.数据长度8位.EVEN校验.停止位1以及热电偶类型为K型 ...

  8. 计算机网络套接字编程实验-TCP单进程循环服务器程序与单进程客户端程序(简单回声)

    1.实验系列 ·Linux NAP-Linux网络应用编程系列 2.实验目的 ·理解并掌握在程序运行时从命令行读取数据的C语言编程方法: ·理解并掌握基于命令参数设置并获取IP与Port的C语言编程方 ...

  9. 静态Web服务器-返回固定页面数据

    1. 开发自己的静态Web服务器 实现步骤: 编写一个TCP服务端程序 获取浏览器发送的http请求报文数据 读取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器. HTTP响应报文数据 ...

最新文章

  1. Redis配置主从数据,实现主从库之间数据同步
  2. iOS 之 内存管理
  3. linux怎么用jconsole_jconsole监控上Linux上的JVM
  4. 报数退圈问题(C语言)
  5. php.ini-development和php.ini-production的区别
  6. [Python][小知识][NO.3] Python 使用系统默认浏览器打开指定URL的网址
  7. iOS10 打开APP设置界面和WIFI界面
  8. 这些常见的分布式存储系统,你是否都了解?
  9. 闪存必须解决的三大问题
  10. Java面向对象编程篇3——接口与抽象类
  11. Auto 和 Decltye 的区别
  12. 零基础带你学习MySQL—多子句查询(十九)
  13. excel日期跳过休息日_休息一下Excel游戏
  14. App消息推送策略:推送用户感兴趣的内容
  15. nmap下载及安装过程。
  16. Spring Cloud源码阅读(一)
  17. 新概念二册 Lesson 20 One man in a boat独坐孤舟 ( 动名词 doing)
  18. gamemaker学习笔记:导入龙骨动画
  19. c语言编程符号函数sgn,在C/C中是否有标准符号函数(signum,sgn)?
  20. Pure Virtual Function

热门文章

  1. C语言找人物坐标,CE找游戏人物3D坐标 | 手游网游页游攻略大全
  2. jquery --为当前 li下的a 添加样式
  3. 实训期间的开发过程及心得体会
  4. 创业需要宽广的心胸吗--leo看赢在中国第三季(6)
  5. JavaScript检查数组中是否有重复值
  6. C++刻晴炸弹人小游戏(开发环境为codeblocks)
  7. 【GCC】2: RTCP cc-feeback 抓包对比协议
  8. mysql答辩会问什么_计算机科学与技术专业,毕设答辩会问什么问题?
  9. 【Ceph】Ceph常用命令|Ceph配置参数和命令解析|ceph管理
  10. Matlab傅里叶级数展开(附结果图)