C# 构建S7服务器 西门子的虚拟服务器 测试通讯 HslCommunication应用
本文将使用一个gitHub开源的组件技术来实现S7服务器的功能,使用的是基于以太网的TCP/IP实现,不需要额外的组件
github地址:https://github.com/dathlin/HslCommunication 如果喜欢可以star或是fork,还可以打赏支持。
官网地址:http://www.hslcommunication.cn/
联系作者及加群方式:http://www.hslcommunication.cn/Cooperation
演示的demo,快速开发,下载demo程序,您可以进行初步的访问的测试,免去了您写测试程序的麻烦,快速实验通讯,测试开发软件的操作。
下载地址为:HslCommunicationDemo.zip
演示的服务器截图为:
再配合本组件支持的客户端通讯、就可以实现自我测试。
两者的地址格式是一致的。
服务器端支持4种地址:输入的I0,输出的Q0,中间寄存器M100,DB块,DB1.100
自定义服务器开发
实际的场景中,很有可能你需要的是一个可以测试的demo,当你在开发上位机,SCADA软件,监控软件,历史追述软件的时候,还没有PLC来给你测试,开发,这时候就可以使用本demo来实现模拟服务器的功能。
当然还有另一种需求,当你要集成一个服务器到你自己的程序中的时候,就可以使用本组件来实现,而要集成一个自定义的服务器将会是非常的简单。
1. 安装组件,可以通过下面的指令,或是nuget可视化的客户端来安装
Install-Package HslCommunication
2. 引用命名空间:
using HslCommunication.Profinet.Siemens; using HslCommunication;
3. 写代码。这个服务器可以是在你的窗体中,当然也可以在你的自定义类中,下面的举例在类中定义(窗体的本质也是类)
private SiemensS7Server s7NetServer; public void S7Start() { try { s7NetServer = new SiemensS7Server(); s7NetServer.ServerStart(102); } catch(Exception ex) { Console.Write("Failed:" + HslCommunication.BasicFramework.SoftBasic.GetExceptionMessage(ex)); } }
这样就启动了一个最基本的西门子虚拟服务器了。然后就可以用Hsl组件去读写数据了。端口号是102。
4. 服务器操作数据信息,有了上述的例子后,我们来扩充服务器的功能,比如在服务器端进行读写数据信息。如下的代码举例的一般类型的读写操作说明。
// 此处以M100寄存器作为示例 bool bool_M100_0 = s7NetServer.ReadBool("M100.0").Content; byte byte_M100 = s7NetServer.ReadByte("M100").Content; // 读取M100的值 short short_M100 = s7NetServer.ReadInt16("M100").Content; // 读取M100-M101组成的字 ushort ushort_M100 = s7NetServer.ReadUInt16("M100").Content; // 读取M100-M101组成的无符号的值 int int_M100 = s7NetServer.ReadInt32("M100").Content; // 读取M100-M103组成的有符号的数据 uint uint_M100 = s7NetServer.ReadUInt32("M100").Content; // 读取M100-M103组成的无符号的值 float float_M100 = s7NetServer.ReadFloat("M100").Content; // 读取M100-M103组成的单精度值 long long_M100 = s7NetServer.ReadInt64("M100").Content; // 读取M100-M107组成的大数据值 ulong ulong_M100 = s7NetServer.ReadUInt64("M100").Content; // 读取M100-M107组成的无符号大数据 double double_M100 = s7NetServer.ReadDouble("M100").Content; // 读取M100-M107组成的双精度值 string string_M100 = s7NetServer.ReadString("M100", 10).Content; // 读取M100-M109组成的ASCII字符串数据 // 读取数组 short[] short_M100_array = s7NetServer.ReadInt16("M100", 10).Content; // 读取M100-M101组成的字 ushort[] ushort_M100_array = s7NetServer.ReadUInt16("M100", 10).Content; // 读取M100-M101组成的无符号的值 int[] int_M100_array = s7NetServer.ReadInt32("M100", 10).Content; // 读取M100-M103组成的有符号的数据 uint[] uint_M100_array = s7NetServer.ReadUInt32("M100", 10).Content; // 读取M100-M103组成的无符号的值 float[] float_M100_array = s7NetServer.ReadFloat("M100", 10).Content; // 读取M100-M103组成的单精度值 long[] long_M100_array = s7NetServer.ReadInt64("M100", 10).Content; // 读取M100-M107组成的大数据值 ulong[] ulong_M100_array = s7NetServer.ReadUInt64("M100", 10).Content; // 读取M100-M107组成的无符号大数据 double[] double_M100_array = s7NetServer.ReadDouble("M100", 10).Content; // 读取M100-M107组成的双精度值
基本的写入操作
// 此处以M100寄存器作为示例 s7NetServer.Write("M100", true); // 写入M100 bool值 s7NetServer.Write("M100", (byte)123); // 写入M100 byte值 s7NetServer.Write("M100", (short)1234); // 写入M100 short值 s7NetServer.Write("M100", (ushort)45678); // 写入M100 ushort值 s7NetServer.Write("M100", 1234566); // 写入M100 int值 s7NetServer.Write("M100", (uint)1234566); // 写入M100 uint值 s7NetServer.Write("M100", 123.456f); // 写入M100 float值 s7NetServer.Write("M100", 123.456d); // 写入M100 double值 s7NetServer.Write("M100", 123456661235123534L); // 写入M100 long值 s7NetServer.Write("M100", 523456661235123534UL); // 写入M100 ulong值 s7NetServer.Write("M100", "K123456789"); // 写入M100 string值 // 读取数组 s7NetServer.Write("M100", new short[] { 123, 3566, -123 }); // 写入M100 short值 ,W3C0,R3C0 效果是一样的 s7NetServer.Write("M100", new ushort[] { 12242, 42321, 12323 }); // 写入M100 ushort值 s7NetServer.Write("M100", new int[] { 1234312312, 12312312, -1237213 }); // 写入M100 int值 s7NetServer.Write("M100", new uint[] { 523123212, 213, 13123 }); // 写入M100 uint值 s7NetServer.Write("M100", new float[] { 123.456f, 35.3f, -675.2f }); // 写入M100 float值 s7NetServer.Write("M100", new double[] { 12343.542312d, 213123.123d, -231232.53432d }); // 写入M100 double值 s7NetServer.Write("M100", new long[] { 1231231242312, 34312312323214, -1283862312631823 }); // 写入M100 long值 s7NetServer.Write("M100", new ulong[] { 1231231242312, 34312312323214, 9731283862312631823 }); // 写入M100 ulong值
5. 高级读写,所谓的高级的读写,就是读写基础的字节数据,相关的转换需要自己来完成
读数据的例子如下:
OperateResult<byte[]> read = s7NetServer.Read("M100", 8); if (read.IsSuccess) { float temp = s7NetServer.ByteTransform.TransInt16(read.Content, 0) / 10f; float press = s7NetServer.ByteTransform.TransInt16(read.Content, 2) / 100f; int count = s7NetServer.ByteTransform.TransInt32(read.Content, 2); // do something } else { // failed }
写数据的例子:
// 拼凑数据,这样的话,一次通讯就完成数据的全部写入 byte[] buffer = new byte[8]; s7NetServer.ByteTransform.TransByte((short)1234).CopyTo(buffer, 0); s7NetServer.ByteTransform.TransByte((short)2100).CopyTo(buffer, 2); s7NetServer.ByteTransform.TransByte(12353423).CopyTo(buffer, 4); OperateResult write = s7NetServer.Write("M100", buffer); if (write.IsSuccess) { // success } else { // failed } // 上面的功能等同于三个数据分别写入,下面的方式性能稍微差一点点,几乎看不出来 // s7NetServer.Write( "M100", (short)1234 ); // s7NetServer.Write( "M100", (short)2100 ); // s7NetServer.Write( "M100", 12353423 );
6. 服务器的高级操作,日志配置
public void S7Start2() { try { s7NetServer = new SiemensS7Server(); s7NetServer.LogNet = new HslCommunication.LogNet.LogNetSingle("logs.txt"); // 配置日志信息 s7NetServer.ServerStart(102); } catch (Exception ex) { Console.Write("Failed:" + HslCommunication.BasicFramework.SoftBasic.GetExceptionMessage(ex)); } }
7. 限制客户端的ip地址,下面举例是仅仅允许本机登录。
public void S7Start3() { try { s7NetServer = new SiemensS7Server(); s7NetServer.LogNet = new HslCommunication.LogNet.LogNetSingle("logs.txt"); // 配置日志信息 s7NetServer.SetTrustedIpAddress(new List<string>() { "127.0.0.1" }); // 仅仅限制本机客户端读写 s7NetServer.ServerStart(102); } catch (Exception ex) { Console.Write("Failed:" + HslCommunication.BasicFramework.SoftBasic.GetExceptionMessage(ex)); } }
8. 捕捉数据接收事件,每当客户端进行数据交互的时候,都可以触发事件,然后可以对报文进行分析,比如说,分析出了客户端写入了地址M100的数据,就触发下自定义的操作,在这种情况下就可以使用如下的代码
public void S7Start4() { try { s7NetServer = new SiemensS7Server(); s7NetServer.LogNet = new HslCommunication.LogNet.LogNetSingle("logs.txt"); // 配置日志信息 s7NetServer.SetTrustedIpAddress(new List<string>() { "127.0.0.1" }); // 仅仅限制本机客户端读写 s7NetServer.OnDataReceived += S7NetServer_OnDataReceived; s7NetServer.ServerStart(102); } catch (Exception ex) { Console.Write("Failed:" + HslCommunication.BasicFramework.SoftBasic.GetExceptionMessage(ex)); } } private void S7NetServer_OnDataReceived(object sender, byte[] data) { Console.WriteLine(HslCommunication.BasicFramework.SoftBasic.ByteToHexString(data, ' ')); // 打印客户端发送的数据 }
9. 示例,举个简单的例子,用来模拟PLC的程序,整个PLC的程序无非是对输入输出进行代码操作,PLC的优势就是实时性,所以我们可以基于本组件开发一个虚拟化的PLC,可用于简单的模拟,调试,测试操作。