• 第0章:HslCommunication简介
  • V12版本特别说明
  • Java版本特别说明
  • Python版本特别说明
  • 第1章:学前必备基础
    • 基础数据类型的说明
    • 程序的本质
    • Socket长短连接
    • 控制台,winform,wpf
    • 用户界面和后台线程
    • 从代码到面向对象编程
    • 了解和使用反射和表达式树
    • C#语言的利器《泛型》
  • 第2章:HSL基础功能说明
    • 如何安装HSL
    • 如何激活HSL
    • OperateResult类说明
    • HSL扩展方法汇总
    • 日志记录功能
    • 统计日志功能
    • 数据的加密解密
  • 第3章:连接配置及地址说明
    • Modbus地址及特别说明
    • 三菱PLC的连接配置
    • 西门子PLC的连接配置
    • 欧姆龙PLC的连接配置
    • 倍福PLC的连接配置
    • 罗克韦尔PLC一些说明
  • 第4章:读写PLC进阶
    • 快速了解使用HSL
    • 多种数据类型的读写
    • 字符串及中文的读写
    • 原始字节及高性能读写
    • HSL的长短连接实现
    • 实时读取刷新数据
    • 读取上升沿,下降沿
    • 实时曲线的演示
    • 实时判断PLC是否在线
    • PLC判断上位机是否在线
    • 管道的使用以及通信锁
    • 基于HSL扩展自定义协议
    • 使用MQTT中转时读取PLC
    • 正确使用多线程
    • 正确的使用异步
    • 优化串口通信的性能
    • 当使用Lora访问设备时
    • 从云服务器访问本地PLC
    • 另一种MOXA串口驱动
  • 第5章:Mqtt服务器客户端
    • 连接和安全认证
    • 发布订阅模式
    • 远程RPC接口实现及调用
    • 文件传输实现
    • 通过RPC远程读写PLC实现
  • 第6章:HTTP服务器及Websocket
    • HTTP服务器实现及验证
    • 远程RPC接口实现及调用
    • 使用WebApi访问PLC
    • 使用Https服务
    • Websocket实现web数据更新
  • 第7章:其他特殊协议说明
    • SecsGem协议使用
  • 第8章:高级技巧
    • CS架构客户端自动更新实现
    • 运行在Linux上
    • VC++调用HSL实现通信
    • Python调用C#版本HSL
    • 使用证书来控制API接口权限
    • Demo程序实现远程测试调试
    • 安卓手机APP采集
    • 虚拟PLC服务器的妙用
    • 一些辅助调试的属性
    • 一些疑难杂症的经验总结

从云服务器访问本地PLC

本节内容主要介绍从云服务器上进行读写本地PLC功能,首先我们来想想看为什么会有这个需求?首先一般的场景都是本地有很多的PLC或是仪表设备,然后我们实现的功能是在云服务器上的程序获取到本地PLC的数据, 或是发送命令写入数据到本地的PLC里,在这里我们可以使用的一个方案是,云服务器上创建一个MQTTServer,然后本地写个小软件,小软件从PLC采集数据,发布给MQTTServer,然后订阅一个用于写入这个PLC的主题, 例如 "PLCA/WriteInt16", 参数payload可以定义为 { "address": "D100", "value": 100 },服务器端只要发布该主题,本地的小软件就收到了写入的主题的数据,然后调用PLC上的 plc.Write("D100", (short)100); 就可以写入PLC了。但是本章节介绍另一个路子,使用DTU转发来实现,好处就是可以丛云服务器上体验到本地访问PLC一样的体验。

使用DEMO快速体验

快速体验需要V12.1.0及以上版本的demo程序,请确保程序的版本是匹配的,然后场景是本地有个三菱PLC,我们现在需要从云服务器读写本地的三菱PLC。那么第一步就是在云服务器上线运行Demo程序,打开三菱的界面, 然后选择DTU管道,然后修改DTU参数信息,其中的ID用来标记不同的连接设备的,密码是用来做二次验证不正确的连接的。具体截图如下所示:然后点击连接按钮后等待DTU的连接操作

