場景模擬
假設你有一批非標設備需要對接,你需要根據設備方提供的協議,為IoTGateway開發驅動,進行數據交互。
文章比較長也可以到官網會有更好的體驗,地址:
http://iotgateway.net/docs/iotgateway/driver/tcpclient
請先瀏覽上一篇驅動簡介:
http://iotgateway.net/docs/iotgateway/driver/drvier
協議概述
對方提供了如下協議文檔:
設備作為TCPServer,端口6666?字節序:Little-Endian,即低地址存放低位
請求回復
需要你主動發起讀取請求:0x01 02 03 04?設備回復:0x08 01 41 D6 3D 71 1A 20
參數說明
總字節數
(byte[0])即0x08:用于簡單的校驗
運行狀態
(byte[1])即0x01:1為運行;其他為停止
設備溫度
(byte[2]-byte[5])即0x41 D6 3D 71:單精度浮點數值26.78
電機轉速
(byte[6]-byte[7])即0x1A 20:對應16進制無符號整型,倍率0.01值66.88
驅動開發
創建驅動項目
在解決方案->Drivers文件夾,右鍵添加->新建項目->C#類庫?
項目名DriverSimTcpClient,放在
iotgateway\Plugins\Drivers
路徑下?修改Class1為SimTcpClient
雙擊項目,修改配置?
iotgateway\Plugins\Drivers\DriverSimTcpClient\DriverSimTcpClient.csproj
<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><TargetFramework>net6.0</TargetFramework><OutputPath>../../../IoTGateway/bin/Debug/net6.0/drivers</OutputPath><CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable></PropertyGroup><ItemGroup><PackageReference Include="SimpleTCP.Core" Version="1.0.4" /></ItemGroup><ItemGroup><ProjectReference Include="..\..\PluginInterface\PluginInterface.csproj" /></ItemGroup> </Project>
說明:
OutputPath節點指定了生成項目的文件夾
SimpleTCP.Core是一個TCP客戶端庫(你也可以自己寫)
ProjectReference節點引用了PluginInterface項目
CopyLocalLockFileAssemblies節點可以確保你引用的nuget拷貝到driver文件夾下 :::
編寫項目代碼:
iotgateway\Plugins\Drivers\DriverSimTcpClient\SimTcpClient.cs
using PluginInterface;
using SimpleTCP;
using System;
using System.Text;
namespace DriverSimTcpClient
{[DriverSupported("SimTcpServerDevice")][DriverInfoAttribute("SimTcpClient", "V1.0.0", "Copyright iotgateway? 2022-06-04")]public class SimTcpClient : IDriver{/// <summary>/// tcp客戶端/// </summary>private SimpleTcpClient? client;/// <summary>/// 緩存最新的服務器返回的原始數據/// </summary>private byte[] latestRcvData;#region 配置參數[ConfigParameter("設備Id")]public Guid DeviceId { get; set; }[ConfigParameter("IP地址")]public string IpAddress { get; set; } = "127.0.0.1";[ConfigParameter("端口號")]public int Port { get; set; } = 6666;/// <summary>/// 為了演示枚舉類型在web端的錄入,這里沒用到 但是你可以拿到/// </summary>[ConfigParameter("連接類型")]public ConnectionType ConnectionType { get; set; } = ConnectionType.Long;[ConfigParameter("超時時間ms")]public int Timeout { get; set; } = 300;[ConfigParameter("最小通訊周期ms")]public uint MinPeriod { get; set; } = 3000;#endregionpublic SimTcpClient(Guid deviceId){DeviceId = deviceId;}/// <summary>/// 判斷連接狀態/// </summary>public bool IsConnected{get{//客戶端對象不為空并且客戶端已連接則返回truereturn client != null && client.TcpClient.Connected;}}/// <summary>/// 進行連接/// </summary>/// <returns>連接是否成功</returns>public bool Connect(){try{//進行連接client = new SimpleTcpClient().Connect(IpAddress, Port);client.DataReceived += Client_DataReceived;}catch (Exception){return false;}return IsConnected;}/// <summary>/// 收到服務端數據/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void Client_DataReceived(object? sender, Message e){//如果收到的數據校驗正確,則放在內存中if (e.Data.Length == 8 && e.Data[0] == 0x08)latestRcvData = e.Data;}/// <summary>/// 斷開連接/// </summary>/// <returns>斷開是否成功</returns>public bool Close(){try{client.DataReceived -= Client_DataReceived;//斷開連接client?.Disconnect();return !IsConnected;}catch (Exception){return false;}}/// <summary>/// 釋放/// </summary>public void Dispose(){try{//釋放資源client?.Dispose();}catch (Exception){}}/// <summary>/// 發送數據/// </summary>private byte[] sendCmd = new byte[4] { 0x01, 0x02, 0x03, 0x04 };/// <summary>/// 解析并返回/// </summary>/// <param name="ioarg">ioarg.Address為起始變量字節編號;ioarg.ValueType為類型</param>/// <returns></returns>[Method("讀模擬設備數據", description: "讀模擬設備數據,開始字節和長度")]public DriverReturnValueModel Read(DriverAddressIoArgModel ioarg){var ret = new DriverReturnValueModel { StatusType = VaribaleStatusTypeEnum.Good };ushort startIndex;//判斷地址是否為整數if (!ushort.TryParse(ioarg.Address, out startIndex)){ret.StatusType = VaribaleStatusTypeEnum.Bad;ret.Message = "起始字節編號錯誤";return ret;}//連接正常則進行讀取if (IsConnected){try{//發送請求client?.Write(sendCmd);//等待恢復,這里可以優化Thread.Sleep(Timeout);if (latestRcvData == null){ret.StatusType = VaribaleStatusTypeEnum.Bad;ret.Message = "沒有收到數據";}else{//解析數據,并返回switch (ioarg.ValueType){case DataTypeEnum.UByte:case DataTypeEnum.Byte:ret.Value = latestRcvData[startIndex];break;case DataTypeEnum.Int16:var buffer16 = latestRcvData.Skip(startIndex).Take(2).ToArray();ret.Value = BitConverter.ToInt16(new byte[] { buffer16[0], buffer16[1] }, 0);break;case DataTypeEnum.Float://拿到有用的數據var buffer32 = latestRcvData.Skip(startIndex).Take(4).ToArray();//大小端轉換一下ret.Value = BitConverter.ToSingle(new byte[] { buffer32[3], buffer32[2], buffer32[1], buffer32[0] }, 0);break;default:break;}}}catch (Exception ex){ret.StatusType = VaribaleStatusTypeEnum.Bad;ret.Message = $"讀取失敗,{ex.Message}";}}else{ret.StatusType = VaribaleStatusTypeEnum.Bad;ret.Message = "連接失敗";}return ret;}public async Task<RpcResponse> WriteAsync(string RequestId, string Method, DriverAddressIoArgModel Ioarg){RpcResponse rpcResponse = new() { IsSuccess = false, Description = "設備驅動內未實現寫入功能" };return rpcResponse;}}public enum ConnectionType{Long,Short}
}
注冊驅動
生成
DriverSimTcpClient?
項目iotgateway\IoTGateway\bin\Debug\net6.0\drivers\net6.0
路徑下可以看到生成了DriverSimTcpClient.dll運行IoTGateway,訪問本地518端口
添加驅動
網關配置->驅動管理->添加
注意:添加驅動后需要重啟一下項目,后面會優化
創建設備
采集配置->設備維護->添加設備
?
添加變量
采集配置->設備維護->添加設備
手動添加或者通過excel批量導入下面變量
變量名 | 方法 | 地址 | 類型 | 表達式 | 設備名 |
---|---|---|---|---|---|
運行狀態 | Read | 1 | uint8 | 模擬設備 | |
設備溫度 | Read | 2 | float | 模擬設備 | |
電機轉速 | Read | 6 | int16 | raw*0.01 | 模擬設備 |
開始采集
采集配置->設備維護->編輯設備?
啟動TcpServer
運行你熟悉的TCPServer測試工具,啟動端口6666,網關客戶端連接后發送響應報文