最爱巴菲特 發表於 2020-12-21 23:30:00

基于HslCommunication实现 基于WebApi, javascript来读写PLC数据,包括三菱,西门子,欧姆龙,modbus,ab plc,台达,横河,信捷,松下,汇川,基恩士,富士,LS等等PLC

<p>本文介绍在一些特殊的场景和需求下,使用HslCommunication的可以实现一些比较有意思的功能。例行介绍HSL的安装</p>
<p>github地址:https://github.com/dathlin/HslCommunication&nbsp;如果喜欢可以star或是fork,还可以打赏支持,打赏请认准源代码项目。</p>
<p>联系作者及加群方式:http://www.hslcommunication.cn/</p>
<p>&nbsp;</p>
<p>在Visual Studio 中的NuGet管理器中可以下载安装,也可以直接在NuGet控制台输入下面的指令安装:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">Install-Package HslCommunication
</pre>
</div>
<p>  </p>
<p>如果需要教程:Nuget安装教程:http://www.cnblogs.com/dathlin/p/7705014.html</p>
<p>组件的完整信息和API介绍参照:&nbsp;http://api.hslcommunication.cn&nbsp; 组件的使用限制,更新日志,都在该页面里面。</p>
<p>&nbsp;</p>
<p>我们来看看这种系统的架构设计模式。</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201206191129227-437684441.png"></p>
<p>&nbsp;</p>
<p>我们有一个主的后台服务器来连接现场的各种PLC设备,然后进行数据交互。这个没有问题,很好实现,但是现在有需求,我们在远程客户端界面,或是手机端界面,浏览器界面,等等,需要对远程的PLC进行读写一些数据操作,这时候怎么实现是最简单方便的?</p>
<p>&nbsp;</p>
<p>在HSL里,提供了两种简单的可能性。可以辅助你快速实现需要的操作信息。</p>
<p>1. 基于MRPC实现,详细见文章:https://www.cnblogs.com/dathlin/p/14094128.html</p>
<p>  优点:对客户端的控制更强,权限控制也更加强,可以细化到每个API的权限控制,和MQTT深度结合,支持访问TOPIC信息,传输数据有进度报告。</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp;缺点:MRPC为定制协议,需要定制开发,目前实现了C#,java,python版本,其他平台或是语言都需要定制实现。</p>
<p>2.基于WebApi实现,下文详细说明。</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp;优点:客户端标准化,使用postman即可测试,绝大多数语言和平台都支持webapi接口,已经有相关的轮子,可以直接调用。</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp;缺点:MRPC的优点就是缺点。</p>
<p>&nbsp;</p>
<p>我们开始写代码,我们先假设有1个PLC需要不停的采集,分析数据,做处理。我们新建一个控制台程序,安装HslCommunication组件。为了方便起见,我们假设这个PLC是西门子PLC,实际上无论什么牌子的都是一样的。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">namespace ConsoleServer
{
        class Program
        {
                static void Main( string[] args )
                {
                        SiemensS7Net plc = new SiemensS7Net( SiemensPLCS.S1200, "127.0.0.1" ); // 此处拿了本地虚拟的PLC测试
                        plc.SetPersistentConnection( ); // 设置了长连接

                        while (true)
                        {
                                Thread.Sleep( 1000 ); // 每秒读取一次
                                OperateResult&lt;short&gt; read = plc.ReadInt16( "M100" );
                                if (read.IsSuccess)
                                {
                                        // 读取成功后,进行业务处理,存入数据库,或是其他的分析
                                        Console.WriteLine( "读取成功,M100:" + read.Content );
                                }
                                else
                                {
                                        // 读取失败之后,显示下状态
                                        Console.WriteLine( "读取PLC失败,原因:" + read.Message );
                                }

                        }
                }
        }
}
</pre>
</div>
<p>  </p>
<p>此处就简化了一些操作信息,反正是要执行一些业务操作的。现在我们需要在客户端的程序里,增加一个按钮,比如功能是锁定机台。这个功能的实现是写入M300.1为True。如果解锁,就是写false</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221215451695-959494886.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>因为我们的客户端是部署在其他电脑的,当然是需要新建一个winform的项目了,如果是WPF也是一样的。也是弄两个按钮出来,然后写事件。</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201206195913177-597021958.png"></p>
<p>&nbsp;</p>
<p>&nbsp;有了事件之后,我们怎么来写代码呢?</p>
<p>如果想实现基于webapi来读写,代码非常精简。我们改造下服务器端的实现:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">namespace ConsoleServer
{
        class Program
        {
                static void Main( string[] args )
                {
                        SiemensS7Net plc = new SiemensS7Net( SiemensPLCS.S1200, "127.0.0.1" ); // 此处拿了本地虚拟的PLC测试
                        plc.SetPersistentConnection( ); // 设置了长连接

                        HttpServer httpServer = new HttpServer( );
                        httpServer.RegisterHttpRpcApi( "MainPlc", plc );
                        httpServer.Start( 8000 );    // 需要管理员启动

                        while (true)
                        {
                                Thread.Sleep( 1000 ); // 每秒读取一次
                                OperateResult&lt;short&gt; read = plc.ReadInt16( "M100" );
                                if (read.IsSuccess)
                                {
                                        // 读取成功后,进行业务处理,存入数据库,或是其他的分析
                                        Console.WriteLine( "读取成功,M100:" + read.Content );
                                }
                                else
                                {
                                        // 读取失败之后,显示下状态
                                        Console.WriteLine( "读取PLC失败,原因:" + read.Message );
                                }

                        }
                }
        }
}
</pre>
</div>
<p>  </p>
<p>增加了三段话,实例化服务,注册PLC,启动。</p>
<p>当前的服务器提供了什么RPC的接口呢?,我们运行起来。然后用HslCommunication的DEMO程序打开瞧瞧就可以了。<br><br></p>
<p>服务器端启动之后,就是一直在打印读取成功了。没有其他的内容</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221220734717-35843840.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>好了,到这里我们的程序写完了,我们的PLC已经提供了远程读写的WebApi的接口了,可能我们会问,在哪里呢?我们打开demo看看。</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221221121646-1881433920.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>上面的操作之后我们看到了所有的接口列表。</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221221211256-252274101.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>我们点击其中的一个接口,例如&nbsp; ReadOrderNumber 接口,我们想读取PLC的订货号。点击之后,界面变化了。</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221221512458-863745421.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>不管他,我们直接点击读取。</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221221734373-1398345758.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>我们看到有数据返回了。我们成功读取到了一个json字符串,是不是很熟悉,获取规则如下:</p>
<p>因为读取是可能会失败的,也就是Content属性可能为空,那么我们就需要先判断 IsSuccess 为 True ,我们再去拿Content的数据。</p>
<p>如果为 False, 那么代表读取失败了,失败原因就在 Message 里面。这时候,你可以显示出来。或是记录日志,写入写入数据库都是可以的。</p>
<p>&nbsp;</p>
<p>我们再来尝试读取M100的short数据,在PLC里就是 MW100的值。</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221222148138-1626896656.png"></p>
<p>&nbsp;</p>
<p>&nbsp;结果如下:</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221222317661-1077378610.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>当我们重新选择ReadInt16接口的方法时,</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221222449225-1853680132.png"></p>
<p>&nbsp;</p>
<p>&nbsp;这时候已经显示当前的接口已经有一次调用的信息了。</p>
<p>我们再测试一个写入的操作。</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221222621620-464849752.png"></p>
<p>&nbsp;</p>
<p>&nbsp;我们可以读取验证一下、</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221222712295-1513602064.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>OK,我们现在用POSTMAIN测试一下。</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221223102293-630242219.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>需要注意的是,当前的方法是POST的。</p>
<p>&nbsp;</p>
<p>既然支持webapi,最后我们来尝试在网页上的操作,我们先做一个网页,放一个按钮,然后搞一个点击事件。去请求数据,然后显示读取结果。我们用到了 jquery,页面如下:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset="utf-8"&gt;
&lt;title&gt;HslCommunication教程&lt;/title&gt;
&lt;script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;测试Webapi的内容&lt;/h1&gt;
    &lt;p&gt;点击下方的按钮即可
    &lt;/p&gt;
    &lt;button onclick="GetPlcValue()"&gt;测试&lt;/button&gt;
    &lt;p&gt;结果如下:&lt;/p&gt;
    &lt;p id="test1"&gt;&lt;/p&gt;
    &lt;script type="text/javascript"&gt;
      function GetPlcValue(){
            $.ajax({
                type:'post',
                async: false,
                url:'http://127.0.0.1:8000/MainPlc/ReadInt16',
                dataType:'JSON',
                data:JSON.stringify({
                  'address': 'M100'
                }),
                success:function(result){
                  console.log(result);
                  if(result.IsSuccess){
                        $("#test1").text("Read Success, value is : " + result.Content);
                  }
                  else{
                        $("#test1").text("Read Failed, Message: " + result.Message);
                  }
                }
            });
      }
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
<p>  </p>
<p>  </p>
<p>很简单没啥内容,我们运行下。</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221225031852-1821498092.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>emmm,好像遇到跨域问题了,没关系,我们在服务器上稍微更改一下。</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221225137992-260984493.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>然后我们在页面上再次进行操作一下:</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221231641795-785915375.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>同理,写入都是类似的。</p>
<p>&nbsp;</p>
<p>最后我们来聊聊,如何实现你自己的API接口到 webapi上。例如我们要增加一个算法的接口,传入两个数,返回方法之和。</p>
<p>我们定义一个类。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">                public class Algorithms
                {
                       
                        public int Add(int a, int b )
                        {
                                return a + b;
                        }
                }</pre>
