C# 读写欧姆龙(Omron)PLC ,C#使用Fins-tcp协议读写数据

本文转自dathlin博客园博客,原文链接:https://www.cnblogs.com/dathlin/p/7469679.html,如需转载请自行联系

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

github地址:GitHub - dathlin/HslCommunication: A very popular industrial Internet of Things communication plug-in. Using this dll can be very convenient, stable, and fast to obtain data from PLC equipment of multiple brands, and also supports redis, mqtt, websocket, etc., which can let your data on the network Free transmission, reducing enterprise development costs. 如果喜欢可以star或是fork,还可以打赏支持。

联系作者及加群方式(激活码在群里发放):Cooperation - 胡工科技

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

1

Install-Package HslCommunication

 如果需要教程:Nuget安装教程:VS使用Nuget教程详解 Visual studio 安装第三方的组件库 - dathlin - 博客园

组件的完整信息和其他API介绍参照:HslCommunication组件库使用说明 - dathlin - 博客园   组件的授权协议,更新日志,都在该页面里面。

本文将展示如何配置网络参数及怎样使用代码来访问PLC数据,希望给有需要的人解决一些实际问题。主要对西门子PLC的M,Q,I,DB块的数据读写,亲测有效。

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

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

1

2

using HslCommunication;

using HslCommunication.Profinet.Omron;

随便聊聊


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

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

C# 开发ModBus的服务器程序 实现ModBus数据总站 搭建自定义的Modbus服务器 同时支持tcp和rtu - dathlin - 博客园

关于两种模式


本组件所提供的所有客户端类,包括三菱,西门子,欧姆龙,modbus-tcp,以及SimplifyNet都是继承自双模式基类,双模式包含了短连接和长连接,下面就具体介绍下两个模式的区别

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

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

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

关于日志记录


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

C# 日志记录分级功能使用 按照日期,大小,或是单文件存储 - dathlin - 博客园

举个例子:

1

omronFinsNet.LogNet = new HslCommunication.LogNet.LogNetSingle( "omron.log.txt" );

关于通讯的说明


使用FINS-TCP协议实现数据交互,如果在测试的过程中,发现写入失败的话,有可能是因为PLC侧进行了写保护操作。

访问测试项目


在上述的github源代码里有个测试项目,HslCommunicationDemo,里面包含了各种客户端的Demo项目,不需要编写任何的代码就可以测试数据的访问了。

下载地址为:HslCommunicationDemo.zip

演示项目


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

GitHub - dathlin/RemoteMonitor: 本项目是一个利用HslCommunication组件读取PLC的示例项目,演示了后台从PLC循环读取到前台显示,并推送给在线客户端,客户端同步显示并画实时曲线图。支持web端同步的数据显示,支持web端远程操作PLC,安卓端数据显示,远程操作PLC

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

演示项目使用的西门子访问,可以方便的改成欧姆龙的项目

地址支持


支持地址及示例如下:

  • DM区  D100
  • CIO区 C100
  • WR区 W100
  • HR区 H100
  • AR区 A100

特别感谢


  • 感谢 酒罢舞 对本组件的测试
  • 感谢 一贝水 对本组件的测试

实例化


1

  

1

private OmronFinsNet omronFinsNet = new OmronFinsNet( "192.168.0.100", 6000 );

连接服务器,也可以放在窗口的Load方法中,一般建议使用长连接,速度更快,又是线程安全的(调用下面的方法就是使用了长连接,如果不连接直接读取数据,那就是短连接):


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

try

{

    OperateResult connect = omronFinsNet.ConnectServer( );

    if (connect.IsSuccess)

    {

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

    }

    else

    {

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

    }

}

catch (Exception ex)

{

    MessageBox.Show( ex.Message );

}

断开连接,也就是关闭了长连接,如果再去请求数据,就变成了短连接


1

omronFinsNet.ConnectClose( );

下面就演示一些简单的数据操作,省去了对结果是否成功的验证,所有的读写结果都是OperateResult类型及派生类型,都有一个IsSuccess属性来判断成功与否

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

// 读取操作,这里的D100可以替换成C100,A100,W100,H100效果时一样的

bool D100_7 = omronFinsNet.ReadBool( "D100.7" ).Content;  // 读取D100.7是否通断,注意D100.0等同于D100

short short_D100 = omronFinsNet.ReadInt16( "D100" ).Content; // 读取D100组成的字

ushort ushort_D100 = omronFinsNet.ReadUInt16( "D100" ).Content; // 读取D100组成的无符号的值

int int_D100 = omronFinsNet.ReadInt32( "D100" ).Content;         // 读取D100-D101组成的有符号的数据

