FF FE 00 00 0A 怎样在PLCphp在哪里编写写

本文将使用一个NuGet公开的组件技术来读写三菱PLC和西门子plc数据,使用的是基于以太网的TCP/IP实现,不需要额外的组件,读取操作只要放到后台线程就不会卡死线程,本组件支持超级方便的高性能读写操作
在Visual Studio 中的NuGet管理器中可以下载安装,也可以直接在NuGet控制台输入下面的指令安装:
Install-Package HslCommunication
&如果需要教程:Nuget安装教程:
技术支持QQ群:&&(组件的版本更新细节也将第一时间在群里发布)最后编辑日期:日 11:03:56
里面各种小伙伴,为您解答数据交互,编程技巧,如果对本界面提供的API有任何疑问,都可以加群咨询,如果有更好的建议,欢迎提出。
如果你需要在读取PLC数据之后,还要群发客户端来实现远程办公室同步监视,可以参考如下的项目(基于该组件扩展起来的,带有账户验证,版本控制,数据群发,公告管理等等功能)
本文将展示如何配置网络参数及怎样使用代码来访问PLC数据,希望给有需要的人解决一些实际问题。主要对三菱Q系列PLC的X,Y,M,L,B,V,F,S,D,W,R区域的数据读写,对西门子PLC的M,Q,I,DB块的数据读写,亲测有效。
此处使用了网线直接的方式,如果PLC接进了局域网,就可以进行远程读写了^_^
此处使用到了2个命名空间:
using&HslC
using&HslCommunication.P
当我们一个上位机需要读取100台西门子PLC设备(此处只是举个例子,凡是都是使用Modbus tcp的都是一样的)的时候,你采用服务器主动去请求100台设备的机制对性能来说是个极大的考验,如果开100个线程去轮询100台设备,那么性能损失将是非常大的,更不用说再增加设备,如果搭建Modbus tcp服务器,就可以完美的解决性能问题,因为连接的压力将会平均分摊给每一台PLC,服务器端只要新增一个时间戳就可以知道客户端有没有连接上。
我们在100台PLC里都增加发送Modbus tcp方法,将数据发送到服务器的ip和端口上去,服务器根据站号来区分设备。这样就可以搭建一个高性能总站。&本组件支持快速搭建一个高性能的Modbus tcp总站。
关于两种模式
在PLC端,包括三菱和西门子篇二以及Modbus Tcp客户端的访问器上,都支持两种模式,短连接模式和长连接模式,现在就来解释下什么原理。
短连接:每次读写都是一个单独的请求,请求完毕也就关闭了,如果服务器的端口仅仅支持单连接,那么关闭后这个端口可以被其他连接复用,但是在频繁的网络请求下,容易发生异常,会有其他的请求不成功,尤其是多线程的情况下。
长连接:创建一个公用的连接通道,所有的读写请求都利用这个通道来完成,这样的话,读写性能更快速,即时多线程调用也不会影响,内部有同步机制。如果服务器的端口仅仅支持单连接,那么这个端口就被占用了,比如三菱的端口机制,西门子的Modbus tcp端口机制也是这样的。以下代码默认使用长连接,性能更高,还支持多线程同步。
在短连接的模式下,每次请求都是单独的访问,所以没有重连的困扰,在长连接的模式下,如果本次请求失败了,在下次请求的时候,会自动重新连接服务器,直到请求成功为止。另外,尽量所有的读写都对结果的成功进行判断。
关于日志记录
不管是三菱的数据访问类,还是西门子的,还是Modbus tcp访问类,都有一个LogNet属性用来记录日志,该属性是一个接口类,ILogNet,凡事继承该接口的都可以用来记录日志,该日志会在访问失败时,尤其是因为网络的原因导致访问失败时会进行日志记录(如果你为这个LogNet属性配置了真实的日志记录器的话):如果你想使用该记录日志的功能,请参照如下的博客进行实例化:
下面的三篇演示了具体如何去访问PLC的数据,我们在访问完成后,通常需要进行处理,以下的示例项目就演示了后台从PLC读取数据后,前台显示并推送给所有在线客户端的功能,客户端并进行图形化显示,具有一定的参考意义,项目地址为:
下面的图片示例中的左边程序就是服务器程序,它应该和PLC直接连接并接入局域网,然后把数据推送给客户端显示。注意:一个复杂高级的程序就应该把处理逻辑程序和界面程序分开,比如这里的服务器程序实现数据采集,推送,存储。让客户端程序去实现数据的整理,分析,显示,这样即使客户端程序因为BUG奔溃,服务器端仍然可以正常的工作。
三菱PLC篇(如果你的PLC内置了以太网,请配置MC协议通讯)
Q06UDV Plc的访问测试感谢网友:hwdq0012
感想网友:小懒猪雨中人& 的测试,VB程序也可以调用本通讯库
环境:此处以GX Works2为示例,添加以太网模块,型号为QJ71E71-100,组态里添加完成后进行以太网的参数配置,此处需要注意的是:参数的配置对接下来的代码中配置参数要一一对应
注意:在PLC的以太网模块的配置中,无法设置网络号为0,也无法设置站号为0, 所以此处均设置为1,在C#程序中也使用上述的配置,在代码中均配置为0,如果您自定义设置为网络2, 站号8,那么在代码中就要写对应的数据。如果仍然通信失败,重新测试0,0。打开设置:在上图中的打开设置选项,进行其他参数的配置,下图只是举了一个例子,开通了4个端口来支持读写操作:端口号设置规则:
为了不与原先存在的系统发生冲突,您在添加自己的端口时尽量使用您自己的端口。
如果读写都需要,尽可能的将读取端口和写入端口区分开来,这样做比较高性能。
如果您的网络状态不是特别稳定,读取端口使用2个,一个受阻切换另一个读取可以提升系统的稳定性。
本文档仅作组件的测试,所以只用了一个端口作为读写。如果你的程序也使用了一个端口,那么你在读取数据时候, 刚好也在写入(异步操作可能发生这样的情况),那么写入会失败!)(在长连接模式下没有这个问题)三菱PLC的数据主要由两类数据组成,位数据和字数据,在位数据中,例如X,Y,M,L都是位数据,字数据例如D,W。 两类的数据在读取解码上存在一点小差别。(事实上也可以先将16个M先赋值给一个D,读取D数据再进行解析, 在读取M的数量比较多的时候,这样操作效率更高)初始化访问PLC对象如果想使用本组件的数据读取功能,必须先初始化数据访问对象,根据实际情况进行数据的填入。 下面仅仅是测试中的数据:
private&MelsecNet melsec_net =&new&MelsecNet();
private&void&MelsecNetInitialization()
&&&&melsec_net.PLCIpAddress = System.Net.IPAddress.Parse("192.168.0.7");&&
&&&&melsec_net.PortRead = 6000;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&melsec_net.PortWrite = 6001;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&melsec_net.NetworkNumber = 0;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&melsec_net.NetworkStationNumber = 0;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&melsec_net.ConnectTimeout = 500;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&melsec_net.ConnectServer();&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&说明:对象应该放在窗体类下面,此处仅仅针对读取一台设备的plc,也可以在访问的方法中实例化局部对象, 初始化数据,然后读取,该对象几乎不损耗内存,内存垃圾由CLR进行自动回收。此处测试方便,窗体的多个按钮均连接同一台PLC 设备,所以本窗体实例化一个对象即可。
关于两种地址的表示方式
第一种,使用系统的类来标识,比如M200,写成(MelsecDataType.M, 200)的表示形式,这样也可以去MelsecDataType里面找到所有支持的数据类型。
第二种,使用字符串表示,这个组件里所有的读写操作提供字符串表示的重载方法,所有的支持访问的类型对应如下,字符串的表示方式存在十进制和十六进制的区别:
输入继电器:"X100","X1A0"&&&&&&&&&&& // 字符串为十六进制机制
输出继电器:"Y100" ,"Y1A0"&&&&&&&&&&&// 字符串为十六进制机制
内部继电器:"M100","M200"&&&&&&&&&& // 字符串为十进制
锁存继电器:"L100"&&,"L200"&&&&&&&&&& //&字符串为十进制
报警器:&&&&&& "F100", "F200"&&&&&&&&&&& // 字符串为十进制
边沿继电器:"V100" , "V200"&&&&&&&&&&// 字符串为十进制
链接继电器:"B100" , "B1A0"&&&& &&&& // 字符串为十六进制
步进继电器:"S100" , "S200"&&&&&&&&& // 字符串为十进制
数据寄存器:"D100", "D200"&&&&&&&&&& // 字符串为十进制
链接寄存器:"W100"&,"W1A0"&&&&&&&&&// 字符串为十六进制
文件寄存器:"R100","R200"&&&&&&&&&&& // 字符串为十进制
展示一些简单实用基础数据读写,这些数据的读写没有进行严格的是否成功判断(判断方法参照后面的代码),一般网络良好的情况下都会成功,但不排除失败,以下代码仅作测试,所有没有严格判断是否成功:
private&void&userButton22_Click(object&sender, EventArgs e)
&&&&bool&M100 = melsec_net.ReadBoolFromPLC("M100").C&&&&&&&&&&&&
&&&&bool&X1A0 = melsec_net.ReadBoolFromPLC("X1A0").C&&&&&&&&&&&&
&&&&bool&Y1A0 = melsec_net.ReadBoolFromPLC("Y1A0").C&&&&&&&&&&&&
&&&&bool&B1A0 = melsec_net.ReadBoolFromPLC("B1A0").C&&&&&&&&&&&&
&&&&short&short_D1000 = melsec_net.ReadShortFromPLC("D1000").C&&&
&&&&ushort&ushort_D1000 = melsec_net.ReadUShortFromPLC("D1000").C&
&&&&int&int_D1000 = melsec_net.ReadIntFromPLC("D1000").C&&&&&&&&&&
&&&&uint&uint_D1000 = melsec_net.ReadUIntFromPLC("D1000").C&&&&&&&
&&&&float&float_D1000 = melsec_net.ReadFloatFromPLC("D1000").C&&&&
&&&&long&long_D1000 = melsec_net.ReadLongFromPLC("D1000").C&&&&&&&
&&&&double&double_D1000 = melsec_net.ReadDoubleFromPLC("D1000").C&
&&&&string&str_D1000 = melsec_net.ReadStringFromPLC("D1000", 10).C&
&&&&melsec_net.WriteIntoPLC("M100",&true);&&&&&&&&&&&&&&&&&&&&&&&&
&&&&melsec_net.WriteIntoPLC("Y1A0",&true);&&&&&&&&&&&&&&&&&&&&&&&&
&&&&melsec_net.WriteIntoPLC("X1A0",&true);&&&&&&&&&&&&&&&&&&&&&&&&
&&&&melsec_net.WriteIntoPLC("B1A0",&true);&&&&&&&&&&&&&&&&&&&&&&&&
&&&&melsec_net.WriteIntoPLC("D1000", (short)1234);&&&&&&&&&&&&&&&&
&&&&melsec_net.WriteIntoPLC("D1000", (ushort)45678);&&&&&&&&&&&&&&
&&&&melsec_net.WriteIntoPLC("D1000", 1234566);&&&&&&&&&&&&&&&&&&&&
&&&&melsec_net.WriteIntoPLC("D1000", (uint)1234566);&&&&&&&&&&&&&&&
&&&&melsec_net.WriteIntoPLC("D1000", 123.456f);&&&&&&&&&&&&&&&&&&&&
&&&&melsec_net.WriteIntoPLC("D1000", 123.456d);&&&&&&&&&&&&&&&&&&&&
&&&&melsec_net.WriteIntoPLC("D1000", 123534L);&&&&&&&&&&
&&&&melsec_net.WriteAsciiStringIntoPLC("D1000",&"K");&&&&&&
下面再分别讲解严格的操作,以及批量化的复杂的读写操作,假设你要读取1000个M,循环读取1千次可能要3秒钟,如果用了下面的批量化读取,只需要50ms,但是需要你对字节的原理比较熟悉才能得心应手的处理X,Y,M,L,F,V,B,S位数据的读写说明
X 输入继电器
Y 输出继电器
M 内部继电器
L 锁存继电器
V 边沿继电器
B 链接继电器
S 步进继电器
本小节将展示八种位数据的读取,虽然更多的时候只是读取D数据即可,或者是将位数据批量挪到D数据中, 但是在此处仍然进行介绍单独的读取X,Y,M,L,F,V,B,S,由于这八种读取手法一致,故针对M数据进行介绍,其他的您可以自己测试。如下方法演示读取了M200-M209这10个M的值,注意:读取长度必须为偶数,即时写了奇数,也会补齐至偶数,读取和写入的最大长度为7168,否则报错。如需实际需求确实大于7168的,请分批次读取。返回值解析:如果读取正常则共返回10个字节的数据,以下示例数据进行批量化的读取
private&void&userButton20_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = melsec_net.ReadFromPLC("M200", 10);
&&&&if&(read.IsSuccess)
&&&&&&&&bool&M200 = read.Content[0] == 1;
&&&&&&&&bool&M201 = read.Content[1] == 1;
&&&&&&&&bool&M202 = read.Content[2] == 1;
&&&&&&&&bool&M203 = read.Content[3] == 1;
&&&&&&&&bool&M204 = read.Content[4] == 1;
&&&&&&&&bool&M205 = read.Content[5] == 1;
&&&&&&&&bool&M206 = read.Content[6] == 1;
&&&&&&&&bool&M207 = read.Content[7] == 1;
&&&&&&&&bool&M208 = read.Content[8] == 1;
&&&&&&&&bool&M209 = read.Content[9] == 1;
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
private&void&userButton21_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = melsec_net.ReadFromPLC("X200", 16);
&&&&if&(read.IsSuccess)
&&&&&&&&bool&X200 = read.Content[0] == 1;
&&&&&&&&bool&X201 = read.Content[1] == 1;
&&&&&&&&bool&X202 = read.Content[2] == 1;
&&&&&&&&bool&X203 = read.Content[3] == 1;
&&&&&&&&bool&X204 = read.Content[4] == 1;
&&&&&&&&bool&X205 = read.Content[5] == 1;
&&&&&&&&bool&X206 = read.Content[6] == 1;
&&&&&&&&bool&X207 = read.Content[7] == 1;
&&&&&&&&bool&X208 = read.Content[8] == 1;
&&&&&&&&bool&X209 = read.Content[9] == 1;
&&&&&&&&bool&X20A = read.Content[10] == 1;
&&&&&&&&bool&X20B = read.Content[11] == 1;
&&&&&&&&bool&X20C = read.Content[12] == 1;
&&&&&&&&bool&X20D = read.Content[13] == 1;
&&&&&&&&bool&X20E = read.Content[14] == 1;
&&&&&&&&bool&X20F = read.Content[15] == 1;
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
private&void&userButton3_Click(object&sender, EventArgs e)
&&&&bool[] values =&new&bool[] {&true,&false,&false,&true,&true&};
&&&&OperateResult write = melsec_net.WriteIntoPLC("M100", values);
&&&&if&(write.IsSuccess)
&&&&&&&&TextBoxAppendStringLine("写入成功");
&&&&&&&&MessageBox.Show(write.ToMessageShowString());
&错误说明:有可能因为站号网络号没有配置正确返回有错误代号没有错误信息, 也有可能因为网络问题导致没有连接上,此时会有连接不上的错误信息。下面展示的是后台线程循环读取的情况,事实上在实际的使用过程中经常会碰见的情况。下面的方法需要 放到单独的线程中,同理,访问D数据时也是按照下面循环就行,此处不再赘述。
while&(true)
&&&&OperateResult&byte[]& read = melsec_net.ReadFromPLC("M200", 10);
&&&&if&(read.IsSuccess)
&&&&&&&&textBox2.BeginInvoke(new&Action(delegate
&&&&&&&&&&&&textBox2.Text =&"M201:"&+ (read.Content[1] == 1 ?&"通"&:&"断");
&&&&&&&&}));
&&&&&&&&LogHelper.save(read.ToMessageShowString());
&&&&System.Threading.Thread.Sleep(1000);
  D,W,R字数据的读写操作此处读取针对中间存在整数数据的情况,因为两者读取方式相同,故而只演示一种数据读取, 使用该组件读取数据,一次最多读取或写入960个字,超出则失败。 如果读取的长度确实超过限制,请考虑分批读取。
