iNeuOS工業互聯平臺,iNeuKernel(物聯網核心組件)遠程式控制制標準化設計與實現。發佈v2.3版本。

来源:https://www.cnblogs.com/lsjwq/archive/2020/02/10/12292859.html
-Advertisement-
Play Games

目 錄 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 視頻應用效果.. ...


目       錄

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • VS Code是微軟開源的一款編輯器,插件系統十分的豐富。本文就介紹瞭如何使用VS Code搭建Go語言開發環境。 VS Code配置Go語言開發環境 說在前面的話,Go語言是採用UTF8編碼的,理論上使用任何文本編輯器都能做Go語言開發。大家可以根據自己的喜好自行選擇。編輯器/IDE沒有最好只有最 ...
  • 變數和常量是編程中必不可少的部分,也是很好理解的一部分。 標識符與關鍵字 標識符 在編程語言中標識符就是程式員定義的具有特殊意義的詞,比如變數名、常量名、函數名等等。 Go語言中標識符由字母數字和_(下劃線)組成,並且只能以字母和_開頭。 舉幾個例子:abc, _, _123, a123。 關鍵字 ...
  • Go語言中有豐富的數據類型,除了基本的整型、浮點型、布爾型、字元串外,還有數組、切片、結構體、函數、map、通道(channel)等。Go 語言的基本類型和其他語言大同小異。 基本數據類型 整型 整型分為以下兩個大類: 按長度分為:int8、int16、int32、int64 對應的無符號整型:ui ...
  • JavaSE學習筆記(7) 數組 1、什麼是數組 數組是相同類型數據的有序集合。數組描述的是相同類型的若幹個數據,按照一定的先後次序排列組合而成。其中,每一個數據稱作一個元素,每個元素可以通過一個索引(下標)來訪問它們。數組的三個基本特點: 1. 長度是確定的。數組一旦被創建,它的大小就是不可以改變 ...
  • 運算符用於在程式運行時執行數學或邏輯運算。 運算符 Go 語言內置的運算符有: 算術運算符 關係運算符 邏輯運算符 位運算符 賦值運算符 算數運算符 運算符描述 + 相加 - 相減 * 相乘 / 相除 % 求餘 註意: ++(自增)和--(自減)在Go語言中是單獨的語句,並不是運算符。 關係運算符 ...
  • 流程式控制制是每種編程語言控制邏輯走向和執行次序的重要部分,流程式控制制可以說是一門語言的“經脈”。 Go語言中最常用的流程式控制制有if和for,而switch和goto主要是為了簡化代碼、降低重覆代碼而生的結構,屬於擴展類的流程式控制制。 if else(分支結構) if條件判斷基本寫法 Go語言中if條件判斷 ...
  • 本文主要介紹Go語言中切片(slice)及它的基本使用。 因為數組的長度是固定的並且數組長度屬於類型的一部分,所以數組有很多的局限性。 例如: func arraySum(x [3]int) int{ sum := 0 for _, v := range x{ sum = sum + v } ret ...
  • ASP .NET CORE 源碼地址:https://github.com/dotnet/ 下拉可以查找相應的源碼信息, 例如:查找 ASP .NET CORE Microsoft.Extensions.Hosting 源碼地址:https://github.com/dotnet/extension ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...