</div>
<p>我们看这个方法,定义了POST,当然了,GET也可以的,这样客户端的方式只要匹配就好了。</p>
<p>然后我们注册下服务</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221232105414-1588111202.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>完事,我们看看客户端的界面。</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221232210672-962544971.png"></p>
<p>&nbsp;</p>
<p>&nbsp;我们更改下传入的值</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201221232339311-646482892.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>当然,HttpServer进行用户名验证也是可以的。具体可以查看另一篇博客:https://www.cnblogs.com/dathlin/p/12335933.html</p>
<p>这时候的POSTMAN就是要携带基本的验证了,我们来看看加一个权限控制的api的使用方法</p>
<p>&nbsp;</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">                public class Algorithms
                {
                       
                        public int Add(int a, int b )
                        {
                                return a + b;
                        }

                       
                       
                        public int Multipy( int a = 10, int b = 20 )
                        {
                                // 当前方法只能用户名为admin的调用
                                return a * b;
                        }
                }
</pre>
</div>
<p>  我们增加的第二个方法就是增加权限的控制,这样的话,客户端再请求的话,就是需要增加权限头</p>
<p>我们先看POSTMAN</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201222094021379-624033149.png"></p>
<p>&nbsp;</p>
<p>&nbsp;<img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201222094053304-634392478.png"></p>
<p>&nbsp;</p>
<p>&nbsp;我们看到了返回了数据,然后我们再看看发送的header里都有什么</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201222094131225-849219752.png"></p>
<p>&nbsp;</p>
<p>&nbsp;这个内容就校验的内容之一了,好了,我们在js里可以这么写代码了</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset="utf-8"&gt;
&lt;title&gt;HslCommunication教程&lt;/title&gt;
&lt;script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;测试Webapi的内容&lt;/h1&gt;
    &lt;p&gt;点击下方的按钮即可
    &lt;/p&gt;
    &lt;button onclick="GetPlcValue()"&gt;测试&lt;/button&gt;
    &lt;p&gt;结果如下:&lt;/p&gt;
    &lt;p id="test1"&gt;&lt;/p&gt;
    &lt;script type="text/javascript"&gt;
      function GetPlcValue(){
            $.ajax({
                headers:{
                  'Authorization': 'Basic YWRtaW46MTIzNDU2'
                },
                type:'post',
                async: false,
                url:'http://127.0.0.1:8000/Algorithms/Multipy',
                dataType:'JSON',
                data:JSON.stringify({
                  'a': 10,
                  'b': 20
                }),
                success:function(result){
                  console.log(result);
                  $("#test1").text("Read Success, value is : " + result);
                  //if(result.IsSuccess){
                        //$("#test1").text("Read Success, value is : " + result.Content);
                  //}
                  //else{
                        //$("#test1").text("Read Failed, Message: " + result.Message);
                  //}
                }
            });
      }
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
<p>  然后再运行,就是返回了正确的结果了</p>
<p><img src="https://img2020.cnblogs.com/blog/1203682/202012/1203682-20201222094321671-1236452790.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>如果有什么其他的问题,欢迎留言。</p>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/dathlin/p/14170802.html
頁: [1]
查看完整版本: 基于HslCommunication实现 基于WebApi, javascript来读写PLC数据,包括三菱,西门子,欧姆龙,modbus,ab plc,台达,横河,信捷,松下,汇川,基恩士,富士,LS等等PLC