uint uint_D100 = omronFinsNet.ReadUInt32( "D100" ).Content;      // 读取D100-D101组成的无符号的值

float float_D100 = omronFinsNet.ReadFloat( "D100" ).Content;   // 读取D100-D101组成的单精度值

long long_D100 = omronFinsNet.ReadInt64( "D100" ).Content;      // 读取D100-D103组成的大数据值

ulong ulong_D100 = omronFinsNet.ReadUInt64( "D100" ).Content;   // 读取D100-D103组成的无符号大数据

double double_D100 = omronFinsNet.ReadDouble( "D100" ).Content; // 读取D100-D103组成的双精度值

string str_D100 = omronFinsNet.ReadString( "D100", 5 ).Content;// 读取D100-D104组成的ASCII字符串数据

// 写入操作,这里的D100可以替换成C100,A100,W100,H100效果时一样的

omronFinsNet.Write( "D100", (byte)0x33 );            // 写单个字节

omronFinsNet.Write( "D100", (short)12345 );          // 写双字节有符号

omronFinsNet.Write( "D100", (ushort)45678 );         // 写双字节无符号

omronFinsNet.Write( "D100", (uint)3456789123 );      // 写双字无符号

omronFinsNet.Write( "D100", 123.456f );              // 写单精度

omronFinsNet.Write( "D100", 1234556434534545L );     // 写大整数有符号

omronFinsNet.Write( "D100", 523434234234343UL );     // 写大整数无符号

omronFinsNet.Write( "D100", 123.456d );              // 写双精度

omronFinsNet.Write( "D100""K123456789" );// 写ASCII字符串

下面说明复杂的数据操作,以及批量化的数据操作,例如读取D100-D105

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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

{

    if (read.IsSuccess)

    {

        // 此处需要根据实际的情况来自定义来处理复杂的数据

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

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

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

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

        short D104 = omronFinsNet.ByteTransform.TransInt16( read.Content, 7 );

    }

    else

    {

        // 发生了异常

    }

}

写入也是一样的,可以反着来操作。

如果想实现自定义的数据类型,需要继承一个接口

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

public class UserType : HslCommunication.IDataTransfer

{

    #region IDataTransfer

    private HslCommunication.Core.IByteTransform ByteTransform = new HslCommunication.Core.ReverseWordTransform( );

    public ushort ReadCount => 20;

    public void ParseSource( byte[] Content )

    {

        int count = ByteTransform.TransInt32( Content, 0 );

        float temp = ByteTransform.TransSingle( Content, 4 );

        short name1 = ByteTransform.TransInt16( Content, 8 );

        string barcode = Encoding.ASCII.GetString( Content, 10, 10 );

    }

    public byte[] ToSource( )

    {

        byte[] buffer = new byte[20];

        ByteTransform.TransByte( count ).CopyTo( buffer, 0 );

        ByteTransform.TransByte( temp ).CopyTo( buffer, 4 );

        ByteTransform.TransByte( name1 ).CopyTo( buffer, 8 );

        Encoding.ASCII.GetBytes( barcode ).CopyTo( buffer, 10 );

        return buffer;

    }

    #endregion

    #region Public Data

    public int count { getset; }

    public float temp { getset; }

    public short name1 { getset; }

    public string barcode { getset; }

    #endregion

}

  这样我们就是可以实现特殊数据的读写了

1

2

3

4

5

6

7

OperateResult<UserType> read = omronFinsNet.ReadCustomer<UserType>( "M100" );

if (read.IsSuccess)

{

    UserType value = read.Content;

}

// write value

omronFinsNet.WriteCustomer( "M100"new UserType( ) );

究极数据的读取:

此处提供一个核心的报文读取机制,你可以自己传入自己的报文,然后接收服务器的报文,再自己解析操作,可以根据报文格式实现任意的操作,当然,前提是需要报文支持。假设我要实现读取D0,D1,那么最终的报文为

46494E530000001A000000020000000080000200210000C000000101820000000002

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

private void userButton23_Click_1(object sender, EventArgs e)

{

    byte[] buffer = HslCommunication.BasicFramework.SoftBasic.HexStringToBytes(

        "46494E530000001A000000020000000080000200210000C000000101820000000002");

    OperateResult<byte[]> operate = omronFinsNet.ReadFromServerCore(buffer);

    if (operate.IsSuccess)

    {

        // 显示服务器返回的报文

        TextBoxAppendStringLine(HslCommunication.BasicFramework.SoftBasic.ByteToHexString(operate.Content));

    }

    else

    {

        // 显示网络错误

        MessageBox.Show(operate.ToMessageShowString());

    }

}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值