mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-22 03:24:29 +08:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
877445bc0a | ||
![]() |
9a5b345bde | ||
![]() |
fc9e8ea7b3 | ||
![]() |
32be6fcfc1 | ||
![]() |
49847236c2 | ||
![]() |
d8424443e6 | ||
![]() |
f3b571ec3f | ||
![]() |
99318bb5d7 | ||
![]() |
1aa154c9aa | ||
![]() |
c65d8a445b | ||
![]() |
80f4f85570 | ||
![]() |
5beee43a6b |
@@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
<Version>2.0.6.0</Version>
|
||||
<Version>2.0.7.0</Version>
|
||||
<Authors>Diego</Authors>
|
||||
<Product>ThingsGateway</Product>
|
||||
<Copyright>© 2023-present Diego</Copyright>
|
||||
|
@@ -48,8 +48,7 @@
|
||||
var controllerTypes = App.EffectiveTypes.
|
||||
Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass
|
||||
&& u.IsDefined(typeof(Microsoft.AspNetCore.Components.RouteAttribute), false))
|
||||
.Where(it => it.Assembly != typeof(BlazorApp).Assembly)
|
||||
;
|
||||
.Where(it => it.Assembly != typeof(BlazorApp).Assembly);
|
||||
var assemblys = controllerTypes?.Select(it => it.Assembly)?.Distinct();
|
||||
return assemblys;
|
||||
}
|
||||
|
@@ -55,7 +55,7 @@ public interface IVariableService : ITransient
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
Task<MemoryStream> ExportFileAsync(List<DeviceVariable> collectDeviceVariables = null);
|
||||
Task<MemoryStream> ExportFileAsync(List<DeviceVariable> collectDeviceVariables = null, string deviceName = null);
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
|
@@ -318,7 +318,7 @@ public class VariableService : DbRepository<DeviceVariable>, IVariableService
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("导出采集变量表", IsRecordPar = false)]
|
||||
public async Task<MemoryStream> ExportFileAsync(List<DeviceVariable> deviceVariables = null)
|
||||
public async Task<MemoryStream> ExportFileAsync(List<DeviceVariable> deviceVariables = null, string deviceName = null)
|
||||
{
|
||||
deviceVariables ??= await GetListAsync(a => !a.IsMemoryVariable);
|
||||
|
||||
@@ -343,7 +343,8 @@ public class VariableService : DbRepository<DeviceVariable>, IVariableService
|
||||
);
|
||||
Dictionary<string, object> variableExport = new();
|
||||
//变量实体没有包含设备名称,手动插入
|
||||
variableExport.Add(ExportHelpers.DeviceName, collectDeviceDicts[devData.DeviceId].Name);
|
||||
var devName = collectDeviceDicts.GetValueOrDefault(devData.DeviceId)?.Name ?? deviceName;
|
||||
variableExport.Add(ExportHelpers.DeviceName, devName);
|
||||
|
||||
foreach (var item in data)
|
||||
{
|
||||
|
@@ -3175,7 +3175,7 @@
|
||||
编辑变量
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Application.IVariableService.ExportFileAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable})">
|
||||
<member name="M:ThingsGateway.Application.IVariableService.ExportFileAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable},System.String)">
|
||||
<summary>
|
||||
导出
|
||||
</summary>
|
||||
@@ -3270,7 +3270,7 @@
|
||||
<member name="M:ThingsGateway.Application.VariableService.MemoryVariableExportFileAsync(System.Collections.Generic.List{ThingsGateway.Application.MemoryVariable})">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Application.VariableService.ExportFileAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable})">
|
||||
<member name="M:ThingsGateway.Application.VariableService.ExportFileAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable},System.String)">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Application.VariableService.MemoryVariablePreviewAsync(Microsoft.AspNetCore.Components.Forms.IBrowserFile)">
|
||||
|
@@ -536,6 +536,7 @@ public class AlarmWorker : BackgroundService
|
||||
HisAlarmTask = await Task.Factory.StartNew(async () =>
|
||||
{
|
||||
_logger?.LogInformation($"历史报警线程开始");
|
||||
await Task.Yield();//返回线程控制,不再阻塞
|
||||
try
|
||||
{
|
||||
await Task.Delay(500, stoppingToken.Token);
|
||||
@@ -561,8 +562,14 @@ public class AlarmWorker : BackgroundService
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (stoppingToken.Token.IsCancellationRequested)
|
||||
{
|
||||
IsExited = true;
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
_logger.LogWarning("连接历史报警表失败,尝试初始化表");
|
||||
sqlSugarClient.CodeFirst.InitTables(typeof(HistoryAlarm));
|
||||
isSuccess = true;
|
||||
StatuString = OperResult.CreateSuccessResult();
|
||||
|
@@ -269,6 +269,7 @@ public class CollectDeviceCore
|
||||
{
|
||||
isInitSuccess = false;
|
||||
GlobalDeviceData.CollectDevices.RemoveWhere(it => it.Id == Device.Id);
|
||||
easyLock.SafeDispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -62,6 +62,7 @@ public class CollectDeviceThread : IAsyncDisposable
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await StopThreadAsync();
|
||||
easyLock.SafeDispose();
|
||||
CollectDeviceCores.Clear();
|
||||
}
|
||||
|
||||
|
@@ -167,7 +167,7 @@ public class HistoryValueWorker : BackgroundService
|
||||
HistoryValueTask = await Task.Factory.StartNew(async () =>
|
||||
{
|
||||
_logger?.LogInformation($"历史数据线程开始");
|
||||
IsExited = false;
|
||||
await Task.Yield();//返回线程控制,不再阻塞
|
||||
try
|
||||
{
|
||||
var result = await GetHisDbAsync();
|
||||
@@ -191,8 +191,14 @@ public class HistoryValueWorker : BackgroundService
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (stoppingToken.Token.IsCancellationRequested)
|
||||
{
|
||||
IsExited = true;
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
_logger.LogWarning("连接历史数据表失败,尝试初始化表");
|
||||
sqlSugarClient.CodeFirst.InitTables(typeof(HistoryValue));
|
||||
LastIsSuccess = true;
|
||||
StatuString = OperResult.CreateSuccessResult();
|
||||
@@ -204,6 +210,7 @@ public class HistoryValueWorker : BackgroundService
|
||||
_logger.LogWarning(ex, "连接历史数据库失败");
|
||||
}
|
||||
}
|
||||
IsExited = false;
|
||||
|
||||
while (!stoppingToken.Token.IsCancellationRequested)
|
||||
{
|
||||
@@ -299,7 +306,6 @@ public class HistoryValueWorker : BackgroundService
|
||||
LastIsSuccess = false;
|
||||
}
|
||||
}
|
||||
IsExited = true;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -317,9 +323,11 @@ public class HistoryValueWorker : BackgroundService
|
||||
IsExited = true;
|
||||
_logger?.LogError(ex, $"历史数据循环异常");
|
||||
}
|
||||
IsExited = true;
|
||||
}
|
||||
, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重启
|
||||
/// </summary>
|
||||
|
@@ -225,7 +225,13 @@ public class UploadDeviceCore
|
||||
{
|
||||
_logger?.LogError(ex, $"{Device.Name} 释放失败");
|
||||
}
|
||||
isInitSuccess = false;
|
||||
finally
|
||||
{
|
||||
isInitSuccess = false;
|
||||
easyLock.SafeDispose();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -51,6 +51,7 @@ public class UploadDeviceThread : IAsyncDisposable
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await StopThreadAsync();
|
||||
easyLock.SafeDispose();
|
||||
UploadDeviceCores.Clear();
|
||||
}
|
||||
|
||||
|
@@ -127,7 +127,7 @@ public abstract class DriverDebugUIBase : ComponentBase, IDisposable
|
||||
/// 导入设备
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task DownDeviceExportAsync(CollectDevice data)
|
||||
public async Task DeviceImportAsync(CollectDevice data)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -145,7 +145,7 @@ public abstract class DriverDebugUIBase : ComponentBase, IDisposable
|
||||
/// 导入变量
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task DownDeviceExportAsync(List<DeviceVariable> data)
|
||||
public async Task DeviceVariableImportAsync(List<DeviceVariable> data)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -159,6 +159,7 @@ public abstract class DriverDebugUIBase : ComponentBase, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
@@ -187,6 +188,51 @@ public abstract class DriverDebugUIBase : ComponentBase, IDisposable
|
||||
isDownExport = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出到excel
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task DownDeviceExportAsync(CollectDevice data)
|
||||
{
|
||||
try
|
||||
{
|
||||
isDownExport = true;
|
||||
StateHasChanged();
|
||||
using var memoryStream = await CollectDeviceService.ExportFileAsync(new List<CollectDevice>() { data });
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
using var streamRef = new DotNetStreamReference(stream: memoryStream);
|
||||
_helper ??= await JS.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Admin.Blazor.Core/js/downloadFileFromStream.js");
|
||||
await _helper.InvokeVoidAsync("downloadFileFromStream", $"设备导出{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx", streamRef);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDownExport = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出到excel
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task DownDeviceVariableExportAsync(List<DeviceVariable> data, string devName)
|
||||
{
|
||||
try
|
||||
{
|
||||
isDownExport = true;
|
||||
StateHasChanged();
|
||||
using var memoryStream = await VariableService.ExportFileAsync(data, devName);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
using var streamRef = new DotNetStreamReference(stream: memoryStream);
|
||||
_helper ??= await JS.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Admin.Blazor.Core/js/downloadFileFromStream.js");
|
||||
await _helper.InvokeVoidAsync("downloadFileFromStream", $"变量导出{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx", streamRef);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDownExport = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void LogOut(TouchSocket.Core.LogLevel logLevel, object source, string message, Exception exception)
|
||||
{
|
||||
|
@@ -428,13 +428,13 @@
|
||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.WriteAsync">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DownDeviceExportAsync(ThingsGateway.Application.CollectDevice)">
|
||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DeviceImportAsync(ThingsGateway.Application.CollectDevice)">
|
||||
<summary>
|
||||
导入设备
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DownDeviceExportAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable})">
|
||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DeviceVariableImportAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable})">
|
||||
<summary>
|
||||
导入变量
|
||||
</summary>
|
||||
@@ -447,6 +447,18 @@
|
||||
<param name="values"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DownDeviceExportAsync(ThingsGateway.Application.CollectDevice)">
|
||||
<summary>
|
||||
导出到excel
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DownDeviceVariableExportAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable},System.String)">
|
||||
<summary>
|
||||
导出到excel
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.LogOut(TouchSocket.Core.LogLevel,System.Object,System.String,System.Exception)">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
|
@@ -29,7 +29,6 @@ using System.Runtime.InteropServices;
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
using TouchSocket.Resources;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
@@ -347,6 +346,8 @@ public class TcpClientBaseEx : BaseSocket, ITcpClient
|
||||
{
|
||||
privateEasyLock.Release();
|
||||
}
|
||||
privateEasyLock.SafeDispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
|
@@ -15,6 +15,17 @@ namespace ThingsGateway.Foundation.Extension;
|
||||
/// <inheritdoc/>
|
||||
public static class OperResultExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 复制信息,包含第一个泛型类
|
||||
/// </summary>
|
||||
public static OperResult<T1> Copy<T1, T2>(this OperResult<T1, T2> result)
|
||||
{
|
||||
OperResult<T1> result1 = new(result.ResultCode, result.Message)
|
||||
{
|
||||
Content = result.Content1
|
||||
};
|
||||
return result1;
|
||||
}
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
|
@@ -1313,6 +1313,11 @@
|
||||
<member name="T:ThingsGateway.Foundation.Extension.OperResultExtensions">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Foundation.Extension.OperResultExtensions.Copy``2(ThingsGateway.Foundation.OperResult{``0,``1})">
|
||||
<summary>
|
||||
复制信息,包含第一个泛型类
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Foundation.Extension.OperResultExtensions.Copy``1(ThingsGateway.Foundation.OperResult)">
|
||||
<summary>
|
||||
复制信息,不包含泛型类
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
<Version>2.0.6.0</Version>
|
||||
<Version>2.0.7.0</Version>
|
||||
<Authors>Diego</Authors>
|
||||
<Product>ThingsGateway</Product>
|
||||
<Copyright>© 2023-present Diego</Copyright>
|
||||
|
@@ -16,290 +16,294 @@ using ThingsGateway.Foundation.Extension.Bool;
|
||||
using ThingsGateway.Foundation.Extension.Byte;
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||
|
||||
internal class ModbusHelper
|
||||
{
|
||||
internal class ModbusHelper
|
||||
/// <summary>
|
||||
/// 添加Crc16
|
||||
/// </summary>
|
||||
internal static byte[] AddCrc(byte[] command)
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加Crc16
|
||||
/// </summary>
|
||||
internal static byte[] AddCrc(byte[] command)
|
||||
{
|
||||
return EasyCRC16.CRC16(command);
|
||||
}
|
||||
return EasyCRC16.CRC16(command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加ModbusTcp报文头
|
||||
/// </summary>
|
||||
internal static byte[] AddModbusTcpHead(byte[] modbus, ushort id)
|
||||
{
|
||||
byte[] tcp = new byte[modbus.Length + 6];
|
||||
tcp[0] = BitConverter.GetBytes(id)[1];
|
||||
tcp[1] = BitConverter.GetBytes(id)[0];
|
||||
tcp[4] = BitConverter.GetBytes(modbus.Length)[1];
|
||||
tcp[5] = BitConverter.GetBytes(modbus.Length)[0];
|
||||
modbus.CopyTo(tcp, 6);
|
||||
return tcp;
|
||||
}
|
||||
/// <summary>
|
||||
/// 添加ModbusTcp报文头
|
||||
/// </summary>
|
||||
internal static byte[] AddModbusTcpHead(byte[] modbus, ushort id)
|
||||
{
|
||||
byte[] tcp = new byte[modbus.Length + 6];
|
||||
tcp[0] = BitConverter.GetBytes(id)[1];
|
||||
tcp[1] = BitConverter.GetBytes(id)[0];
|
||||
tcp[4] = BitConverter.GetBytes(modbus.Length)[1];
|
||||
tcp[5] = BitConverter.GetBytes(modbus.Length)[0];
|
||||
modbus.CopyTo(tcp, 6);
|
||||
return tcp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// modbus地址格式说明
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal static string GetAddressDescription()
|
||||
/// <summary>
|
||||
/// modbus地址格式说明
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal static string GetAddressDescription()
|
||||
{
|
||||
StringBuilder stringBuilder = new();
|
||||
stringBuilder.AppendLine("Modbus寄存器");
|
||||
stringBuilder.AppendLine("线圈寄存器使用从 00001 开始的地址编号。");
|
||||
stringBuilder.AppendLine("离散输入寄存器使用从 10001 开始的地址编号。");
|
||||
stringBuilder.AppendLine("输入寄存器使用从 30001 开始的地址编号。");
|
||||
stringBuilder.AppendLine("保持寄存器使用从 40001 开始的地址编号。");
|
||||
stringBuilder.AppendLine("举例:");
|
||||
stringBuilder.AppendLine("40001=>保持寄存器第一个寄存器");
|
||||
stringBuilder.AppendLine("额外格式:");
|
||||
stringBuilder.AppendLine("设备站号 ,比如40001;s=2; ,代表设备地址为2的保持寄存器第一个寄存器");
|
||||
stringBuilder.AppendLine("写入功能码 ,比如40001;w=16; ,代表保持寄存器第一个寄存器,写入值时采用0x10功能码,而不是默认的0x06功能码");
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
/// <summary>
|
||||
/// 通过错误码来获取到对应的文本消息
|
||||
/// </summary>
|
||||
internal static string GetDescriptionByErrorCode(byte code)
|
||||
{
|
||||
return code switch
|
||||
{
|
||||
StringBuilder stringBuilder = new();
|
||||
stringBuilder.AppendLine("Modbus寄存器");
|
||||
stringBuilder.AppendLine("线圈寄存器使用从 00001 开始的地址编号。");
|
||||
stringBuilder.AppendLine("离散输入寄存器使用从 10001 开始的地址编号。");
|
||||
stringBuilder.AppendLine("输入寄存器使用从 30001 开始的地址编号。");
|
||||
stringBuilder.AppendLine("保持寄存器使用从 40001 开始的地址编号。");
|
||||
stringBuilder.AppendLine("举例:");
|
||||
stringBuilder.AppendLine("40001=>保持寄存器第一个寄存器");
|
||||
stringBuilder.AppendLine("额外格式:");
|
||||
stringBuilder.AppendLine("设备站号 ,比如40001;s=2; ,代表设备地址为2的保持寄存器第一个寄存器");
|
||||
stringBuilder.AppendLine("写入功能码 ,比如40001;w=16; ,代表保持寄存器第一个寄存器,写入值时采用0x10功能码,而不是默认的0x06功能码");
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
/// <summary>
|
||||
/// 通过错误码来获取到对应的文本消息
|
||||
/// </summary>
|
||||
internal static string GetDescriptionByErrorCode(byte code)
|
||||
{
|
||||
return code switch
|
||||
{
|
||||
1 => "不支持的功能码",
|
||||
2 => "读取寄存器越界",
|
||||
3 => "读取长度超限",
|
||||
4 => "读写异常",
|
||||
_ => "未知错误",
|
||||
};
|
||||
}
|
||||
1 => "不支持的功能码",
|
||||
2 => "读取寄存器越界",
|
||||
3 => "读取长度超限",
|
||||
4 => "读写异常",
|
||||
_ => "未知错误",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取modbus数据区内容,返回数据需去除Crc和报文头,例如:01 03 02 00 01,发送数据需报文头
|
||||
/// </summary>
|
||||
/// <param name="send">发送数据</param>
|
||||
/// <param name="response">返回数据</param>
|
||||
/// <returns></returns>
|
||||
internal static OperResult<byte[]> GetModbusData(byte[] send, byte[] response)
|
||||
/// <summary>
|
||||
/// 获取modbus数据区内容,返回数据需去除Crc和报文头,例如:01 03 02 00 01,发送数据需报文头
|
||||
/// </summary>
|
||||
/// <param name="send">发送数据</param>
|
||||
/// <param name="response">返回数据</param>
|
||||
/// <returns></returns>
|
||||
internal static OperResult<byte[], FilterResult> GetModbusData(byte[] send, byte[] response)
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
if (response[1] >= 0x80)//错误码
|
||||
return new OperResult<byte[]>(GetDescriptionByErrorCode(response[2]));
|
||||
if (send.Length == 0)
|
||||
{
|
||||
var result = OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3));
|
||||
result.Message = "接收数据正确,但主机并没有主动请求数据";
|
||||
result.ResultCode = ResultCode.Canceled;
|
||||
return result;
|
||||
}
|
||||
if (send[0] != response[0])
|
||||
return new OperResult<byte[]>(string.Format("站号不一致", send[0], response[0]));
|
||||
if (send[1] != response[1])
|
||||
return new OperResult<byte[]>(response) { Message = "功能码不一致" };
|
||||
if (response.Length > 3)
|
||||
return OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3));
|
||||
else
|
||||
return new OperResult<byte[]>(response) { Message = "数据长度为0" };
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex.Message);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 去除Crc,返回modbus数据区
|
||||
/// </summary>
|
||||
/// <param name="send"></param>
|
||||
/// <param name="response"></param>
|
||||
/// <param name="crcCheck"></param>
|
||||
/// <returns></returns>
|
||||
internal static OperResult<byte[]> GetModbusRtuData(byte[] send, byte[] response, bool crcCheck = true)
|
||||
{
|
||||
if (response.Length < 5)
|
||||
return new OperResult<byte[]>("数据长度不足" + response.ToHexString());
|
||||
if (crcCheck && !EasyCRC16.CheckCRC16(response))
|
||||
return new OperResult<byte[]>("Crc校验失败" + DataTransUtil.ByteToHexString(response, ' '));
|
||||
return GetModbusData(send, response.RemoveLast(2));
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取读取报文
|
||||
/// </summary>
|
||||
internal static OperResult<byte[]> GetReadModbusCommand(string address, int length, byte station)
|
||||
{
|
||||
try
|
||||
{
|
||||
ModbusAddress mAddress = new(address, station);
|
||||
return GetReadModbusCommand(mAddress, length);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex.Message);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取写入布尔量报文,根据地址识别功能码
|
||||
/// </summary>
|
||||
internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool[] values, byte station)
|
||||
{
|
||||
try
|
||||
{
|
||||
ModbusAddress mAddress = new(address, station);
|
||||
//功能码或实际长度
|
||||
if (values?.Length > 1 || mAddress.WriteFunction == 15)
|
||||
return GetWriteBoolModbusCommand(mAddress, values, values.Length);
|
||||
else
|
||||
return GetWriteBoolModbusCommand(address, values[0], station);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex.Message);
|
||||
}
|
||||
}
|
||||
if (response.Length < 3)
|
||||
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||
|
||||
/// <summary>
|
||||
/// 获取写入字报文,根据地址识别功能码
|
||||
/// </summary>
|
||||
internal static OperResult<byte[]> GetWriteModbusCommand(string address, byte[] value, byte station)
|
||||
{
|
||||
try
|
||||
{
|
||||
ModbusAddress mAddress = new(address, station);
|
||||
//功能码或实际长度
|
||||
if (value?.Length > 2 || mAddress.WriteFunction == 16)
|
||||
return GetWriteModbusCommand(mAddress, value);
|
||||
else
|
||||
return GetWriteOneModbusCommand(mAddress, value);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
if (response[1] >= 0x80)//错误码
|
||||
return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success };
|
||||
if ((response.Length < response[2] + 3))
|
||||
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||
|
||||
|
||||
if (send.Length == 0)
|
||||
{
|
||||
return new OperResult<byte[]>(ex.Message);
|
||||
var result = OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success);
|
||||
result.Message = "接收数据正确,但主机并没有主动请求数据";
|
||||
return result;
|
||||
}
|
||||
if (send[0] != response[0])
|
||||
return new OperResult<byte[], FilterResult>(string.Format("站号不一致", send[0], response[0])) { Content2 = FilterResult.Success };
|
||||
if (send[1] != response[1])
|
||||
return new OperResult<byte[], FilterResult>() { Message = "功能码不一致", Content2 = FilterResult.Success };
|
||||
return OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success);
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取读取报文
|
||||
/// </summary>
|
||||
private static OperResult<byte[]> GetReadModbusCommand(ModbusAddress mAddress, int length)
|
||||
catch (Exception ex)
|
||||
{
|
||||
byte[] array = new byte[6]
|
||||
{
|
||||
(byte) mAddress.Station,
|
||||
(byte) mAddress.ReadFunction,
|
||||
BitConverter.GetBytes(mAddress.AddressStart)[1],
|
||||
BitConverter.GetBytes(mAddress.AddressStart)[0],
|
||||
BitConverter.GetBytes(length)[1],
|
||||
BitConverter.GetBytes(length)[0]
|
||||
};
|
||||
|
||||
return OperResult.CreateSuccessResult(array);
|
||||
return new OperResult<byte[], FilterResult>(ex.Message) { Content2 = FilterResult.Success };
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 去除Crc,返回modbus数据区
|
||||
/// </summary>
|
||||
/// <param name="send"></param>
|
||||
/// <param name="response"></param>
|
||||
/// <param name="crcCheck"></param>
|
||||
/// <returns></returns>
|
||||
internal static OperResult<byte[], FilterResult> GetModbusRtuData(byte[] send, byte[] response, bool crcCheck = true)
|
||||
{
|
||||
if (response.Length < 3)
|
||||
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||
|
||||
/// <summary>
|
||||
/// 获取05写入布尔量报文
|
||||
/// </summary>
|
||||
private static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station)
|
||||
if (crcCheck && !EasyCRC16.CheckCRC16(response))
|
||||
return new OperResult<byte[], FilterResult>("Crc校验失败" + DataTransUtil.ByteToHexString(response, ' ')) { Content2 = FilterResult.Success };
|
||||
return GetModbusData(send, response.RemoveLast(2));
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取读取报文
|
||||
/// </summary>
|
||||
internal static OperResult<byte[]> GetReadModbusCommand(string address, int length, byte station)
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
if (address.IndexOf('.') <= 0)
|
||||
{
|
||||
ModbusAddress mAddress = new(address, station);
|
||||
return GetWriteBoolModbusCommand(mAddress, value);
|
||||
}
|
||||
return new("不支持写入字寄存器的某一位");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex.Message);
|
||||
}
|
||||
ModbusAddress mAddress = new(address, station);
|
||||
return GetReadModbusCommand(mAddress, length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取05写入布尔量报文
|
||||
/// </summary>
|
||||
private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool value)
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex.Message);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取写入布尔量报文,根据地址识别功能码
|
||||
/// </summary>
|
||||
internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool[] values, byte station)
|
||||
{
|
||||
try
|
||||
{
|
||||
ModbusAddress mAddress = new(address, station);
|
||||
//功能码或实际长度
|
||||
if (values?.Length > 1 || mAddress.WriteFunction == 15)
|
||||
return GetWriteBoolModbusCommand(mAddress, values, values.Length);
|
||||
else
|
||||
return GetWriteBoolModbusCommand(address, values[0], station);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取写入字报文,根据地址识别功能码
|
||||
/// </summary>
|
||||
internal static OperResult<byte[]> GetWriteModbusCommand(string address, byte[] value, byte station)
|
||||
{
|
||||
try
|
||||
{
|
||||
ModbusAddress mAddress = new(address, station);
|
||||
//功能码或实际长度
|
||||
if (value?.Length > 2 || mAddress.WriteFunction == 16)
|
||||
return GetWriteModbusCommand(mAddress, value);
|
||||
else
|
||||
return GetWriteOneModbusCommand(mAddress, value);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex.Message);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取读取报文
|
||||
/// </summary>
|
||||
private static OperResult<byte[]> GetReadModbusCommand(ModbusAddress mAddress, int length)
|
||||
{
|
||||
byte[] array = new byte[6]
|
||||
{
|
||||
byte[] array = new byte[6]
|
||||
{
|
||||
(byte) mAddress.Station,
|
||||
(byte)5,
|
||||
(byte) mAddress.ReadFunction,
|
||||
BitConverter.GetBytes(mAddress.AddressStart)[1],
|
||||
BitConverter.GetBytes(mAddress.AddressStart)[0],
|
||||
0,
|
||||
0
|
||||
};
|
||||
if (value)
|
||||
{
|
||||
array[4] = 0xFF;
|
||||
array[5] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
array[4] = 0;
|
||||
array[5] = 0;
|
||||
}
|
||||
return OperResult.CreateSuccessResult(array);
|
||||
}
|
||||
BitConverter.GetBytes(length)[1],
|
||||
BitConverter.GetBytes(length)[0]
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 获取15写入布尔量报文
|
||||
/// </summary>
|
||||
private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool[] values, int length)
|
||||
return OperResult.CreateSuccessResult(array);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取05写入布尔量报文
|
||||
/// </summary>
|
||||
private static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station)
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
if (address.IndexOf('.') <= 0)
|
||||
{
|
||||
byte[] numArray1 = values.BoolArrayToByte();
|
||||
byte[] numArray2 = new byte[7 + numArray1.Length];
|
||||
numArray2[0] = (byte)mAddress.Station;
|
||||
numArray2[1] = (byte)15;
|
||||
numArray2[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
|
||||
numArray2[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
|
||||
numArray2[4] = (byte)(length / 256);
|
||||
numArray2[5] = (byte)(length % 256);
|
||||
numArray2[6] = (byte)numArray1.Length;
|
||||
numArray1.CopyTo(numArray2, 7);
|
||||
return OperResult.CreateSuccessResult(numArray2);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex.Message);
|
||||
ModbusAddress mAddress = new(address, station);
|
||||
return GetWriteBoolModbusCommand(mAddress, value);
|
||||
}
|
||||
return new("不支持写入字寄存器的某一位");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取16写入字报文
|
||||
/// </summary>
|
||||
private static OperResult<byte[]> GetWriteModbusCommand(ModbusAddress mAddress, byte[] values)
|
||||
catch (Exception ex)
|
||||
{
|
||||
byte[] numArray = new byte[7 + values.Length];
|
||||
numArray[0] = (byte)mAddress.Station;
|
||||
numArray[1] = (byte)16;
|
||||
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
|
||||
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
|
||||
numArray[4] = (byte)(values.Length / 2 / 256);
|
||||
numArray[5] = (byte)(values.Length / 2 % 256);
|
||||
numArray[6] = (byte)values.Length;
|
||||
values.CopyTo(numArray, 7);
|
||||
return OperResult.CreateSuccessResult(numArray);
|
||||
|
||||
return new OperResult<byte[]>(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取6写入字报文
|
||||
/// </summary>
|
||||
private static OperResult<byte[]> GetWriteOneModbusCommand(ModbusAddress mAddress, byte[] values)
|
||||
/// <summary>
|
||||
/// 获取05写入布尔量报文
|
||||
/// </summary>
|
||||
private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool value)
|
||||
{
|
||||
byte[] array = new byte[6]
|
||||
{
|
||||
byte[] numArray = new byte[4 + values.Length];
|
||||
numArray[0] = (byte)mAddress.Station;
|
||||
numArray[1] = (byte)6;
|
||||
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
|
||||
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
|
||||
values.CopyTo(numArray, 4);
|
||||
return OperResult.CreateSuccessResult(numArray);
|
||||
(byte) mAddress.Station,
|
||||
(byte)5,
|
||||
BitConverter.GetBytes(mAddress.AddressStart)[1],
|
||||
BitConverter.GetBytes(mAddress.AddressStart)[0],
|
||||
0,
|
||||
0
|
||||
};
|
||||
if (value)
|
||||
{
|
||||
array[4] = 0xFF;
|
||||
array[5] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
array[4] = 0;
|
||||
array[5] = 0;
|
||||
}
|
||||
return OperResult.CreateSuccessResult(array);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取15写入布尔量报文
|
||||
/// </summary>
|
||||
private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool[] values, int length)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] numArray1 = values.BoolArrayToByte();
|
||||
byte[] numArray2 = new byte[7 + numArray1.Length];
|
||||
numArray2[0] = (byte)mAddress.Station;
|
||||
numArray2[1] = (byte)15;
|
||||
numArray2[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
|
||||
numArray2[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
|
||||
numArray2[4] = (byte)(length / 256);
|
||||
numArray2[5] = (byte)(length % 256);
|
||||
numArray2[6] = (byte)numArray1.Length;
|
||||
numArray1.CopyTo(numArray2, 7);
|
||||
return OperResult.CreateSuccessResult(numArray2);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取16写入字报文
|
||||
/// </summary>
|
||||
private static OperResult<byte[]> GetWriteModbusCommand(ModbusAddress mAddress, byte[] values)
|
||||
{
|
||||
byte[] numArray = new byte[7 + values.Length];
|
||||
numArray[0] = (byte)mAddress.Station;
|
||||
numArray[1] = (byte)16;
|
||||
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
|
||||
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
|
||||
numArray[4] = (byte)(values.Length / 2 / 256);
|
||||
numArray[5] = (byte)(values.Length / 2 % 256);
|
||||
numArray[6] = (byte)values.Length;
|
||||
values.CopyTo(numArray, 7);
|
||||
return OperResult.CreateSuccessResult(numArray);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取6写入字报文
|
||||
/// </summary>
|
||||
private static OperResult<byte[]> GetWriteOneModbusCommand(ModbusAddress mAddress, byte[] values)
|
||||
{
|
||||
byte[] numArray = new byte[4 + values.Length];
|
||||
numArray[0] = (byte)mAddress.Station;
|
||||
numArray[1] = (byte)6;
|
||||
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
|
||||
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
|
||||
values.CopyTo(numArray, 4);
|
||||
return OperResult.CreateSuccessResult(numArray);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -48,47 +48,14 @@ public class ModbusRtuDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<M
|
||||
{
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
return FilterResult.Success;
|
||||
request.Content = result.Content1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (response.Length <= 1)
|
||||
{
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
//如果长度不足,返回缓存
|
||||
return FilterResult.Cache;
|
||||
}
|
||||
if (!(response[1] <= 0x10))
|
||||
{
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
//功能码不对,返回放弃
|
||||
return FilterResult.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((response.Length > response[2] + 4))
|
||||
{
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
//如果长度已经超了,说明这段报文已经不能继续解析了,直接返回放弃
|
||||
return FilterResult.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
//否则返回缓存
|
||||
return FilterResult.Cache;
|
||||
}
|
||||
}
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
}
|
||||
return result.Content2;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -10,6 +10,8 @@
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using ThingsGateway.Foundation.Extension;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||
|
||||
/// <summary>
|
||||
@@ -38,6 +40,7 @@ public class ModbusRtuOverUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAd
|
||||
protected override OperResult<byte[]> UnpackResponse(
|
||||
byte[] send, byte[] response)
|
||||
{
|
||||
return ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
|
||||
var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
|
||||
return result.Copy();
|
||||
}
|
||||
}
|
||||
|
@@ -10,13 +10,12 @@
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using ThingsGateway.Foundation.Extension;
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// ModbusTcpDataHandleAdapter
|
||||
/// </summary>
|
||||
public class ModbusTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpMessage>
|
||||
{
|
||||
@@ -58,48 +57,14 @@ public class ModbusTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<M
|
||||
{
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
return FilterResult.Success;
|
||||
request.Content = result.Content1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//如果返回错误,具体分析
|
||||
var op = result.Copy<byte[], FilterResult>();
|
||||
if (response.Length == 9)
|
||||
{
|
||||
if (response[7] >= 0x80)//错误码
|
||||
{
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
return FilterResult.Success;
|
||||
}
|
||||
}
|
||||
if (response.Length < 10)
|
||||
{
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
return FilterResult.Cache;
|
||||
//如果长度不足,返回缓存
|
||||
}
|
||||
if ((response.Length > response[8] + 9))
|
||||
{
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
return FilterResult.Success;
|
||||
//如果长度已经超了,说明这段报文已经不能继续解析了,直接返回放弃
|
||||
}
|
||||
else
|
||||
{
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
return FilterResult.Cache;
|
||||
//否则返回缓存
|
||||
}
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
}
|
||||
return result.Content2;
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using ThingsGateway.Foundation.Extension;
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||
@@ -50,6 +51,7 @@ public class ModbusUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<M
|
||||
/// <inheritdoc/>
|
||||
protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response)
|
||||
{
|
||||
return ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6));
|
||||
var result = ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6));
|
||||
return result.Copy();
|
||||
}
|
||||
}
|
||||
|
@@ -458,7 +458,7 @@
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
ModbusTcpDataHandleAdapter
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.IsCheckMessageId">
|
||||
|
@@ -349,6 +349,7 @@ public class OPCDAClient : DisposableObject
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
PrivateDisconnect();
|
||||
checkLock.SafeDispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
|
@@ -109,7 +109,6 @@ public static class JsonUtils
|
||||
{
|
||||
if (ValueRank == ValueRanks.Scalar)
|
||||
{
|
||||
Type type = TypeInfo.GetSystemType(builtInType, ValueRank);
|
||||
switch (builtInType)
|
||||
{
|
||||
case BuiltInType.Null: { var variant = decoder.ReadVariant(fieldName); return variant.Value; }
|
||||
@@ -124,7 +123,7 @@ public static class JsonUtils
|
||||
case BuiltInType.UInt64: { return decoder.ReadUInt64(fieldName); }
|
||||
case BuiltInType.Float: { return decoder.ReadFloat(fieldName); }
|
||||
case BuiltInType.Double: { return decoder.ReadDouble(fieldName); }
|
||||
case BuiltInType.String: { return decoder.ReadString(fieldName); }
|
||||
case BuiltInType.String: { return decoder.ReadField(fieldName, out var token) ? token?.ToString() : null; }
|
||||
case BuiltInType.DateTime: { return decoder.ReadDateTime(fieldName); }
|
||||
case BuiltInType.Guid: { return decoder.ReadGuid(fieldName); }
|
||||
case BuiltInType.ByteString: { return decoder.ReadByteString(fieldName); }
|
||||
@@ -138,7 +137,8 @@ public static class JsonUtils
|
||||
case BuiltInType.DataValue: { return decoder.ReadDataValue(fieldName); }
|
||||
case BuiltInType.Enumeration:
|
||||
{
|
||||
return type.IsEnum ? decoder.ReadEnumerated(fieldName, type) : (object)decoder.ReadInt32(fieldName);
|
||||
Type type = TypeInfo.GetSystemType(builtInType, ValueRank);
|
||||
return type.IsEnum ? decoder.ReadEnumerated(fieldName, type) : decoder.ReadInt32(fieldName);
|
||||
}
|
||||
case BuiltInType.DiagnosticInfo: { return decoder.ReadDiagnosticInfo(fieldName); }
|
||||
case BuiltInType.Variant: { return decoder.ReadVariant(fieldName); }
|
||||
@@ -300,7 +300,7 @@ public static class JsonUtils
|
||||
case BuiltInType.DataValue: { encoder.WriteDataValue(fieldName, (DataValue)value); return; }
|
||||
case BuiltInType.Enumeration:
|
||||
{
|
||||
if (value.GetType().IsEnum)
|
||||
if (value?.GetType().IsEnum == true)
|
||||
{
|
||||
encoder.WriteEnumerated(fieldName, (Enum)value);
|
||||
}
|
||||
|
@@ -230,7 +230,7 @@ public class OPCUAClient : DisposableObject
|
||||
StartNodeId = variableNode.NodeId,
|
||||
AttributeId = Attributes.Value,
|
||||
DisplayName = items[i],
|
||||
Filter = new DataChangeFilter() { DeadbandValue = OPCNode.DeadBand, DeadbandType = (int)DeadbandType.Absolute, Trigger = DataChangeTrigger.StatusValue },
|
||||
Filter = OPCNode.DeadBand == 0 ? null : new DataChangeFilter() { DeadbandValue = OPCNode.DeadBand, DeadbandType = (int)DeadbandType.Absolute, Trigger = DataChangeTrigger.StatusValue },
|
||||
SamplingInterval = OPCNode?.UpdateRate ?? 1000,
|
||||
};
|
||||
await typeSystem.LoadType(variableNode.DataType, true, true);
|
||||
@@ -249,7 +249,7 @@ public class OPCUAClient : DisposableObject
|
||||
m_subscription.Create();
|
||||
foreach (var item in m_subscription.MonitoredItems.Where(a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode)))
|
||||
{
|
||||
item.Filter = new DataChangeFilter() { DeadbandValue = OPCNode.DeadBand, DeadbandType = (int)DeadbandType.None, Trigger = DataChangeTrigger.StatusValue };
|
||||
item.Filter = OPCNode.DeadBand == 0 ? null : new DataChangeFilter() { DeadbandValue = OPCNode.DeadBand, DeadbandType = (int)DeadbandType.None, Trigger = DataChangeTrigger.StatusValue };
|
||||
}
|
||||
m_subscription.ApplyChanges();
|
||||
|
||||
@@ -320,12 +320,21 @@ public class OPCUAClient : DisposableObject
|
||||
VariableNode variableNode = (VariableNode)ReadNode(monitoreditem.StartNodeId.ToString(), false);
|
||||
foreach (var value in monitoreditem.DequeueValues())
|
||||
{
|
||||
var data = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value);
|
||||
if (data == null && value.Value != null)
|
||||
if (value.Value != null)
|
||||
{
|
||||
Log.Warning($"{monitoreditem.StartNodeId}转换出错,原始值String为{value.Value}");
|
||||
var data = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value);
|
||||
if (data == null && value.Value != null)
|
||||
{
|
||||
Log.Warning($"{monitoreditem.StartNodeId}转换出错,原始值String为{value.Value}");
|
||||
}
|
||||
DataChangedHandler?.Invoke((variableNode, value, data));
|
||||
}
|
||||
DataChangedHandler?.Invoke((variableNode, value, data));
|
||||
else
|
||||
{
|
||||
var data = JValue.CreateNull();
|
||||
DataChangedHandler?.Invoke((variableNode, value, data));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -855,6 +864,7 @@ public class OPCUAClient : DisposableObject
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
Disconnect();
|
||||
checkLock.SafeDispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
|
@@ -124,7 +124,7 @@ internal partial class SiemensHelper
|
||||
}
|
||||
|
||||
byte err = content[21];
|
||||
if (err == byte.MaxValue)
|
||||
if (err != byte.MaxValue)
|
||||
{
|
||||
return new OperResult<byte[]>((int)content[21] + GetCpuError(content[21]));
|
||||
}
|
||||
@@ -301,7 +301,7 @@ internal partial class SiemensHelper
|
||||
{
|
||||
return new OperResult<string>("在PLC中不是字符串类型");
|
||||
}
|
||||
var result2 = await plc.ReadAsync(address, 2 + result1.Content[1]);
|
||||
var result2 = await plc.ReadAsync(address, 2 + result1.Content[1], token);
|
||||
if (!result2.IsSuccess)
|
||||
{
|
||||
return result2.Copy<string>();
|
||||
|
@@ -54,32 +54,9 @@ public class SiemensS7PLCDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapte
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
return FilterResult.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
//如果返回错误,具体分析
|
||||
if (response.Length < 21)
|
||||
{
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
//如果长度不足,返回缓存
|
||||
return FilterResult.Cache;
|
||||
}
|
||||
else
|
||||
{
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
//如果长度已经超了,说明这段报文已经不能继续解析了,直接返回放弃
|
||||
return FilterResult.Success;
|
||||
}
|
||||
}
|
||||
request.ResultCode = result.ResultCode;
|
||||
request.Message = result.Message;
|
||||
request.Content = result.Content;
|
||||
return FilterResult.Success;
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ internal static class ModbusHelper
|
||||
|
||||
IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter);
|
||||
item.ThingsGatewayBitConverter = transformParameter;
|
||||
//item.VariableAddress = address;
|
||||
item.VariableAddress = address;
|
||||
item.Index = device.GetBitOffset(item.VariableAddress);
|
||||
}
|
||||
|
||||
|
@@ -49,7 +49,7 @@ public class IotSharpClient : UpLoadBase
|
||||
private const string WriteMethod = "WRITE";
|
||||
|
||||
private readonly IotSharpClientProperty driverPropertys = new();
|
||||
private readonly EasyLock lockobj = new();
|
||||
private readonly EasyLock easyLock = new();
|
||||
private readonly IotSharpClientVariableProperty variablePropertys = new();
|
||||
private ConcurrentQueue<DeviceData> _collectDeviceRunTimes = new();
|
||||
private ConcurrentQueue<VariableData> _collectVariableRunTimes = new();
|
||||
@@ -204,6 +204,7 @@ public class IotSharpClient : UpLoadBase
|
||||
{
|
||||
try
|
||||
{
|
||||
easyLock.SafeDispose();
|
||||
_globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange);
|
||||
|
||||
_globalDeviceData?.CollectDevices?.ForEach(a =>
|
||||
@@ -472,7 +473,7 @@ GetPropertyValue(tag, nameof(variablePropertys.VariableRpcEnable)).ToBoolean()
|
||||
return OperResult.CreateSuccessResult();
|
||||
try
|
||||
{
|
||||
await lockobj.WaitAsync();
|
||||
await easyLock.WaitAsync();
|
||||
if (_mqttClient?.IsConnected == true)
|
||||
return OperResult.CreateSuccessResult();
|
||||
using var timeoutToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(driverPropertys.ConnectTimeOut));
|
||||
@@ -497,7 +498,7 @@ GetPropertyValue(tag, nameof(variablePropertys.VariableRpcEnable)).ToBoolean()
|
||||
}
|
||||
finally
|
||||
{
|
||||
lockobj.Release();
|
||||
easyLock.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -43,7 +43,7 @@ namespace ThingsGateway.Mqtt;
|
||||
public class MqttClient : UpLoadBase
|
||||
{
|
||||
private readonly MqttClientProperty driverPropertys = new();
|
||||
private readonly EasyLock lockobj = new();
|
||||
private readonly EasyLock easyLock = new();
|
||||
private readonly MqttClientVariableProperty variablePropertys = new();
|
||||
private List<CollectDeviceRunTime> _collectDevice;
|
||||
private ConcurrentQueue<DeviceData> _collectDeviceRunTimes = new();
|
||||
@@ -285,6 +285,7 @@ public class MqttClient : UpLoadBase
|
||||
{
|
||||
try
|
||||
{
|
||||
easyLock.SafeDispose();
|
||||
_globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange);
|
||||
|
||||
_globalDeviceData?.CollectDevices?.ForEach(a =>
|
||||
@@ -311,7 +312,7 @@ public class MqttClient : UpLoadBase
|
||||
{
|
||||
var mqttFactory = new MqttFactory(new PrivateLogger(LogMessage));
|
||||
_mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
|
||||
.WithClientId(driverPropertys.ConnectId)
|
||||
.WithClientId(driverPropertys.ConnectId)
|
||||
.WithCredentials(driverPropertys.UserName, driverPropertys.Password)//账密
|
||||
.WithTcpServer(driverPropertys.IP, driverPropertys.Port)//服务器
|
||||
.WithCleanSession(true)
|
||||
@@ -520,7 +521,7 @@ public class MqttClient : UpLoadBase
|
||||
return OperResult.CreateSuccessResult();
|
||||
try
|
||||
{
|
||||
await lockobj.WaitAsync();
|
||||
await easyLock.WaitAsync();
|
||||
if (_mqttClient?.IsConnected == true)
|
||||
return OperResult.CreateSuccessResult();
|
||||
using var timeoutToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(driverPropertys.ConnectTimeOut));
|
||||
@@ -548,7 +549,7 @@ public class MqttClient : UpLoadBase
|
||||
}
|
||||
finally
|
||||
{
|
||||
lockobj.Release();
|
||||
easyLock.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -60,6 +60,25 @@
|
||||
<PModal @bind-Value="IsShowImportVariableList" OnCancel="() => IsShowImportVariableList = false"
|
||||
Title=导入OPC变量 Height=@("700") Persistent
|
||||
MaxWidth="1500" OnSave="DownDeviceExport">
|
||||
<SaveContent Context="save">
|
||||
<MMenu OffsetY Context="menu">
|
||||
<ActivatorContent>
|
||||
<MButton @attributes="@menu.Attrs" Color="primary"
|
||||
Class="my-1 mx-2 ">
|
||||
导出
|
||||
<AppChevronDown></AppChevronDown>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<MList>
|
||||
<MListItem OnClick="()=>DownDeviceExport()" Disabled="save.Loading" Loading="save.Loading">导出到excel</MListItem>
|
||||
<MListItem OnClick="()=>DeviceImport()" Disabled="save.Loading" Loading="save.Loading">导入到系统</MListItem>
|
||||
</MList>
|
||||
|
||||
</ChildContent>
|
||||
</MMenu>
|
||||
|
||||
</SaveContent>
|
||||
<ChildContent>
|
||||
@if (IsShowImportVariableList)
|
||||
{
|
||||
@@ -67,11 +86,5 @@
|
||||
}
|
||||
</ChildContent>
|
||||
|
||||
|
||||
<SaveContent Context="save">
|
||||
<MButton Text Color="primary" OnClick="save.Click" Disabled="defalutDebugDriverPage.isDownExport" Loading="defalutDebugDriverPage.isDownExport">
|
||||
<MLabel>导入</MLabel>
|
||||
</MButton>
|
||||
</SaveContent>
|
||||
</PModal>
|
||||
|
||||
|
@@ -96,7 +96,22 @@ public partial class OPCDAClientDebugDriverPage : IDisposable
|
||||
return;
|
||||
}
|
||||
await defalutDebugDriverPage.DownDeviceExportAsync(data?.Item1);
|
||||
await defalutDebugDriverPage.DownDeviceExportAsync(data?.Item2);
|
||||
await defalutDebugDriverPage.DownDeviceVariableExportAsync(data?.Item2, data?.Item1.Name);
|
||||
await PopupService.EnqueueSnackbarAsync("<22>ɹ<EFBFBD>", AlertTypes.Success);
|
||||
}
|
||||
}
|
||||
private async Task DeviceImport()
|
||||
{
|
||||
var data = ImportVariable?.GetImportVariableList();
|
||||
if (data != null)
|
||||
{
|
||||
if (data?.Item2?.Count == 0)
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync("<22><EFBFBD><DEBF>ñ<EFBFBD><C3B1><EFBFBD>", AlertTypes.Warning);
|
||||
return;
|
||||
}
|
||||
await defalutDebugDriverPage.DeviceImportAsync(data?.Item1);
|
||||
await defalutDebugDriverPage.DeviceVariableImportAsync(data?.Item2);
|
||||
await PopupService.EnqueueSnackbarAsync("<22>ɹ<EFBFBD>", AlertTypes.Success);
|
||||
}
|
||||
}
|
||||
|
@@ -61,6 +61,25 @@
|
||||
<PModal @bind-Value="IsShowImportVariableList" OnCancel="() => IsShowImportVariableList = false"
|
||||
Title=导入OPC变量 Height=@("700") Persistent
|
||||
MaxWidth="1500" OnSave="DownDeviceExport">
|
||||
<SaveContent Context="save">
|
||||
|
||||
<MMenu OffsetY Context="menu">
|
||||
<ActivatorContent>
|
||||
<MButton @attributes="@menu.Attrs" Color="primary" Class="my-1 mx-2 ">
|
||||
导出
|
||||
<AppChevronDown></AppChevronDown>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<MList>
|
||||
<MListItem OnClick="()=>DownDeviceExport()" Disabled="save.Loading" Loading="save.Loading">导出到excel</MListItem>
|
||||
<MListItem OnClick="()=>DeviceImport()" Disabled="save.Loading" Loading="save.Loading">导入到系统</MListItem>
|
||||
</MList>
|
||||
|
||||
</ChildContent>
|
||||
</MMenu>
|
||||
|
||||
</SaveContent>
|
||||
<ChildContent>
|
||||
@if (IsShowImportVariableList)
|
||||
{
|
||||
@@ -69,11 +88,6 @@
|
||||
</ChildContent>
|
||||
|
||||
|
||||
<SaveContent Context="save">
|
||||
<MButton Text Color="primary" OnClick="save.Click" Disabled="defalutDebugDriverPage.isDownExport" Loading="defalutDebugDriverPage.isDownExport">
|
||||
<MLabel>导入</MLabel>
|
||||
</MButton>
|
||||
</SaveContent>
|
||||
</PModal>
|
||||
|
||||
|
||||
|
@@ -95,7 +95,20 @@ public partial class OPCUAClientDebugDriverPage
|
||||
return;
|
||||
}
|
||||
await defalutDebugDriverPage.DownDeviceExportAsync(data.Item1);
|
||||
await defalutDebugDriverPage.DownDeviceExportAsync(data.Item2);
|
||||
await defalutDebugDriverPage.DownDeviceVariableExportAsync(data.Item2, data.Item1.Name);
|
||||
await PopupService.EnqueueSnackbarAsync("<22>ɹ<EFBFBD>", AlertTypes.Success);
|
||||
}
|
||||
|
||||
private async Task DeviceImport()
|
||||
{
|
||||
var data = await ImportVariable?.GetImportVariableListAsync();
|
||||
if (data.Item2?.Count == 0)
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync("<22><EFBFBD><DEBF>ñ<EFBFBD><C3B1><EFBFBD>", AlertTypes.Warning);
|
||||
return;
|
||||
}
|
||||
await defalutDebugDriverPage.DeviceImportAsync(data.Item1);
|
||||
await defalutDebugDriverPage.DeviceVariableImportAsync(data.Item2);
|
||||
await PopupService.EnqueueSnackbarAsync("<22>ɹ<EFBFBD>", AlertTypes.Success);
|
||||
}
|
||||
|
||||
|
@@ -33,7 +33,7 @@ namespace ThingsGateway.Siemens
|
||||
|
||||
IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter);
|
||||
item.ThingsGatewayBitConverter = transformParameter;
|
||||
//item.VariableAddress = address;
|
||||
item.VariableAddress = address;//需要使用过滤后的地址
|
||||
|
||||
item.Index = siemensS7Net.GetBitOffset(item.VariableAddress);
|
||||
}
|
||||
@@ -54,6 +54,18 @@ namespace ThingsGateway.Siemens
|
||||
else if (it.DataTypeEnum.GetSystemType() == typeof(string))
|
||||
{
|
||||
lastLen = it.ThingsGatewayBitConverter.StringLength;
|
||||
if (siemensS7Net.CurrentPlc == SiemensEnum.S200Smart)
|
||||
{
|
||||
//字符串在S200Smart中,第一个字节不属于实际内容
|
||||
it.Index += 1;
|
||||
it.ThingsGatewayBitConverter.StringLength -= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//字符串在S7中,前两个字节不属于实际内容
|
||||
it.Index += 2;
|
||||
it.ThingsGatewayBitConverter.StringLength -= 2;
|
||||
}
|
||||
}
|
||||
else if (it.DataTypeEnum.GetSystemType() == typeof(object))
|
||||
{
|
||||
|
@@ -5,26 +5,29 @@
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Configuration\App.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Configuration\Database.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Configuration\JWT.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Configuration\Swagger.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Configuration\App.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Configuration\Database.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Configuration\JWT.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Configuration\Swagger.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!--Admin-->
|
||||
<ProjectReference Include="..\ThingsGateway.Admin.ApiController\ThingsGateway.Admin.ApiController.csproj" />
|
||||
<ProjectReference Include="..\ThingsGateway.Admin.Blazor\ThingsGateway.Admin.Blazor.csproj" />
|
||||
<ProjectReference Include="..\ThingsGateway.Admin.ApiController\ThingsGateway.Admin.ApiController.csproj" />
|
||||
<ProjectReference Include="..\ThingsGateway.Admin.Blazor\ThingsGateway.Admin.Blazor.csproj" />
|
||||
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(SolutionName)'!='ThingsGateway.Admin'">
|
||||
<!--采集网关-->
|
||||
<ProjectReference Include="..\ThingsGateway.ApiController\ThingsGateway.ApiController.csproj" />
|
||||
<ProjectReference Include="..\ThingsGateway.Blazor\ThingsGateway.Blazor.csproj" />
|
||||
|
Reference in New Issue
Block a user