云服务器侧我们已经弄好了,然后本地的PC上也要运行这个DEMO程序,这时候本地的PC需要同时连接着本地的三菱PLC,然后另一个网卡连接着外网,这时候我们打开V12.1.0及以上版本的demo程序, 打开TCP调式界面,进入如下的输入操作

先确认云服务器的DEMO程序已经点击连接按钮

然后点击打开本地的DTU转发功能。

这时候我们就可以在云服务器上读写PLC数据,就像本地读写一样的体验

我们再来看下本地的调试界面发生了什么事情。

使用第三方时自定义注册包

实际中在设备现场是一个专门的DTU模块,一个带4G联网功能的小盒子,这时候需要配置连接到我们的云服务器的IP地址和端口,然后配置自定义的注册包,这个注册包就是下图的内容

我们看到注册包里 48534C00193132333435363738393031313233343536C0A8000110270100 包含了一些ID信息,密码信息,连接暗号之类的,具体的规则如下:

当然手动拼接这样的数据包我也觉得麻烦,都是一串数字,输入多了很容易看错,所以可以在DEMO 程序里直接计算生成操作。

使用写代码来实现

上面的云服务器的功能是直接使用DEMO测试的,云服务器侧的代码可以从DEMO上简单的看到示例代码,如下所示:

然后把上面的代码复制出来,新建一个控制台项目,后面再加几行代码,如下所示:

    
    class Program
    {
        static void Main( string[] args )
        {
            HslCommunication.Profinet.Melsec.MelsecMcNet plc = new HslCommunication.Profinet.Melsec.MelsecMcNet( );
            plc.NetworkNumber = 0;
            plc.NetworkStationNumber = 0;
            plc.TargetIOStation = 1023;
            plc.EnableWriteBitToWordRegister = false;
            plc.ByteTransform.IsStringReverseByteWord = false;

            NetworkAlienClient dtuServer = new NetworkAlienClient( );
            dtuServer.IsResponseAck = true;
            dtuServer.SetPassword( Encoding.ASCII.GetBytes( "123456" ) );
            dtuServer.OnClientConnected += ( PipeDtuNet session ) =>
            {
                if (session.DTU == "12345678901")
                {
                    OperateResult connect = plc.SetDtuPipe( session );
                    if (connect.IsSuccess)
                    {
                        Console.WriteLine( "connect success" );
                    }
                }
            };
            dtuServer.ServerStart( 10000 );
            plc.SetDtuPipe( new PipeDtuNet( ) );   // 在连接上来之前,先给一个默认的空的DTU会话

            while(true)
            {
                HslHelper.ThreadSleep( 1000 );

                OperateResult<short> read = plc.ReadInt16( "D0" );
                if (read.IsSuccess)
                {
                    Console.WriteLine( DateTime.Now.ToString( ) + " Read Value: " + read.Content );
                }
                else
                {
                    Console.WriteLine( DateTime.Now.ToString( ) + " Read failed: " + read.Message );
                }
            }
        }
    }
    
    

这个就是我们运行在云服务器的代码程序了,然后启动这个程序,本地的也运行TCP转DTU界面,这时候,我们就看到了采集到正确的数据

我们看到上面的运行结果里,一开始运行的时候,DTU还没连接上来,过了几秒钟后,连接上来就自动成功了。如果后面网络异常的话,会继续采集失败,等待DTU连接上来才能成功。

如果有两个设备需要采集

