394 lines
13 KiB
C#
394 lines
13 KiB
C#
![]() |
using SimpleTCP;
|
|||
|
using System.Text;
|
|||
|
using System.IO.Ports;
|
|||
|
using PluginInterface;
|
|||
|
using Microsoft.Extensions.Logging;
|
|||
|
|
|||
|
namespace Other.Toledo
|
|||
|
{
|
|||
|
[DriverSupported("Toledo")]
|
|||
|
[DriverInfo("Toledo", "V1.0.0", "Copyright IoTGateway.net 20230220")]
|
|||
|
public class DeviceToledo : IDriver
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// tcp客户端
|
|||
|
/// </summary>
|
|||
|
private SimpleTcpClient? client;
|
|||
|
/// <summary>
|
|||
|
/// 缓存最新的服务器返回的原始数据
|
|||
|
/// </summary>
|
|||
|
private byte[]? latestRcvData;
|
|||
|
|
|||
|
private byte[] TempRcvData;
|
|||
|
private DateTime latestDate;
|
|||
|
|
|||
|
public ILogger _logger { get; set; }
|
|||
|
private readonly string _device;
|
|||
|
#region 配置参数
|
|||
|
|
|||
|
[ConfigParameter("设备Id")] public string DeviceId { get; set; }
|
|||
|
|
|||
|
[ConfigParameter("IP地址")]
|
|||
|
public string IpAddress { get; set; } = "127.0.0.1";
|
|||
|
|
|||
|
[ConfigParameter("端口号")]
|
|||
|
public int Port { get; set; } = 6666;
|
|||
|
|
|||
|
[ConfigParameter("数据位")]
|
|||
|
public int DataBits { get; set; } = 17;
|
|||
|
|
|||
|
[ConfigParameter("校验位")]
|
|||
|
public Parity Parity { get; set; } = Parity.None;
|
|||
|
|
|||
|
[ConfigParameter("停止位")]
|
|||
|
public StopBits StopBits { get; set; } = StopBits.One;
|
|||
|
|
|||
|
/// <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;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 生命周期
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 反射构造函数
|
|||
|
/// </summary>
|
|||
|
/// <param name="device"></param>
|
|||
|
/// <param name="logger"></param>
|
|||
|
public DeviceToledo(string device, ILogger logger)
|
|||
|
{
|
|||
|
_device = device;
|
|||
|
_logger = logger;
|
|||
|
|
|||
|
_logger.LogInformation($"Device:[{_device}],Create()");
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 判断连接状态
|
|||
|
/// </summary>
|
|||
|
public bool IsConnected
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
//客户端对象不为空并且客户端已连接则返回true
|
|||
|
return client != null && client.TcpClient != null && client.TcpClient.Connected;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 进行连接
|
|||
|
/// </summary>
|
|||
|
/// <returns>连接是否成功</returns>
|
|||
|
public bool Connect()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
//进行连接
|
|||
|
client = new SimpleTcpClient().Connect(IpAddress, Port);
|
|||
|
client.DataReceived += Client_DataReceived;
|
|||
|
_logger.LogInformation($"Device:[{_device}],Connect()");
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
_logger.LogError(ex, $"Device:[{_device}],Connect()");
|
|||
|
return false;
|
|||
|
}
|
|||
|
return IsConnected;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 断开连接
|
|||
|
/// </summary>
|
|||
|
/// <returns>断开是否成功</returns>
|
|||
|
public bool Close()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
client.DataReceived -= Client_DataReceived;
|
|||
|
//断开连接
|
|||
|
client?.Disconnect();
|
|||
|
|
|||
|
_logger.LogInformation($"Device:[{_device}],Close()");
|
|||
|
return !IsConnected;
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
_logger.LogError(ex, $"Device:[{_device}],Close()");
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 释放
|
|||
|
/// </summary>
|
|||
|
public void Dispose()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
//释放资源
|
|||
|
client?.Dispose();
|
|||
|
|
|||
|
// Suppress finalization.
|
|||
|
GC.SuppressFinalize(this);
|
|||
|
_logger.LogInformation($"Device:[{_device}],Dispose()");
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
_logger.LogError(ex, $"Device:[{_device}],Dispose()");
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 读写方法
|
|||
|
/// <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 };
|
|||
|
|
|||
|
//连接正常则进行读取
|
|||
|
if (IsConnected)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
ushort address, count;
|
|||
|
ret = AnalyseAddress(ioArg, out address, out count);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
if (
|
|||
|
(latestRcvData == null) ||
|
|||
|
(DateTime.Now.Subtract(latestDate).TotalSeconds > 10)
|
|||
|
)
|
|||
|
{
|
|||
|
latestRcvData = null;
|
|||
|
ret.StatusType = VaribaleStatusTypeEnum.Bad;
|
|||
|
ret.Message = "没有收到数据";
|
|||
|
|
|||
|
if (ioArg.ValueType == DataTypeEnum.Custome1)
|
|||
|
{
|
|||
|
ret.Value = latestDate;
|
|||
|
ret.StatusType = VaribaleStatusTypeEnum.Good;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
|
|||
|
//解析数据,并返回
|
|||
|
switch (ioArg.ValueType)
|
|||
|
{
|
|||
|
case DataTypeEnum.UByte:
|
|||
|
|
|||
|
case DataTypeEnum.Byte:
|
|||
|
ret.Value = latestRcvData[address];
|
|||
|
break;
|
|||
|
case DataTypeEnum.Int16:
|
|||
|
ret.Value = (short)latestRcvData[address];
|
|||
|
//var buffer16 = latestRcvData.Skip(address).Take(count).ToArray();
|
|||
|
//ret.Value = BitConverter.ToInt16(buffer16[0], 0);
|
|||
|
break;
|
|||
|
case DataTypeEnum.Int32:
|
|||
|
//拿到有用的数据
|
|||
|
var bufferCustome1 = latestRcvData.Skip(address).Take(count).ToArray();
|
|||
|
|
|||
|
var strCustome1 = Encoding.ASCII.GetString(bufferCustome1);
|
|||
|
if (strCustome1.Contains("\0"))
|
|||
|
strCustome1 = strCustome1.Replace("\0", "");
|
|||
|
|
|||
|
ret.Value = strCustome1 == "" ? 0 : int.Parse(strCustome1);
|
|||
|
|
|||
|
break;
|
|||
|
case DataTypeEnum.Float:
|
|||
|
//拿到有用的数据
|
|||
|
var buffer32 = latestRcvData.Skip(address).Take(count).ToArray();
|
|||
|
//大小端转换一下
|
|||
|
ret.Value = BitConverter.ToSingle(buffer32, 0);
|
|||
|
break;
|
|||
|
case DataTypeEnum.AsciiString:
|
|||
|
//拿到有用的数据
|
|||
|
var bufferAscii = latestRcvData.Skip(address).Take(count).ToArray();
|
|||
|
|
|||
|
|
|||
|
var str = Encoding.ASCII.GetString(bufferAscii);
|
|||
|
if (str.Contains("\0"))
|
|||
|
str = str.Replace("\0", "");
|
|||
|
|
|||
|
ret.Value = str;
|
|||
|
break;
|
|||
|
case DataTypeEnum.Custome1:
|
|||
|
ret.Value = latestDate;
|
|||
|
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 = "设备驱动内未实现写入功能" };
|
|||
|
await Task.CompletedTask;
|
|||
|
return rpcResponse;
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 私有方法
|
|||
|
|
|||
|
private byte[] addBytes(byte[] data1, byte[] data2)
|
|||
|
{
|
|||
|
byte[] data3 = new byte[(data1 != null ? data1.Length : 0) + data2.Length]; ;
|
|||
|
|
|||
|
if (data1 != null)
|
|||
|
{
|
|||
|
data1.CopyTo(data3, 0);
|
|||
|
}
|
|||
|
if (data2 != null)
|
|||
|
data2.CopyTo(data3, data1 != null ? data1.Length : 0);
|
|||
|
|
|||
|
return data3;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 收到服务端数据
|
|||
|
/// </summary>
|
|||
|
/// <param name="sender"></param>
|
|||
|
/// <param name="e"></param>
|
|||
|
private void Client_DataReceived(object? sender, Message e)
|
|||
|
{
|
|||
|
if (e.Data.Length == 0) { return; }
|
|||
|
|
|||
|
latestDate = DateTime.Now;
|
|||
|
|
|||
|
|
|||
|
//合并TempRcvData, e.Data
|
|||
|
byte[] RcvData = addBytes(TempRcvData, e.Data);
|
|||
|
|
|||
|
//_logger.LogInformation($"Device:[{_device}],TempRcvData:{BitConverter.ToString(TempRcvData)}," +
|
|||
|
// $"e.Data:{BitConverter.ToString(e.Data)},RcvData:{BitConverter.ToString(RcvData)}");
|
|||
|
|
|||
|
int index = RcvData.Length - 1;
|
|||
|
|
|||
|
if (RcvData.Length >= DataBits)
|
|||
|
{
|
|||
|
while (index > 0)
|
|||
|
{
|
|||
|
if (RcvData[index] != 0x0d)
|
|||
|
{
|
|||
|
index--;
|
|||
|
continue;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (index + 1 >= DataBits && RcvData[index - DataBits + 1] == 0x02)
|
|||
|
{
|
|||
|
latestRcvData = new byte[DataBits];
|
|||
|
Array.Copy(RcvData, index - DataBits + 1, latestRcvData, 0, DataBits);
|
|||
|
|
|||
|
|
|||
|
if (RcvData.Length - index > 1)
|
|||
|
{
|
|||
|
TempRcvData = new byte[RcvData.Length - index];
|
|||
|
Array.Copy(RcvData, index + 1, TempRcvData, 0, RcvData.Length - index - 1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (latestRcvData != null) { Array.Clear(latestRcvData, 0, latestRcvData.Length); }
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (TempRcvData != null)
|
|||
|
{
|
|||
|
Array.Clear(TempRcvData, 0, TempRcvData.Length);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
TempRcvData = RcvData;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 分析地址的开始地址和总位数
|
|||
|
/// </summary>
|
|||
|
/// <param name="ioarg"></param>
|
|||
|
/// <param name="StartAddress">起始位置</param>
|
|||
|
/// <param name="ReadCount">总位数</param>
|
|||
|
/// <returns></returns>
|
|||
|
private DriverReturnValueModel AnalyseAddress(DriverAddressIoArgModel ioarg, out ushort StartAddress, out ushort ReadCount)
|
|||
|
{
|
|||
|
DriverReturnValueModel ret = new() { StatusType = VaribaleStatusTypeEnum.Good };
|
|||
|
try
|
|||
|
{
|
|||
|
if (ioarg.ValueType == DataTypeEnum.AsciiString)
|
|||
|
{
|
|||
|
StartAddress = ushort.Parse(ioarg.Address.Split(',')[0]);
|
|||
|
ReadCount = ushort.Parse(ioarg.Address.Split(',')[1]);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
StartAddress = ushort.Parse(ioarg.Address);
|
|||
|
ReadCount = 1;
|
|||
|
}
|
|||
|
return ret;
|
|||
|
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
ret.StatusType = VaribaleStatusTypeEnum.AddressError;
|
|||
|
ret.Message = ex.Message;
|
|||
|
StartAddress = 0;
|
|||
|
ReadCount = 0;
|
|||
|
return ret;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
}
|
|||
|
|
|||
|
public enum ConnectionType
|
|||
|
{
|
|||
|
Long,
|
|||
|
Short
|
|||
|
}
|
|||
|
}
|