C#读写三菱PLC数据 使用TCP/IP 协议

本文将使用一个Github开源的组件库技术来读写三菱PLC和西门子plc数据,使用的是基于以太网的TCP/IP实现,不需要额外的组件,读取操作只要放到后台线程就不会卡死线程,本组件支持超级方便的高性能读写操作

github地址:https://github.com/dathlin/HslCommunication 如果喜欢可以star或是fork,还可以打赏支持,打赏请认准源代码项目。

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


1

Install-Package HslCommunication

如果需要教程:Nuget安装教程:http://www.cnblogs.com/dathlin/p/7705014.html

联系作者及加群方式(激活码在群里发放):http://www.hslcommunication.cn/Cooperation

组件的完整信息和API介绍参照:http://www.cnblogs.com/dathlin/p/7703805.html   组件的使用限制,更新日志,都在该页面里面。

如果你需要在读取PLC数据之后,还要群发客户端来实现远程办公室同步监视,可以参考如下的项目(基于该组件扩展起来的,带有账户验证,版本控制,数据群发,公告管理等等功能)

https://github.com/dathlin/ClientServerProject

本文将展示如何配置网络参数及怎样使用代码来访问PLC数据,希望给有需要的人解决一些实际问题。主要对三菱Q系列PLC的X,Y,M,L,B,V,F,S,D,W,R区域的数据读写,对西门子PLC的M,Q,I,DB块的数据读写,亲测有效。

此处使用了网线直接的方式,如果PLC接进了局域网,就可以进行远程读写了^_^

此处使用到了2个命名空间:


1

2

using HslCommunication;

using HslCommunication.Profinet;

随便聊聊



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

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

http://www.cnblogs.com/dathlin/p/7782315.html

关于两种模式



在PLC端,包括三菱,西门子,欧姆龙以及Modbus Tcp客户端的访问器上,都支持两种模式,短连接模式和长连接模式,现在就来解释下什么原理。

短连接:每次读写都是一个单独的请求,请求完毕也就关闭了,如果服务器的端口仅仅支持单连接,那么关闭后这个端口可以被其他连接复用,但是在频繁的网络请求下,容易发生异常,会有其他的请求不成功,尤其是多线程的情况下。

长连接:创建一个公用的连接通道,所有的读写请求都利用这个通道来完成,这样的话,读写性能更快速,即时多线程调用也不会影响,内部有同步机制。如果服务器的端口仅仅支持单连接,那么这个端口就被占用了,比如三菱的端口机制,西门子的Modbus tcp端口机制也是这样的。以下代码默认使用长连接,性能更高,还支持多线程同步。

在短连接的模式下,每次请求都是单独的访问,所以没有重连的困扰,在长连接的模式下,如果本次请求失败了,在下次请求的时候,会自动重新连接服务器,直到请求成功为止。另外,尽量所有的读写都对结果的成功进行判断。

关于日志记录



不管是三菱的数据访问类,还是西门子的,还是Modbus tcp访问类,都有一个LogNet属性用来记录日志,该属性是一个接口类,ILogNet,凡事继承该接口的都可以用来记录日志,该日志会在访问失败时,尤其是因为网络的原因导致访问失败时会进行日志记录(如果你为这个 LogNet 属性配置了真实的日志记录器的话):如果你想使用该记录日志的功能,请参照如下的博客进行实例化:

http://www.cnblogs.com/dathlin/p/7691693.html

访问测试项目



下面的一个项目是这个组件的访问测试项目,您可以进行初步的访问的测试,免去了您写测试程序的麻烦,三菱的界面和西门子的界面几乎是一致的。可以同时参考。该项目位于本篇文章开始处的Gitbub源代码里面的

下载地址为:HslCommunicationDemo.zip

演示项目



下面的三篇演示了具体如何去访问PLC的数据,我们在访问完成后,通常需要进行处理,以下的示例项目就演示了后台从PLC读取数据后,前台显示并推送给所有在线客户端的功能,客户端并进行图形化显示,具有一定的参考意义,并且推送给网页前端,项目地址为:

https://github.com/dathlin/RemoteMonitor

下面的图片示例中的左边程序就是服务器程序,它应该和PLC直接连接并接入局域网,然后把数据推送给客户端显示。注意:一个复杂高级的程序就应该把处理逻辑程序和界面程序分开,比如这里的服务器程序实现数据采集,推送,存储。让客户端程序去实现数据的整理,分析,显示,这样即使客户端程序因为BUG奔溃,服务器端仍然可以正常的工作。