如果我们还有个modbusrtu的设备需要这么采集呢?我们看到有个DTU ID 信息,可以用来标记不同的设备,我们再增加一个ID为 12345678902 的ModbusRtu设备, 服务器端的代码就需要修改下了,如下所示

    
    class Program
    {
        static void Main( string[] args )
        {
            HslCommunication.Profinet.Melsec.MelsecMcNet plc = new HslCommunication.Profinet.Melsec.MelsecMcNet( );
            plc.NetworkNumber = 0;
            plc.NetworkStationNumber = 0;
            plc.TargetIOStation = 1023;
            plc.EnableWriteBitToWordRegister = false;
            plc.ByteTransform.IsStringReverseByteWord = false;

            HslCommunication.ModBus.ModbusRtu modbus = new HslCommunication.ModBus.ModbusRtu( );
            modbus.AddressStartWithZero = true;
            modbus.IsStringReverse = false;
            modbus.DataFormat = HslCommunication.Core.DataFormat.CDAB;
            modbus.Station = 1;
            modbus.Crc16CheckEnable = true;
            modbus.IsClearCacheBeforeRead = false;
            modbus.StationCheckMacth = true;


            NetworkAlienClient dtuServer = new NetworkAlienClient( );
            dtuServer.IsResponseAck = true;
            dtuServer.SetPassword( Encoding.ASCII.GetBytes( "123456" ) );
            dtuServer.OnClientConnected += ( PipeDtuNet session ) =>
            {
                if (session.DTU == "12345678901")
                {
                    OperateResult connect = plc.SetDtuPipe( session );
                    if (connect.IsSuccess)
                    {
                        Console.WriteLine( "Melsec connect success" );
                    }
                }
                else if (session.DTU == "12345678902")  // 这里的ID和上面的
                {
                    OperateResult connect = modbus.SetDtuPipe( session );
                    if (connect.IsSuccess)
                    {
                        Console.WriteLine( "Modbus connect success" );
                    }
                }
            };
            dtuServer.ServerStart( 10000 );
            plc.SetDtuPipe( new PipeDtuNet( ) );   // 在连接上来之前,先给一个默认的空的DTU会话
            modbus.SetDtuPipe( new PipeDtuNet( ) );   // 在连接上来之前,先给一个默认的空的DTU会话

            // 这时候,我们就需要创建两个线程来访问两个PLC了
            Thread thread1 = new Thread( new ParameterizedThreadStart( ThreadReadMelsec ) );
            thread1.IsBackground = true;
            thread1.Start( plc );

            Thread thread2 = new Thread( new ParameterizedThreadStart( ThreadReadModbus ) );
            thread2.IsBackground = true;
            thread2.Start( modbus );

            // Console.ReadLine( ); 在某些情况下这行代码仍然会退出
            while(true)
            {
                HslHelper.ThreadSleep( 1000 );
            }

        }

        private static void ThreadReadMelsec( object obj )
        {
            if (obj is HslCommunication.Profinet.Melsec.MelsecMcNet plc)
            {
                while (true)
                {
                    HslHelper.ThreadSleep( 1000 );

                    OperateResult<short> read = plc.ReadInt16( "D0" );
                    if (read.IsSuccess)
                    {
                        Console.WriteLine( DateTime.Now.ToString( ) + " Melsec Read[D0] Value: " + read.Content );
                    }
                    else
                    {
                        Console.WriteLine( DateTime.Now.ToString( ) + " Modbus Read[D0] failed: " + read.Message );
                    }
                }
            }
        }

        private static void ThreadReadModbus( object obj )
        {
            if (obj is HslCommunication.ModBus.ModbusRtu modbus)
            {
                while (true)
                {
                    HslHelper.ThreadSleep( 1000 );

                    OperateResult<short> read = modbus.ReadInt16( "100" );
                    if (read.IsSuccess)
                    {
                        Console.WriteLine( DateTime.Now.ToString( ) + " Modbus Read[100] Value: " + read.Content );
                    }
                    else
                    {
                        Console.WriteLine( DateTime.Now.ToString( ) + " Modbus Read[100] failed: " + read.Message );
                    }
                }
            }
        }
    }
    
    

我们看到有更多的设备也没有关系的,都是可以直接连接操作的。

本篇文章目录 在线学习人数:0