private&void&button1_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = melsec_net.ReadFromPLC("D6000", 21);
&&&&if(read.IsSuccess)
&&&&&&&&textBox2.Text =&"D6000:"&+ melsec_net.GetShortFromBytes(read.Content, 0);
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
private&void&button2_Click(object&sender, EventArgs e)
&&&&short[] values =&new&short[4] { ,
&&&&OperateResult write = melsec_net.WriteIntoPLC(MelsecDataType.D, 6000, values);
&&&&if(write.IsSuccess)
&&&&&&&&textBox2.Text =&"写入成功";
&&&&&&&&MessageBox.Show(write.ToMessageShowString());
ASCII字符串数据的读写在实际项目中,有可能会碰到PLC存储了规格数据,或是条码数据,这些数据是以ASCII编码形式存在, 我们需要把数据进行读取出来用于显示,保存等操作。下面演示读取指定长度的条码数据,数据的数据存放在D中, 长度应该为存储条码的最大长度,也即是占用了5个D,一个D可以存储2个ASCII码字符:
private&void&button7_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = melsec_net.ReadFromPLC(MelsecDataType.D, 2000, 5);
&&&&if&(read.IsSuccess)
&&&&&&&&textBox2.Text = Encoding.ASCII.GetString(read.Content);
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
private&void&button8_Click(object&sender, EventArgs e)
&&&&OperateResult write = melsec_net.WriteAsciiStringIntoPLC(MelsecDataType.D, 2000,&"K");
&&&&if&(write.IsSuccess)
&&&&&&&&textBox2.Text =&"写入成功";
&&&&&&&&MessageBox.Show(write.ToMessageShowString());
需要注意的是,如果第一次在D中写入了"K",第二次写入了"K6666",那么读取D的条码数据会读取到 K,如果要避免这种情况,则需要在写入条码的时候,指定总长度,该长度必须为偶数, 不然也会自动补0,小于该长度时,自动补零,大于该长度时,自动截断数据,具体的使用方法如下:
private&void&button8_Click(object&sender, EventArgs e)
&&&&OperateResult write = melsec_net.WriteAsciiStringIntoPLC(MelsecDataType.D, 2000,&"K6666", 10);
&&&&if&(write.IsSuccess)
&&&&&&&&textBox2.Text =&"写入成功";
&&&&&&&&MessageBox.Show(write.ToMessageShowString());
中文及特殊字符的读写在需要读写复杂的字符数据时,上述的ASCII编码已经不能满足要求,虽然使用读写的基础方法可以实现任意数据的读写, 但是此处为了方便,还是提供了一个方便的方法来读写中文数据,采用Unicode编码的字符, 该编码下的一个字符占用一个D或W来存储。如下将演示,读写方法,基本用途和上述 ASCII编码的读写一致。
private&void&button9_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = melsec_net.ReadFromPLC(MelsecDataType.D, 3000, 10);
&&&&if&(read.IsSuccess)
&&&&&&&&textBox2.Text = Encoding.Unicode.GetString(read.Content);
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
private&void&button10_Click(object&sender, EventArgs e)
&&&&OperateResult write = melsec_net.WriteUnicodeStringIntoPLC(MelsecDataType.D, 3000,&"测试数据test", 10);
&&&&if&(write.IsSuccess)
&&&&&&&&textBox2.Text =&"写入成功";
&&&&&&&&MessageBox.Show(write.ToMessageShowString());
 一个实际中复杂的例子演示实际中可能碰到的情况会很复杂,一台设备中需要上传的数据包含了温度,压力,产量,规格等等信息,在一串数据中 会包含各种各样的不同的数据,上述的读取D,读取M,读取条码的方式不太好用,所以此处做一个完整示例的演示,假设我们需要读取 D的数据,假设D4000存放了温度数据,55.1℃在D中为551,D4001存放了压力数据,1.23MPa在D中存放为123,D4002存放了 设备状态,0为停止,1为运行,D4003存放了产量,1000就是指1000个,D4004备用,D存放了规格,以下代码演示如何去解析数据:
private&void&button29_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = melsec_net.ReadFromPLC(MelsecDataType.D, 4000, 10);
&&&&if&(read.IsSuccess)
&&&&&&&&double&温度 = BitConverter.ToInt16(read.Content, 0) / 10d;
&&&&&&&&double&压力 = BitConverter.ToInt16(read.Content, 2) / 100d;
&&&&&&&&bool&IsRun = BitConverter.ToInt16(read.Content, 4) == 1;
&&&&&&&&int&产量 = BitConverter.ToInt16(read.Content, 6);
&&&&&&&&string&规格 = Encoding.ASCII.GetString(read.Content, 10, 10);
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
究极数据读取展示,用于测试你自己的报文以及扩展自己的更高级,更变态的API,以下演示,使用这个高级模式,写入M100,True的操作:
我们要写入的字节数组HEX表示形式为:50 00 00 FF FF 03 00 0D 00 0A 00 01 14 01 00 64 00 00 90 01 00 10
private&void&userButton23_Click(object&sender, EventArgs e)
&&&&byte[] buffer = HslCommunication.BasicFramework.SoftBasic.HexStringToBytes("50 00 00 FF FF 03 00 0D 00 0A 00 01 14 01 00 64 00 00 90 01 00 10");
&&&&OperateResult&byte[]& operate = melsec_net.ReadFromServerCore(buffer);
&&&&if(operate.IsSuccess)
&&&&&&&&MessageBox.Show(HslCommunication.BasicFramework.SoftBasic.ByteToHexString(operate.Content));
&&&&&&&&MessageBox.Show(operate.ToMessageShowString());
西门子篇一(S5兼容的FETCH/WRITE协议,配置比较麻烦,不再维护,s300系列和1200系列参照西门子篇二)
环境:此处使用了STEP 7V5.5 sp4编程软件作为示例,在添加以太网模块(6GK7 343-1EX30-0E0 CP343-1)到组态中时,可以设置IP地址及子网掩码, 此处测试使用,所以不使用路由器,如果您的西门子需要连接到内网中的话,需要配置路由器。目前只支持M,I,Q数据的读写。 然后点击新建,创建一个Ethernet(1)网络。以太网参数配置如下图:将以太网的模块添加到机架中以后,现在打开网络组态&,打开后点击组态上的PLC模块。会出现如下界面,在箭头出进行双击操作,可以弹出对话框,并进行一系列操作:
按照上面一套操作下来,创建了一个读取的端口,端口号为2000,后面有用,需要记住, 按照上述的步骤再创建一个写入的端口,只有最后一步不一致,如下:配置完之后的效果图如下,新建了两个端口,一个用于读取数据,一个用于写入数据。 &strong&注意:设置完成后一定要写入到PLC才算真的完成。&/strong&如上图所示,共配置了两个端口号,配置完成后需要进行重启PLC,端口的配置原则如下:
端口号设置规则:
为了不与原先存在的系统发生冲突,您在添加自己的端口时尽量使用您自己的端口。
如果读写都需要,尽可能的将读取端口和写入端口区分开来,这样做比较高性能。
如果您的网络状态不是特别稳定,读取端口使用2个,一个受阻切换另一个读取可以提升系统的稳定性。
西门子PLC的数据种类其实只有一种,就是byte数据,更大的数据用多个byte来组合,位数据就是byte上的位,所以会比三菱的简单一点点,好处理一点。初始化访问PLC对象如果想使用本组件的数据读取功能,必须先初始化数据访问对象,根据实际情况进行数据的填入。 下面仅仅是测试中的数据:
private&SiemensNet siemens_net =&new&SiemensNet();
private&void&FormPlcTest_Load(object&sender, EventArgs e)
&&&&siemens_net.ConnectTimeout = 500;
&&&&siemens_net.PortRead = 2000;
&&&&siemens_net.PortReadBackup = 2002;
&&&&siemens_net.PortWrite = 2001;
&&&&siemens_net.PLCIpAddress = System.Net.IPAddress.Parse("192.168.0.6");
说明:对象应该放在窗体类下面,此处仅仅针对读取一台设备的plc,也可以在访问的方法中实例化局部对象, 初始化数据,然后读取,该对象几乎不损耗内存,内存垃圾由CLR进行自动回收。此处测试方便,窗体的多个按钮均连接同一台PLC 设备,所以本窗体实例化一个对象即可。M,I,Q数据读写由于西门子这三个数据种类相同,操作时一模一样,所以此处只展示读写M寄存器的例子。如下方法演示读取了M100-M101这2个M的值,返回值解析:如果读取正常则共返回2个字节的数据,以下示例数据
private&void&button3_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = siemens_net.ReadFromPLC(SiemensDataType.M, 100, 2);
&&&&if(read.IsSuccess)
&&&&&&&&textBox4.Text =&"M100:"&+ read.Content[0] +&" M101:"&+ read.Content[1];
&&&&&&&&bool&M100_0 = (read.Content[0] & 0x01) == 0x01;
&&&&&&&&bool&M100_1 = (read.Content[0] & 0x02) == 0x02;
&&&&&&&&bool&M100_2 = (read.Content[0] & 0x04) == 0x04;
&&&&&&&&bool&M100_3 = (read.Content[0] & 0x08) == 0x08;
&&&&&&&&bool&M100_4 = (read.Content[0] & 0x10) == 0x10;
&&&&&&&&bool&M100_5 = (read.Content[0] & 0x20) == 0x20;
&&&&&&&&bool&M100_6 = (read.Content[0] & 0x40) == 0x40;
&&&&&&&&bool&M100_7 = (read.Content[0] & 0x80) == 0x80;
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
private&void&button4_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& write = siemens_net.WriteIntoPLC(SiemensDataType.M, 100,&new&byte[] { 81, 22, 124 });
&&&&if&(write.IsSuccess)
&&&&&&&&textBox4.Text =&"写入成功";
&&&&&&&&MessageBox.Show(write.ToMessageShowString());
错误说明:有可能因为站号网络号没有配置正确返回有错误代号没有错误信息, 也有可能因为网络问题导致没有连接上,此时会有连接不上的错误信息。下面展示的是后台线程循环读取的情况,事实上在实际的使用过程中经常会碰见的情况。下面的方法需要 放到单独的线程中,同理,访问D数据时也是按照下面循环就行,此处不再赘述。
while&(true)
&&&&OperateResult&byte[]& read = siemens_net.ReadFromPLC(SiemensDataType.M, 100, 2);
&&&&if&(read.IsSuccess)
&&&&&&&&textBox2.BeginInvoke(new&Action(delegate
&&&&&&&&&&&&textBox4.Text =&"M100:"&+ read.Content[0] +&" M101:"&+ read.Content[1];
&&&&&&&&}));
&&&&&&&&LogHelper.save(read.ToMessageShowString());
&&&&System.Threading.Thread.Sleep(1000);
整数数据读写(一个数据由2个byte组成)虽然上述实现了M数据的读写,但是只能表示0-255的数据,想要支持更大的数据,需要自己指定规则, 这就需要你对数据和字节原理非常清晰才能实现,为了方便,此处提供了读写双字节数据的功能,先演示读取M100-M106 的数据,对应有三个双字节数据,代码如下:
private&void&button15_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = siemens_net.ReadFromPLC(SiemensDataType.M, 100, 6);
&&&&if&(read.IsSuccess)
&&&&&&&&short[] result = siemens_net.GetArrayFromBytes(read.Content);
&&&&&&&&textBox4.Text =&"M100:"&+ result[0] +&",M102:"&+ result[1] +&",M104:"&+ result[2];
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
接下来介绍写入整数数据的操作,例如,我们要使得M100,M101=2456,M102,M103=4567,M104,M105=-124,那么代码如下:
private&void&button16_Click(object&sender, EventArgs e)
&&&&OperateResult write = siemens_net.WriteIntoPLC(SiemensDataType.M, 100,&new&short[] { , -124 });
&&&&if&(write.IsSuccess)
&&&&&&&&textBox4.Text =&"写入成功";
&&&&&&&&MessageBox.Show(write.ToMessageShowString());
ASCII字符串数据的读写在实际项目中,有可能会碰到PLC存储了规格数据,或是条码数据,这些数据是以ASCII编码形式存在, 我们需要把数据进行读取出来用于显示,保存等操作。下面演示读取指定长度的条码数据,数据的数据存放在M100-M109中, 长度应该为存储条码的最大长度,也即是占用了10个M,一个M可以存储1个ASCII码字符:
private&void&button12_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = siemens_net.ReadFromPLC(SiemensDataType.M, 100, 10);
&&&&if&(read.IsSuccess)
&&&&&&&&textBox4.Text = Encoding.ASCII.GetString(read.Content);
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
下面演示写入条码数据,地址在M100-M109中,所以需要写入10个字符:
private&void&button14_Click(object&sender, EventArgs e)
&&&&OperateResult write = siemens_net.WriteAsciiStringIntoPLC(SiemensDataType.M, 100,&"K");
&&&&if&(write.IsSuccess)
&&&&&&&&textBox4.Text =&"写入成功";
&&&&&&&&MessageBox.Show(write.ToMessageShowString());
需要注意的是,如果第一次在M100-M109中写入了"K",第二次写入了"K6666",那么读取M100-M109的条码数据会读取到K,如果要避免这种情况,则需要在写入条码的时候,指定总长度,该长度 可单数可偶数,具体的使用方法如下:
private&void&button14_Click(object&sender, EventArgs e)
&&&&OperateResult write = siemens_net.WriteAsciiStringIntoPLC(SiemensDataType.M, 100,&"K6666", 10);
&&&&if&(write.IsSuccess)
&&&&&&&&textBox4.Text =&"写入成功";
&&&&&&&&MessageBox.Show(write.ToMessageShowString());
中文及特殊字符的读写在需要读写复杂的字符数据时,上述的ASCII编码已经不能满足要求,虽然使用读写的基础方法可以实现任意数据的读写, 但是此处为了方便,还是提供了一个方便的方法来读写中文数据,采用Unicode编码的字符, 该编码下的一个字符占用两个M来存储。如下将演示,读写方法,基本用途和上述 ASCII编码的读写一致。
private&void&button11_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = siemens_net.ReadFromPLC(SiemensDataType.M, 200, 12);
&&&&if&(read.IsSuccess)
&&&&&&&&textBox4.Text = Encoding.Unicode.GetString(read.Content);
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
在写入的过程中,只演示写入指定长度的(实际中也应该使用这个方法),指定长度的意思为多少个中文。
private&void&button13_Click(object&sender, EventArgs e)
&&&&OperateResult write = siemens_net.WriteUnicodeStringIntoPLC(SiemensDataType.M, 200,&"测试de东西", 10);
&&&&if&(write.IsSuccess)
&&&&&&&&textBox4.Text =&"写入成功";
&&&&&&&&MessageBox.Show(write.ToMessageShowString());
一个实际中复杂的例子演示实际中可能碰到的情况会很复杂,一台设备中需要上传的数据包含了温度,压力,产量,规格等等信息,在一串数据中 会包含各种各样的不同的数据,所以此处做一个完整示例的演示,假设我们需要读取 M100-M116的数据,假设M100,M101存放了温度数据,55.1℃在M中为551,M102,M103存放了压力数据,1.23MPa在M中存放为123,M104存放了 设备状态,0为停止,1为运行,M105,M106存放了产量,1000就是指1000个,M107-M116存放了规格,以下代码演示如何去解析数据:
private&void&button29_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = siemens_net.ReadFromPLC(SiemensDataType.M, 200, 17);
&&&&if&(read.IsSuccess)
&&&&&&&&double&温度 = BitConverter.ToInt16(read.Content, 0) / 10d;
&&&&&&&&double&压力 = BitConverter.ToInt16(read.Content, 2) / 100d;
&&&&&&&&bool&IsRun = read.Content[4] == 1;
&&&&&&&&int&产量 = BitConverter.ToInt16(read.Content, 5);
&&&&&&&&string&规格 = Encoding.ASCII.GetString(read.Content, 7, 10);
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
西门子篇二(采用S7协议下的tcp直接通讯,配置简单,一般PLC都支持)
测试通过的PLC:1200系列&本人亲测
&&&&&&&&&&&&&&&&&&&&&&&&&&200smart& 感谢 無名①终止^^ 的测试
上述的西门子篇一的通讯配置相对太麻烦,采用西门子官方的库的话,又是DEMO版本,会有广告,而且携带组件过多,最新学习了西门子的TCP/IP报文格式,报文格式在官网没有找到,参考了如下地址的格式:
如果你擅长于网络通信和组件开发,可以通过报文格式开发出自己的西门子通信库,我所做的就是基于报文格式进行了二次封装,隐藏了socket通信的细节,还包含了异常处理,提供了简单方便的API来读写数据。提供了整数数据的读写,字符串读写,来丰富各种需求,从事实上来说,只要可以读写字节,相当于任何数据了。
准备:在西门子PLC上配置好IP地址,就只有一个IP地址就够了,然后打开电脑的cmd指令,只要能ping通西门子PLC即可。
private&SiemensTcpNet siemensTcpNet =&new&SiemensTcpNet(SiemensPLCS.S1200)
&&&&&&&&&&&&PLCIpAddress = System.Net.IPAddress.Parse("192.168.1.195"),
&&&&&&&&&&&&ConnectTimeout = 5000,&
&&&&&&&&};
切换长连接(可以根据自己的需求来确认是否切换),也可以放在窗口的Load方法中,一般建议使用长连接,速度更快,又是线程安全的:
private&void&userButton16_Click(object&sender, EventArgs e)
&&&&siemensTcpNet.ConnectServer();
&一行代码就可以切换到长连接模式,长连接的模式通讯更加稳定(如果网络确实好的话),这行代码可以放到Form的Load事件方法中。
只要放到form窗口下即可,实例化需要指定访问的是1200系列还是300系列,然后指定IP地址,端口号不需要指定,西门子有个默认的端口号102,支持读写操作。
演示一些简单使用的数据读写操作,以下代码没有进行对读写结果严格判断(判断是否读写成功,参照更下面的代码),网络良好的情况下几乎不会失败,但不保证完全没有错误,生产使用时尽可能的完善:
private&void&userButton40_Click(object&sender, EventArgs e)
&&&&bool&M100_7 = siemensTcpNet.ReadBoolFromPLC("M100.7").C&&
&&&&byte&byte_M100 = siemensTcpNet.ReadByteFromPLC("M100").C&
&&&&short&short_M100 = siemensTcpNet.ReadShortFromPLC("M100").C&
&&&&ushort&ushort_M100 = siemensTcpNet.ReadUShortFromPLC("M100").C&
&&&&int&int_M100 = siemensTcpNet.ReadIntFromPLC("M100").C&&&&&&&&&
&&&&uint&uint_M100 = siemensTcpNet.ReadUIntFromPLC("M100").C&&&&&&
&&&&float&float_M100 = siemensTcpNet.ReadFloatFromPLC("M100").C&&&
&&&&long&long_M100 = siemensTcpNet.ReadLongFromPLC("M100").C&&&&&&
&&&&ulong&ulong_M100 = siemensTcpNet.ReadULongFromPLC("M100").C&&&
&&&&double&double_M100 = siemensTcpNet.ReadDoubleFromPLC("M100").C&
&&&&string&str_M100 = siemensTcpNet.ReadStringFromPLC("M100", 10).C
&&&&siemensTcpNet.WriteIntoPLC("M100.7",&true);&&&&&&&&&&&&&&&&
&&&&siemensTcpNet.WriteIntoPLC("M100", (byte)0x33);&&&&&&&&&&&&
&&&&siemensTcpNet.WriteIntoPLC("M100", (short)12345);&&&&&&&&&&
&&&&siemensTcpNet.WriteIntoPLC("M100", (ushort)45678);&&&&&&&&&
&&&&siemensTcpNet.WriteIntoPLC("M100", );&&&&&&&&&&&&&
&&&&siemensTcpNet.WriteIntoPLC("M100", (uint));&&&&&&
&&&&siemensTcpNet.WriteIntoPLC("M100", 123.456f);&&&&&&&&&&&&&&
&&&&siemensTcpNet.WriteIntoPLC("M100", 4545L);&&&&&
&&&&siemensTcpNet.WriteIntoPLC("M100", 343UL);&&&&&
&&&&siemensTcpNet.WriteIntoPLC("M100", 123.456d);&&&&&&&&&&&&&&
&&&&siemensTcpNet.WriteAsciiStringIntoPLC("M100",&"K");
如果上面的指令不能满足你的需求,下面再分别讲解严格的操作,以及批量化的复杂的读写操作,假设你要读取1000个M,循环读取1千次可能要3秒钟,如果用了下面的批量化读取,只需要50ms,但是需要你对字节的原理比较熟悉才能得心应手的处理
批量位数据写入:(如果长度刚好为8的倍数,比如24个,那就刚好写3个字节的数据,如果像下面的代码写10个长度,那么实际上会改变M200-M201共16个开关点,这个一定要注意)
private&void&userButton14_Click(object&sender, EventArgs e)
&&&&&&&&&&&&bool[] data =&new&bool[10]
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&false,&&&&
&&&&&&&&&&&&&&&&false,&&&&
&&&&&&&&&&&&&&&&false,&&&&
&&&&&&&&&&&&&&&&true,&&&&&
&&&&&&&&&&&&&&&&true,&&&&&
&&&&&&&&&&&&&&&&false,&&&&
&&&&&&&&&&&&&&&&true,&&&&&
&&&&&&&&&&&&&&&&false,&&&&
&&&&&&&&&&&&&&&&true,&&&&&
&&&&&&&&&&&&&&&&false&&&&&
&&&&&&&&&&&&};
&&&&&&&&&&&&OperateResult write = siemensTcpNet.WriteIntoPLC("M200", data);
&&&&&&&&&&&&if&(write.IsSuccess)
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&textBox4.Text =&"写入成功";
&&&&&&&&&&&&}
&&&&&&&&&&&&else
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&MessageBox.Show(write.ToMessageShowString());
&&&&&&&&&&&&}
M,I,Q的读写(此处演示批量读取,自己根据需求来解析数据):
这三个数据的读写是一致的,为了区分之前旧的一个通讯类,此处的数据地址格式变更为字符串,
private&void&button3_Click(object&sender, EventArgs e)
&&&&&&&&{&br&&&&&&&&&&&&&
&&&&&&&&&&&&OperateResult&byte[]& read = siemensTcpNet.ReadFromPLC("M100", 2);
&&&&&&&&&&&&if(read.IsSuccess)
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&textBox4.Text =&"M100:"&+ read.Content[0] +&" M101:"&+ read.Content[1];
&&&&&&&&&&&&&&&&bool&M100_0 = (read.Content[0] & 0x01) == 0x01;
&&&&&&&&&&&&&&&&bool&M100_1 = (read.Content[0] & 0x02) == 0x02;
&&&&&&&&&&&&&&&&bool&M100_2 = (read.Content[0] & 0x04) == 0x04;
&&&&&&&&&&&&&&&&bool&M100_3 = (read.Content[0] & 0x08) == 0x08;
&&&&&&&&&&&&&&&&bool&M100_4 = (read.Content[0] & 0x10) == 0x10;
&&&&&&&&&&&&&&&&bool&M100_5 = (read.Content[0] & 0x20) == 0x20;
&&&&&&&&&&&&&&&&bool&M100_6 = (read.Content[0] & 0x40) == 0x40;
&&&&&&&&&&&&&&&&bool&M100_7 = (read.Content[0] & 0x80) == 0x80;
&&&&&&&&&&&&}
&&&&&&&&&&&&else
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&MessageBox.Show(read.ToMessageShowString());
&&&&&&&&&&&&}
&&&&&&&&private&void&button4_Click(object&sender, EventArgs e)
&&&&&&&&&&&&
&&&&&&&&&&&&OperateResult write = siemensTcpNet.WriteIntoPLC("M100",&new&byte[] { 81, 22, 124 });
&&&&&&&&&&&&if&(write.IsSuccess)
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&textBox4.Text =&"写入成功";
&&&&&&&&&&&&}
&&&&&&&&&&&&else
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&MessageBox.Show(write.ToMessageShowString());
&&&&&&&&&&&&}
  如果是输入的话,就是把"M100"换成"I100","Q100",效果是一样的,这样的就可以对数据进行读写了。而DB块的格式有些区别,比如我们要写DB块20的100地址数据,我们需要写成"DB20.100"这样就可以正常读写了。DB块数据并没有进行严格测试,如果你需要使用这个功能,最好需要测试下是否真的能读取数据。