三菱PLC篇(下面列举了三种配置方法,本组件支持二进制和ASCII通讯,支持1E帧兼容协议访问)



Q06UDV Plc的访问测试感谢:hwdq0012

fx5u plc的访问测试感谢:山楂

Q02CPU, L02CPU-CM : 本人测试

感谢:小懒猪雨中人  的测试,VB程序也可以调用本通讯库

环境1:此处以GX Works3为示例,fx5u的配置如下:(感谢 山楂 提供的图片)

环境2:此处以GX Works2为示例,测试PLC为L02CPU,内置了以太网协议

环境3:此处以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对象

注意:如果你想采用ASCII来读写数据,请使用MelsecMcAsciiNet类,如果想采用1E帧协议,使用MelsecA1ENet类,除了实例化,其他的数据交互都是一样的。
如果想使用本组件的数据读取功能,必须先初始化数据访问对象,根据实际情况进行数据的填入。 下面仅仅是测试中的数据:


1

private MelsecMcNet melsec_net = new MelsecMcNet( "192.168.0.1", 6000 );

如上图所示,只要指定了IP地址和端口号就完成了初始化的搭建了,当然还支持一些额外的信息配置


1

2

3

melsec_net.ConnectTimeOut = 2000; // 网络连接的超时时间

melsec_net.NetworkNumber = 0x00;  // 网络号

melsec_net.NetworkStationNumber = 0x00; // 网络站号

打开连接,并可以判断是否连接上


1

melsec_net.ConnectClose( );

如果需要判断,那么按照如下的操作


1

2

3

4

5

6

7

8

9

OperateResult connect = melsec_net.ConnectServer( );

if (connect.IsSuccess)

{

    MessageBox.Show( "连接成功!" );

}

else

{

    MessageBox.Show( "连接失败!" );

}

说明:对象应该放在窗体类下面,此处仅仅针对读取一台设备的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"            // 字符串为十进制

展示一些简单实用基础数据读写,这些数据的读写没有进行严格的是否成功判断(判断方法参照后面的代码),一般网络良好的情况下都会成功,但不排除失败,以下代码仅作测试,所有没有严格判断是否成功:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

bool[] M100 = melsec_net.ReadBool("M100",1).Content;            // 读取M100是否通,十进制地址

bool[] X1A0 = melsec_net.ReadBool("X1A0",1).Content;            // 读取X1A0是否通,十六进制地址

bool[] Y1A0 = melsec_net.ReadBool("Y1A0",1).Content;            // 读取Y1A0是否通,十六进制地址

bool[] B1A0 = melsec_net.ReadBool("B1A0",1).Content;            // 读取B1A0是否通,十六进制地址

short short_D1000 = melsec_net.ReadInt16("D1000").Content;   // 读取D1000的short值  ,W3C0,R3C0 效果是一样的

ushort ushort_D1000 = melsec_net.ReadUInt16("D1000").Content; // 读取D1000的ushort值

int int_D1000 = melsec_net.ReadInt32("D1000").Content;          // 读取D1000-D1001组成的int数据

uint uint_D1000 = melsec_net.ReadUInt32("D1000").Content;       // 读取D1000-D1001组成的uint数据

float float_D1000 = melsec_net.ReadFloat("D1000").Content;    // 读取D1000-D1001组成的float数据

long long_D1000 = melsec_net.ReadInt64("D1000").Content;       // 读取D1000-D1003组成的long数据

ulong ulong_D1000 = melsec_net.ReadUInt64( "D1000" ).Content;       // 读取D1000-D1003组成的long数据

double double_D1000 = melsec_net.ReadDouble("D1000").Content; // 读取D1000-D1003组成的double数据

string str_D1000 = melsec_net.ReadString("D1000", 10).Content; // 读取D1000-D1009组成的条码数据

melsec_net.Write("M100"new bool[] { true} );                        // 写入M100为通

melsec_net.Write( "Y1A0"new bool[] { true } );                        // 写入Y1A0为通

melsec_net.Write( "X1A0"new bool[] { true } );                        // 写入X1A0为通

melsec_net.Write( "B1A0"new bool[] { true } );                        // 写入B1A0为通

melsec_net.Write( "D1000", (short)1234);                // 写入D1000  short值  ,W3C0,R3C0 效果是一样的

melsec_net.Write( "D1000", (ushort)45678);              // 写入D1000  ushort值

