Compare commits

...

12 Commits
2.0.6 ... 2.0.7

Author SHA1 Message Date
Kimdiego2098
877445bc0a 修复写入返回结果时的解析,0XFF为成功 2023-08-16 15:56:25 +08:00
Kimdiego2098
9a5b345bde 修复s7字符串打包读取 2023-08-16 15:47:37 +08:00
Kimdiego2098
fc9e8ea7b3 修复s7字符串读取 2023-08-16 15:35:15 +08:00
Kimdiego2098
32be6fcfc1 主动释放锁 2023-08-16 14:56:47 +08:00
Kimdiego2098
49847236c2 OPCUA死区不再固定传入 2023-08-16 14:21:19 +08:00
Kimdiego2098
d8424443e6 fix: 优化JsonUtils中的获取type语句顺序 2023-08-16 08:59:18 +08:00
Kimdiego2098
f3b571ec3f OPC系列增加导出excel/导入系统双选项 2023-08-15 17:38:40 +08:00
Kimdiego2098
99318bb5d7 OPCUA采集设备写入字符串,不再需要传入双引号 2023-08-15 14:15:39 +08:00
Kimdiego2098
1aa154c9aa 历史数据/报警 线程初始化时不再阻塞 2023-08-15 12:12:00 +08:00
Kimdiego2098
c65d8a445b 更改ItemGroup Condition条件 2023-08-14 17:36:59 +08:00
Kimdiego2098
80f4f85570 传播token;
更新admin解决方案
2023-08-14 16:43:30 +08:00
Kimdiego2098
5beee43a6b 优化适配器解析代码 2023-08-14 14:21:00 +08:00
37 changed files with 526 additions and 426 deletions

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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)
{

View File

@@ -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)">

View File

@@ -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();

View File

@@ -269,6 +269,7 @@ public class CollectDeviceCore
{
isInitSuccess = false;
GlobalDeviceData.CollectDevices.RemoveWhere(it => it.Id == Device.Id);
easyLock.SafeDispose();
}
}

View File

@@ -62,6 +62,7 @@ public class CollectDeviceThread : IAsyncDisposable
public async ValueTask DisposeAsync()
{
await StopThreadAsync();
easyLock.SafeDispose();
CollectDeviceCores.Clear();
}

View File

@@ -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>

View File

@@ -225,7 +225,13 @@ public class UploadDeviceCore
{
_logger?.LogError(ex, $"{Device.Name} 释放失败");
}
isInitSuccess = false;
finally
{
isInitSuccess = false;
easyLock.SafeDispose();
}
}
/// <summary>

View File

@@ -51,6 +51,7 @@ public class UploadDeviceThread : IAsyncDisposable
public async ValueTask DisposeAsync()
{
await StopThreadAsync();
easyLock.SafeDispose();
UploadDeviceCores.Clear();
}

View File

@@ -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)
{

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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>

View File

@@ -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>
复制信息,不包含泛型类

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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">

View File

@@ -349,6 +349,7 @@ public class OPCDAClient : DisposableObject
protected override void Dispose(bool disposing)
{
PrivateDisconnect();
checkLock.SafeDispose();
base.Dispose(disposing);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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>();

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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))
{

View File

@@ -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" />