• iNeuOS工业互联平台,iNeuKernel(物联网核心组件)远程控制标准化设计与实现。发布v2.3版本。


    目       录

    1.      概述... 2

    2.      平台演示... 2

    3.      控制端与iNeuKernel的交互协议... 3

    4.      设备驱动实现控制业务... 4

    5.      应用效果... 9

    5.1           CS客户端... 9

    5.2           BS浏览器... 9

    5.3           Linux环境测试... 10

    5.4           视频应用效果... 10


     >>iNeuKernel v2.3版本下载:https://pan.baidu.com/s/1nxpnC7FazBBVyK9zUFgjyg


     1.   概述

            2019年6月,iNeuOS发布第一个版本以来,实际合作的客户和在谈合作的客户,对于云端控制或云端修改参数的需求比较多,基本上是共识。将来5G的发展和应用,云端控制将是一个常态,在iNeuOS的规划中已经作为一项重要工作。之所以没有早期开发云端控制部分,是因为我们所在的工业领域(煤炭、钢铁、化工、电力等)是重工业,与互联网传输数据都有严格的限制,更不可能让云端控制,另外涉及到安全问题。

            大部分云端控制的需求涉及行业一般为环保、农业、电工装备等,业务需求一般是远程修改参数和控制一些开关状态,但是这些操作不会带来致命事故,更多的是从远程运维角度考虑的需求。

            现在iNeuKernel(设备容器)物联网核心组件,已经具备了远程控制服务功能,支持终端设备或软件以Socket或WebSocket通道协议,通过标准的文本协议与硬件或传感进行交互,实现远程修改参数和控制的目的。后续会把这项功能集成到iNeuView(Web组态)视图建模,可以通过组态页面操作实现远程控制。

            参见以前的文章:

          《终端控制传感器或设备,形成回路控制

          《4种通讯模式机制

    2.   平台演示

    在线演示:http://demo.ineuos.net  (注:服务器比较慢,请耐心等待。自已注册用户,体验系统功能)

    视频演示:http://www.ineuos.net/video/iNeuOS%20and%20app.mp4

    驱动开发:http://www.ineuos.net/index.php/products/ineukernel-15.html (v2.1版本)

    手机APP:http://demo.ineuos.net/app/ineuos_app.apk

    3.   控制端与iNeuKernel的交互协议

          控制端可以理解为iNeuView组态,控制操作通过交互协议与后台的iNeuKernel进行交互,进一步与设备驱动交互,对设备进行控制操作,当然其他终端的操作链路也是这样的。示意,如下图:

         交互协议充分考虑了系统命令和设备命令两种情况,一个是对后台服务进行远程控制,一个是对设备进行控制,综合完成远程运维业务需求。示意,如下图:

    4.   设备驱动实现控制业务

           以Modbus RTU通讯协议为例,实现远程控制业务需求。IRunDevice接口有一个RunServiceConnector函数接口接收控制端发来的交互信息,交互信息是一个DevicePacket协议包。

          注:具体代码事例,可以加物联网&大数据技术群:54256083,找群主

        (1)DevicePacket设备协议包转换成JSON格式

    "DevicePacket": {
        "OperationCode": "20200206202127025",
        "DeviceId": "92b2306385f749d494a9b24f469cac87",
        "DeviceName": "Modbus",
        "Desc": "",
        "DeviceFrames": [
          {
            "TagId": "79be2b1e790f4c06873a8446bca755ea",
            "TagName": "tag1",
            "DataType": "int",
            "Value": "true",
            "FrameId": "20200206202127032",
            "OperationType": "setack",
            "Code": "200",
            "Desc": "淇�敼鎴愬姛",
            "Time": "2020-02-06T20:21:27.0322772+08:00"
          }
        ]
      }

      (2)RunServiceConnector函数接口接收设备协议包

           /// <summary>
            /// 与服务(Service)交互使用的接口,与service.OnServiceConnector配合使用。
            /// </summary>
            /// <param name="fromService"></param>
            /// <param name="toDevice"></param>
            /// <param name="asyncService"></param>
            /// <returns></returns>
            public override IServiceConnectorCallbackResult RunServiceConnector(IFromService fromService, IServiceToDevice toDevice, AsyncServiceConnectorCallback asyncService)
            {
                if(!(toDevice.Object is DevicePacket))
                {
                    OnDeviceRuningLog("RunServiceConnector 接收到的为非 DevicePacket 对象");
                    return null;
                }
                //设置优先调用此设备驱动
                this.DevicePriority = DevicePriority.Priority;
                //接收过来的设备协议包
                this._devicePacket = (DevicePacket)toDevice.Object;
                //异步返回操作结果,把操作最终的结果反馈给代理服务,最终通知操作端
                this._asyncServiceConnectorCallback = asyncService;
                //设置为当前为接收状态
                this._sendCommandState = SendCommandState.Received;
                OnDeviceRuningLog("已经接收到命令交换信息,正在准备下发命令......");
                return new ServiceConnectorCallbackResult(true, null);
            }

      (3)GetConstantCommand()函数接口获得下发控制命令

               if (_sendCommandState == SendCommandState.Received)   //发送命令
                {
                    #region 构建发送命令
                    if (_devicePacket != null)
                    {
                        #region
                        _sendCommandState = SendCommandState.ReadySending;
                        foreach (DeviceFrame df in _devicePacket.DeviceFrames)
                        {
                            ITag tag = tags.Where(t => t.TagId == df.TagId && t.TagName == df.TagName).FirstOrDefault();
                            if (tag != null)
                            {
                                IModbusMessage requestMsg = null;
                                byte[] reqeustBytes = null;
                                switch (tag.Function)
                                {
                                    case 0x01:   //线圈,bool值
                                        reqeustBytes = _modbusRtuMaster.BuildWriteSingleCoilCommand(byte.Parse(tag.SlaveId), ushort.Parse(tag.Address), bool.Parse(df.Value), out requestMsg);
                                        break;
                                    case 0x03:   //保持寄存器,数值
                                        reqeustBytes = _modbusRtuMaster.BuildWriteSingleRegisterCommand(byte.Parse(tag.SlaveId), ushort.Parse(tag.Address), ushort.Parse(df.Value), out requestMsg);
                                        break;
                                    default:
                                        OnDeviceRuningLog("没有找到可写入命令的标签配置");
                                        break;
                                }
    
                                if (reqeustBytes != null)
                                {
                                    requestList.Add(new RequestInfo(reqeustBytes, new SendObject(requestMsg, tag)));
                                }
                            }
                        }
                        _sendCommandState = SendCommandState.Sending;
                        #endregion
                    }
                    else
                    {
                        _sendCommandState = SendCommandState.None;
                    }
                    #endregion
     }

      (4)Communicate接收控制命令返回结果

                byte[] revData = info.Data;
                IModbusMessage requestMessage = _sendObject.ModbusMessage;
                ITag tag = _sendObject.Tag;
    //用于验证修改值返回数据
                bool modiftyData = false;  //是否修改返回数据
                bool validate = true;      //验证是否通过
                if(requestMessage.FunctionCode == Modbus.Modbus.WriteSingleCoil)
                {
                    modiftyData = true;
                    try
                    {
                         _modbusRtuMaster.ValidateWriteSingleCoilResponse(revData, requestMessage);
                    }
                    catch(Exception ex)
                    {
                        validate = false;
                        OnDeviceRuningLog(ex.Message);
                    }
                }
                else if(requestMessage.FunctionCode==Modbus.Modbus.WriteSingleRegister)
                {
                    modiftyData = true;
                    try
                    {
                        _modbusRtuMaster.ValidateWriteSingleRegisterResponse(revData, requestMessage);
                    }
                    catch (Exception ex)
                    {
                        validate = false;
                        OnDeviceRuningLog(ex.Message);
                    }
                }
    
              if(modiftyData)
                {
                    if (_devicePacket != null)
                    {
                        DeviceFrame df = _devicePacket.DeviceFrames.Where(t => t.TagId == tag.TagId).FirstOrDefault();
                        if (df != null)
                        {
                            if (validate)  //正常
                            {
                                df.OperationType = "setack";
                                df.Code = "200";
                                df.Desc = "修改成功";
                                _modifyConfirm.Add(df.TagId, true);
                            }
                            else
                            {
                                df.OperationType = "setack";
                                df.Code = "-1";
                                df.Desc = "修改失败";
                            }
     
                            OnDeviceRuningLog(String.Format("{0},{1}!!!", tag.TagName, df.Desc));
     
                            if (_asyncServiceConnectorCallback != null)
                            {
                                _asyncServiceConnectorCallback.Invoke(_devicePacket);
                            }
                        }
                        else
                        {
                            OnDeviceRuningLog("获得修改设备帧数据为空");
                        }
                        bool modifyComplete = true;
                        foreach(DeviceFrame d in _devicePacket.DeviceFrames)
                        {
                            modifyComplete=_modifyConfirm.ContainsKey(d.TagId);
                            if(!modifyComplete)
                            {
                                break;
                            }
                        }
    
                        if(modifyComplete)
                        {
                            _devicePacket = null;
                            _asyncServiceConnectorCallback = null;
                            _modifyConfirm.Clear();
                            _sendCommandState = SendCommandState.Complete;
                            OnDeviceRuningLog("修改参数已全部完成!!!");
                        }
                    }
                }

    5.   应用效果

    5.1    CS客户端

    5.2    BS浏览器

    5.3    Linux环境测试

    5.4    视频应用效果


    文章:

     .NET Core开发的iNeuOS工业互联网平台,发布 iNeuDA 数据分析展示组件,快捷开发图形报表和数据大屏

     [视频演示].NET Core开发的iNeuOS物联网平台,实现从设备&PLC、云平台、移动APP数据链路闭环

     .NET Core开发的iNeuOS物联网平台部署树霉派(raspbian),从网关到云端整体解决方案

     .NET Core开发的iNeuOS物联网平台部署在Ubuntu操作系统,无缝跨平台

     iNeuOS 物联网云操作系统2.0发布,集成设备容器、视图建模、机器学习三大模块

     iNeuOS云操作系统,.NET Core全系打造


     物联网&大数据技术 QQ群:54256083 

     物联网&大数据合作 QQ群:727664080

     网站:http://www.ineuos.net

     联系QQ:504547114

     合作微信:wxzz0151

  • 相关阅读:
    python笔记第二节
    python笔记第一节
    hadoop 家族生态圈 小结
    JDBC:impala 连接DEMO
    关于excel表格的导入导出的工具类。(java 反射 excel)
    简单写了一个类似全文检索的帮助类。
    关于框架SSM中文乱码问题总结
    关于SpringMVC, bean对象中含有Date型变,改如何接收数据的问题。
    Nlog
    java手动编译
  • 原文地址:https://www.cnblogs.com/lsjwq/p/12292859.html
Copyright © 2020-2023  润新知