melsec_net.Write( "D1000", 1234566);                    // 写入D1000  int值

melsec_net.Write( "D1000", (uint)1234566);               // 写入D1000  uint值

melsec_net.Write( "D1000", 123.456f);                    // 写入D1000  float值

melsec_net.Write( "D1000", 123.456d);                    // 写入D1000  double值

melsec_net.Write( "D1000", 123456661235123534L);          // 写入D1000  long值

melsec_net.Write( "D1000""K123456789");                // 写入D1000  string值

下面再分别讲解严格的操作,以及批量化的复杂的读写操作,假设你要读取1000个M,循环读取1千次可能要3秒钟,如果用了下面的批量化读取,只需要50ms,但是需要你对字节的原理比较熟悉才能得心应手的处理

X,Y,M,L,F,V,B,S位数据的读写说明

  • X 输入继电器
  • Y 输出继电器
  • M 内部继电器
  • L 锁存继电器
  • F 报警器
  • V 边沿继电器
  • B 链接继电器
  • S 步进继电器

本小节将展示八种位数据的读取,虽然更多的时候只是读取D数据即可,或者是将位数据批量挪到D数据中, 但是在此处仍然进行介绍单独的读取X,Y,M,L,F,V,B,S,由于这八种读取手法一致,故针对M数据进行介绍,其他的您可以自己测试。

如下方法演示读取了M200-M209这10个M的值,注意:读取长度必须为偶数,即时写了奇数,也会补齐至偶数,读取和写入的最大长度为7168,否则报错。如需实际需求确实大于7168的,请分批次读取。
返回值解析:如果读取正常则共返回10个字节的数据,以下示例数据进行批量化的读取


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

private void userButton20_Click(object sender, EventArgs e)

{

    // M200-M209读取显示

    OperateResult<bool[]> read = melsec_net.ReadBool("M200", 10);

    if (read.IsSuccess)

    {

        // 成功读取,True代表通,False代表不通

        bool M200 = read.Content[0];

        bool M201 = read.Content[1];

        bool M202 = read.Content[2];

        bool M203 = read.Content[3];

        bool M204 = read.Content[4];

        bool M205 = read.Content[5];

        bool M206 = read.Content[6];

        bool M207 = read.Content[7];

        bool M208 = read.Content[8];

        bool M209 = read.Content[9];

        // 显示

    }

    else

    {

        //失败读取,显示失败信息

        MessageBox.Show(read.ToMessageShowString());

    }

}

private void userButton21_Click(object sender, EventArgs e)

{

    // X100-X10F读取显示

    OperateResult<bool[]> read = melsec_net.ReadBool("X200", 16);

    if (read.IsSuccess)

    {

        // 成功读取,True代表通,False代表不通

        bool X200 = read.Content[0];

        bool X201 = read.Content[1];

        bool X202 = read.Content[2];

        bool X203 = read.Content[3];

        bool X204 = read.Content[4];

        bool X205 = read.Content[5];

        bool X206 = read.Content[6];

        bool X207 = read.Content[7];

        bool X208 = read.Content[8];

        bool X209 = read.Content[9];

        bool X20A = read.Content[10];

        bool X20B = read.Content[11];

        bool X20C = read.Content[12];

        bool X20D = read.Content[13];

        bool X20E = read.Content[14];

        bool X20F = read.Content[15];

        // 显示

    }

    else

    {

        //失败读取,显示失败信息

        MessageBox.Show(read.ToMessageShowString());

    }

}

private void userButton3_Click(object sender, EventArgs e)

{

    // M100-M104 写入测试 此处写入后M100:通 M101:断 M102:断 M103:通 M104:通

    bool[] values = new bool[] { truefalsefalsetruetrue };// 等同于 byte[] values = new byte[]{0x01,0x00,0x00,0x01,0x01}

    OperateResult write = melsec_net.Write("M100", values);

    if (write.IsSuccess)

    {

        TextBoxAppendStringLine("写入成功");

    }

    else

    {

        MessageBox.Show(write.ToMessageShowString());

    }

}

错误说明:有可能因为站号网络号没有配置正确返回有错误代号没有错误信息, 也有可能因为网络问题导致没有连接上,此时会有连接不上的错误信息。

下面展示的是后台线程循环读取的情况,事实上在实际的使用过程中经常会碰见的情况。下面的方法需要 放到单独的线程中,同理,访问D数据时也是按照下面循环就行,此处不再赘述。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