DB块的读写测试感谢我的好朋友:吃饱睡好
整数数据读写(一个数据由2个byte组成)虽然上述实现了M数据的读写,但是只能表示0-255的数据,想要支持更大的数据,需要自己指定规则, 这就需要你对数据和字节原理非常清晰才能实现,为了方便,此处提供了读写双字节数据的功能,先演示读取M100-M106 的数据,对应有三个双字节数据,代码如下:
private&void&button15_Click(object&sender, EventArgs e)
&&&&&&&&OperateResult&byte[]& read = siemensTcpNet.ReadFromPLC("M100", 6);
&&&&&&&&if&(read.IsSuccess)
&&&&&&&&&&&&short[] result = siemensTcpNet.GetArrayFromBytes(read.Content);
&&&&&&&&&&&&textBox4.Text =&"M100:"&+ result[0] +&",M102:"&+ result[1] +&",M104:"&+ result[2];
&&&&&&&&&&&&
&&&&&&&&&&&&
&&&&&&&&&&&&
&&&&&&&&&&&&
&&&&&&&&else
&&&&&&&&&&&&MessageBox.Show(read.ToMessageShowString());
  接下来介绍写入整数数据的操作,例如,我们要使得M100,M101=2456,M102,M103=4567,M104,M105=-124,那么代码如下:
private&void&button16_Click(object&sender, EventArgs e)
&&&&OperateResult write = siemensTcpNet.WriteIntoPLC("M100",&new&short[] { , -124 });
&&&&if&(write.IsSuccess)
&&&&&&&&textBox4.Text =&"写入成功";
&&&&&&&&MessageBox.Show(write.ToMessageShowString());
ASCII字符串数据的读写在实际项目中,有可能会碰到PLC存储了规格数据,或是条码数据,这些数据是以ASCII编码形式存在, 我们需要把数据进行读取出来用于显示,保存等操作。下面演示读取指定长度的条码数据,数据的数据存放在M100-M109中, 长度应该为存储条码的最大长度,也即是占用了10个M,一个M可以存储1个ASCII码字符:
private&void&button12_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = siemensTcpNet.ReadFromPLC("M100", 10);
&&&&if&(read.IsSuccess)
&&&&&&&&textBox4.Text = Encoding.ASCII.GetString(read.Content);
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
下面演示写入条码数据,地址在M100-M109中,所以需要写入10个字符:
private&void&button14_Click(object&sender, EventArgs e)
&&&&OperateResult write = siemensTcpNet.WriteAsciiStringIntoPLC("M100",&"K");
&&&&if&(write.IsSuccess)
&&&&&&&&textBox4.Text =&"写入成功";
&&&&&&&&MessageBox.Show(write.ToMessageShowString());
需要注意的是,如果第一次在M100-M109中写入了"K",第二次写入了"K6666",那么读取M100-M109的条码数据会读取到K,如果要避免这种情况,则需要在写入条码的时候,指定总长度,该长度 可单数可偶数,具体的使用方法如下:
private&void&button14_Click(object&sender, EventArgs e)
&&&&OperateResult write = siemensTcpNet.WriteAsciiStringIntoPLC("M100",&"K6666", 10);
&&&&if&(write.IsSuccess)
&&&&&&&&textBox4.Text =&"写入成功";
&&&&&&&&MessageBox.Show(write.ToMessageShowString());
中文及特殊字符的读写在需要读写复杂的字符数据时,上述的ASCII编码已经不能满足要求,虽然使用读写的基础方法可以实现任意数据的读写, 但是此处为了方便,还是提供了一个方便的方法来读写中文数据,采用Unicode编码的字符, 该编码下的一个字符占用两个M来存储。如下将演示,读写方法,基本用途和上述 ASCII编码的读写一致。
private&void&button11_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = siemensTcpNet.ReadFromPLC("M200", 12);
&&&&if&(read.IsSuccess)
&&&&&&&&textBox4.Text = Encoding.Unicode.GetString(read.Content);
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
在写入的过程中,只演示写入指定长度的(实际中也应该使用这个方法),指定长度的意思为多少个中文。
private&void&button13_Click(object&sender, EventArgs e)
&&&&OperateResult write = siemensTcpNet.WriteUnicodeStringIntoPLC("M200",&"测试de东西", 10);
&&&&if&(write.IsSuccess)
&&&&&&&&textBox4.Text =&"写入成功";
&&&&&&&&MessageBox.Show(write.ToMessageShowString());
一个实际的复杂例子:
实际中可能碰到的情况会很复杂,一台设备中需要上传的数据包含了温度,压力,产量,规格等等信息,在一串数据中 会包含各种各样的不同的数据,所以此处做一个完整示例的演示,假设我们需要读取 M100-M116的数据,假设M100,M101存放了温度数据,55.1℃在M中为551,M102,M103存放了压力数据,1.23MPa在M中存放为123,M104存放了 设备状态,0为停止,1为运行,M105,M106存放了产量,1000就是指1000个,M107-M116存放了规格,以下代码演示如何去解析数据:
private&void&button49_Click(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = siemensTcpNet.ReadFromPLC("M200", 17);
&&&&if&(read.IsSuccess)
&&&&&&&&double&温度 = siemensTcpNet.GetShortFromBytes(read.Content, 0) / 10d;
&&&&&&&&double&压力 = siemensTcpNet.GetShortFromBytes(read.Content, 2) / 100d;
&&&&&&&&bool&IsRun = read.Content[4] == 1;
&&&&&&&&int&产量 = siemensTcpNet.GetShortFromBytes(read.Content, 5);
&&&&&&&&string&规格 = Encoding.ASCII.GetString(read.Content, 7, 10);
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
散乱数据的读取:
实际中我们需要读取PLC中的数据,并不是连续的数据块,最好的方法是将所有需要的数据块挪到一个连续的区块,比如M块,这样可以加速数据的读取,系统的性能也能更加高效,即时需要读取1000个M点,也是毫秒级的事情,如果1000个M点分成1000次来读取,那么使用本组件的效率是非常低下的,循环1000次的siemensTcpNet.ReadFromPLC("M100", 1);非常的耗时甚至可能达到几秒的量级,因为这个方法每次调用都会重新请求网络连接,然后初始化连接,请求数据,断开连接,所以最好的方法就是所有的数据都挪到一个统一的数据区块。
但是如果你确实有需求读取多个地址的数据,比如做成访问PLC的数据是可配置的,在配置文件里追加一个M100,长度4的int型数据,这种情况就不太适合将散乱的数据进行挪到统一的区块,所以本组件提供了一个高性能数组读取(但是仍然比一次读取连续区块慢一点,基本上是同一个量级的),声明如下:
/// &summary&
/// 一次性从PLC获取所有的数据,按照先后顺序返回一个统一的Buffer,需要按照顺序处理,两个数组长度必须一致
/// &/summary&
/// &param name="address"&起始地址数组&/param&
/// &param name="count"&数据长度数组&/param&
/// &returns&&/returns&
public&OperateResultBytes ReadFromPLC(string[] address,&ushort[] count);
address和count数组都不能为空,否则报错,两者的长度必须一致,否则报错
接下来我们举例访问PLC数据,比如我们需要读取M100开始的4个字节(这是一个int数据),M150开始的4个字节(这是一个float数据),M200开始的2个字节(这是一个short数据),I300开始的一个字节(普通的byte数据)。
private&void&button29_Click_1(object&sender, EventArgs e)
&&&&OperateResult&byte[]& read = siemensTcpNet.ReadFromPLC(
&&&&&&&&new&string[] {&"M100",&"M150",&"M200",&"I300"&},
&&&&&&&&new&ushort[] { 4, 4, 2, 1});
&&&&if(read.IsSuccess)
&&&&&&&&int&value1 = siemensTcpNet.GetIntFromBytes(read.Content, 0);&&&&&
&&&&&&&&float&value2 = siemensTcpNet.GetFloatFromBytes(read.Content, 4);&
&&&&&&&&short&value3 = siemensTcpNet.GetShortFromBytes(read.Content, 8);&
&&&&&&&&byte&value4 = read.Content[10];&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&MessageBox.Show(read.ToMessageShowString());
究极数据的读取:
此处提供一个核心的报文读取机制,你可以自己传入自己的报文,然后接收服务器的报文,再自己解析操作,可以根据报文格式实现任意的操作,当然,前提是需要报文支持。假设我要实现写入M100,为0x3B,那么最终的报文为
03 00 00 24 02 F0 80 32 01 00 00 00 01 00 0E 00 05 05 01 12 0A 10 02 00 01 00 00 83 00 03 20 00 04 00 08 3B
private&void&userButton23_Click_1(object&sender, EventArgs e)
&&&&byte[] buffer = HslCommunication.BasicFramework.SoftBasic.HexStringToBytes(
&&&&&&&&"03 00 00 24 02 F0 80 32 01 00 00 00 01 00 0E 00 05 05 01 12 0A 10 02 00 01 00 00 83 00 03 20 00 04 00 08 3B");
&&&&OperateResult&byte[]& operate = siemensTcpNet.ReadFromServerCore(buffer);
&&&&if&(operate.IsSuccess)
&&&&&&&&TextBoxAppendStringLine(HslCommunication.BasicFramework.SoftBasic.ByteToHexString(operate.Content));
&&&&&&&&MessageBox.Show(operate.ToMessageShowString());
阅读(...) 评论() &}

我要回帖

更多关于 块编写选项板里面没有 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信