//后台循环读取PLC数据 M200开始10个字 也即是M200-M209

while (true)

{

    OperateResult<bool[]> read = melsec_net.ReadFromPLC("M200", 10);

    if (read.IsSuccess)

    {

        //成功读取,委托显示

        textBox2.BeginInvoke(new Action(delegate

        {

            textBox2.Text = "M201:" + (read.Content[1] ? "通" "断");

        }));

    }

    else

    {

        //失败读取,应该对失败信息进行日志记录,不应该显示,测试访问时才适合显示错误信息

        LogHelper.save(read.ToMessageShowString());

    }

    System.Threading.Thread.Sleep(1000);//决定了访问的频率

}

  

D,W,R字数据的读写操作

此处读取针对中间存在整数数据的情况,因为两者读取方式相同,故而只演示一种数据读取, 使用该组件读取数据,一次最多读取或写入960个字,超出则失败。 如果读取的长度确实超过限制,请考虑分批读取。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

private void userButton2_Click(object sender, EventArgs e)

{

    // D100-D104读取

    OperateResult<byte[]> read = melsec_net.Read("D100", 5);

    if (read.IsSuccess)

    {

        // 成功读取,提取各自的值,此处的值有个前提假设,假设PLC上的数据是有符号的数据,表示-32768-32767

        short D100 = melsec_net.ByteTransform.TransInt16(read.Content, 0);

        short D101 = melsec_net.ByteTransform.TransInt16( read.Content, 2);

        short D102 = melsec_net.ByteTransform.TransInt16( read.Content, 4);

        short D103 = melsec_net.ByteTransform.TransInt16( read.Content, 6);

        short D104 = melsec_net.ByteTransform.TransInt16( read.Content, 8);

        TextBoxAppendStringLine("D100:" + D100);

        TextBoxAppendStringLine("D101:" + D101);

        TextBoxAppendStringLine("D102:" + D102);

        TextBoxAppendStringLine("D103:" + D103);

        TextBoxAppendStringLine("D104:" + D104);

    }

    else

    {

        //失败读取

        MessageBox.Show(read.ToMessageShowString());

    }

}

private void userButton4_Click( object sender, EventArgs e )

{

    short[] values = new short[5] { 1335, 8765, 1234, 4567, -2563 };

    // D100为1234,D101为8765,D102为1234,D103为4567,D104为-2563

    OperateResult write = melsec_net.Write( "D6000", values );

    if (write.IsSuccess)

    {

        //成功写入

        TextBoxAppendStringLine( "写入成功" );

    }

    else

    {

        MessageBox.Show( write.ToMessageShowString( ) );

    }

}

  

ASCII字符串数据的读写

在实际项目中,有可能会碰到PLC存储了规格数据,或是条码数据,这些数据是以ASCII编码形式存在, 我们需要把数据进行读取出来用于显示,保存等操作。下面演示读取指定长度的条码数据,数据的数据存放在D2000-D2004中, 长度应该为存储条码的最大长度,也即是占用了5个D,一个D可以存储2个ASCII码字符:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

private void button7_Click(object sender, EventArgs e)

{

    //读取字符串数据,共计10个字节长度

    OperateResult<byte[]> read = melsec_net.Read("D2000", 5);

    if (read.IsSuccess)

    {

        //成功读取

        textBox2.Text = Encoding.ASCII.GetString(read.Content);

    }

    else

    {

        //失败读取

        MessageBox.Show(read.ToMessageShowString());

    }

}

private void button8_Click(object sender, EventArgs e)

{

    //写字符串,如果写入K12345678这9个字符,读取出来时末尾会补0

    OperateResult write = melsec_net.WriteAsciiString("D2000""K123456789");

    if (write.IsSuccess)

    {

        textBox2.Text = "写入成功";

    }

    else

    {

        MessageBox.Show(write.ToMessageShowString());

    }

}

  

需要注意的是,如果第一次在D2000-D2004中写入了"K123456789",第二次写入了"K6666",那么读取D2000-D2004的条码数据会读取到 K666656789,如果要避免这种情况,则需要在写入条码的时候,指定总长度,该长度必须为偶数, 不然也会自动补0,小于该长度时,自动补零,大于该长度时,自动截断数据,具体的使用方法如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

private void button8_Click(object sender, EventArgs e)

{

    //写字符串,本次写入指定了10个长度的字符,其余的D的数据将被清空,是一种安全的写入方式

    OperateResult write = melsec_net.WriteAsciiString("D2000""K6666", 10);

    if (write.IsSuccess)

    {

        textBox2.Text = "写入成功";

    }

    else

    {

        MessageBox.Show(write.ToMessageShowString());

    }

}

中文及特殊字符的读写

在需要读写复杂的字符数据时,上述的ASCII编码已经不能满足要求,虽然使用读写的基础方法可以实现任意数据的读写, 但是此处为了方便,还是提供了一个方便的方法来读写中文数据,采用Unicode编码的字符, 该编码下的一个字符占用一个D或W来存储。如下将演示,读写方法,基本用途和上述 ASCII编码的读写一致。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

private void button9_Click(object sender, EventArgs e)

{

    //读中文,存储在D3000-D3009

    OperateResult<byte[]> read = melsec_net.Read("D3000", 10);

    if (read.IsSuccess)

    {

        //解析数据

        textBox2.Text = Encoding.Unicode.GetString(read.Content);

    }

    else

    {

        MessageBox.Show(read.ToMessageShowString());

    }

}

private void button10_Click(object sender, EventArgs e)

{

    //写中文 D3000-D3009,该10含义为中文字符数

    OperateResult write = melsec_net.WriteUnicodeString("D3000""测试数据test", 10);

    if (write.IsSuccess)

    {

        textBox2.Text = "写入成功";

    }

    else

    {

        MessageBox.Show(write.ToMessageShowString());

    }

}

 

一个实际中复杂的例子演示

实际中可能碰到的情况会很复杂,一台设备中需要上传的数据包含了温度,压力,产量,规格等等信息,在一串数据中 会包含各种各样的不同的数据,上述的读取D,读取M,读取条码的方式不太好用,所以此处做一个完整示例的演示,假设我们需要读取 D4000-D4009的数据,假设D4000存放了温度数据,55.1℃在D中为551,D4001存放了压力数据,1.23MPa在D中存放为123,D4002存放了 设备状态,0为停止,1为运行,D4003存放了产量,1000就是指1000个,D4004备用,D4005-D4009存放了规格,以下代码演示如何去解析数据:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

private void button29_Click(object sender, EventArgs e)

{

    //解析复杂数据

    OperateResult<byte[]> read = melsec_net.Read("D4000", 10);

    if (read.IsSuccess)

    {

        double 温度 = melsec_net.ByteTransform.TransInt16(read.Content, 0) / 10d;//索引很重要

        double 压力 = melsec_net.ByteTransform.TransInt16(read.Content, 2) / 100d;

        bool IsRun = melsec_net.ByteTransform.TransInt16(read.Content, 4) == 1;

        int 产量 = BitConverter.ToInt16(read.Content, 6);

        string 规格 = Encoding.ASCII.GetString(read.Content, 10, 10);

    }

    else

    {

        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


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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.ReadFromCoreServer(buffer);

    if(operate.IsSuccess)

    {

        // 返回PLC的报文反馈,需要自己对报文进行结果分析

        MessageBox.Show(HslCommunication.BasicFramework.SoftBasic.ByteToHexString(operate.Content));

    }

    else

    {

        // 网络原因导致的失败

        MessageBox.Show(operate.ToMessageShowString());

    }

}

更详细的信息,可以参照源代码里面的测试项目。

创作不易,感谢打赏

微信:        支付宝:

原文地址:https://www.cnblogs.com/Gxiaopan/p/11479115.html

时间: 2024-10-01 07:28:47

C#读写三菱PLC数据 使用TCP/IP 协议的相关文章

python 读写三菱PLC数据,使用以太网读写Q系列,L系列,Fx系列的PLC数据

本文将使用一个gitHub开源的组件技术来读写三菱的plc数据,使用的是基于以太网的TCP/IP实现,不需要额外的组件,读取操作只要放到后台线程就不会卡死线程,本组件支持超级方便的高性能读写操作 github地址:https://github.com/dathlin/HslCommunication                             如果喜欢可以star或是fork,还可以打赏支持. 官网地址:http://www.hslcommunication.cn/         

java android 读写西门子PLC数据,包含S7协议和Fetch/Write协议,s7支持200smart,300PLC,1200PLC,1500PLC

本文将使用一个gitHub开源的组件技术来读写西门子plc数据,使用的是基于以太网的TCP/IP实现,不需要额外的组件,读取操作只要放到后台线程就不会卡死线程,本组件支持超级方便的高性能读写操作 github地址:https://github.com/dathlin/HslCommunication 如果喜欢可以star或是fork,还可以打赏支持,打赏请认准源代码项目. nuget地址:https://www.nuget.org/packages/HslCommunication/      

C#读写西门子PLC数据

C#读写西门子PLC数据,包含S7协议和Fetch/Write协议,s7支持200smart,300PLC,1200PLC,1500PLC 本文将使用一个gitHub开源的组件技术来读写西门子plc数据,使用的是基于以太网的TCP/IP实现,不需要额外的组件,读取操作只要放到后台线程就不会卡死线程,本组件支持超级方便的高性能读写操作 官方地址:http://www.hslcommunication.cn/ 打赏请认准官网. nuget地址:https://www.nuget.org/packag

KepServerEX读写三菱PLC,车间现场测试记录,带你了解【数据采集的困境】的前世与今生

1.不了解KepServerEX 的鞋童,可以先了解一下OPC UA,OPC UA服务端.我们当前项目读写三菱PLC是自己写的类库,但我感觉调用不够方便灵活,工作之余用OPC UA方式尝试一下 2.数据采集的困境:理想很丰满,现实很骨感. 3.存在即是合理的,OPC UA的方式的本质是想以一种统一的方式,以结束现在这种,五门八门的协议采集,以降低采集成本,降低门槛,加快工业互联的脚步. 5.现在很多著名的设备大厂商纷纷也加入了OPC UA的队伍,让自家的新设备支持OPC UA的方式进行通信.但现

TCP/IP协议数据包文件PCAP分析器

一.设计原理 1.PCAP文件构成 参考http://blog.csdn.net/gulu_gulu_jp/article/details/50494909 PCAP文件由一个PCAP文件头和多个PCAP数据包组成,PCAP数据又由数据包头和数据包内容组成.数据包内容才是我们将要进行分析还原的TCP/IP协议数据.PCAP总体结构如图 11所示: 图 11 PCAP文件总体结构 以TCP协议为例,详细解析PCAP文件格式.PCAP前24个字节为文件头,包含了文件信息,其结构如图 11所示.接着1

深入浅出--iOS的TCP/IP协议族剖析&amp;&amp;Socket

深入浅出--iOS的TCP/IP协议族剖析&&Socket 简介 该篇文章主要回顾--TCP/IP协议族中的TCP/UDP.HTTP:还有Socket.(--该文很干,酝酿了许久!你能耐心看完吗?) 我在这个文章中,列举了常见的TCP/IP族中的协议,今天主角是--传输层协议. 传输层(Transport Layer)是OSI(七层模型)中最重要.最关键的一层,它负责总体的数据传输和数据控制的一层,传输层提供端到端(应用会在网卡注册一个端口号)的交换数据的机制,检查分组编号与次序.传输层对

初探-TCP/IP 协议

一.TCP/IP协议族 TCP/IP协议族体系结构及主要协议.png 1.数据链路层 ARP协议和RARP协议,它们实现了IP地址和机器物理地址之间的相互转化 ARP协议(Address Resolve Protocol,地址解析协议) RARP协议(Reverse Address Resolve Protocol,逆地址解析协议) 作用:网络层是用IP地址寻址一台机器,而数据链路层是用物理地址寻址一台机器,因此网络层必须先将目标机器的IP地址转化成其物理地址,这就是ARP协议的用途.RARP协

TCP/IP 协议详解和运作过程

一.TCP/IP协议族 TCP/IP协议族体系结构及主要协议.png 1.数据链路层 ARP协议和RARP协议,它们实现了IP地址和机器物理地址之间的相互转化 ARP协议(Address Resolve Protocol,地址解析协议) RARP协议(Reverse Address Resolve Protocol,逆地址解析协议) 作用:网络层是用IP地址寻址一台机器,而数据链路层是用物理地址寻址一台机器,因此网络层必须先将目标机器的IP地址转化成其物理地址,这就是ARP协议的用途.RARP协

iOS的TCP/IP协议族剖析&amp;&amp;Socket

原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0 简介 该篇文章主要回顾--TCP/IP协议族中的TCP/UDP.HTTP:还有Socket.(--该文很干,酝酿了许久!你能耐心看完吗?O_o) 我在这个文章中,列举了常见的TCP/IP族中的协议,今天主角是--传输层协议. 传输层(Transport Layer)是OSI(七层模型)中最重要.最关键的一层,它负责总体的数据传输和数据控制的一层,传输层提供端到端(应用会在网卡注