diff --git a/src/Admin/ThingsGateway.Admin.Application/Entity/VerificatInfo.cs b/src/Admin/ThingsGateway.Admin.Application/Entity/VerificatInfo.cs index a52e3019b..a13bf9b7f 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Entity/VerificatInfo.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Entity/VerificatInfo.cs @@ -45,6 +45,7 @@ public class VerificatInfo : PrimaryIdEntity /// 登录IP /// [AutoGenerateColumn(Filterable = true, Sortable = true, Width = 200)] + [SugarColumn(IsNullable = true)] public string LoginIp { get; set; } /// @@ -78,5 +79,6 @@ public class VerificatInfo : PrimaryIdEntity /// 登录设备 /// [AutoGenerateColumn(Filterable = true, Sortable = true, Width = 100)] + [SugarColumn(IsNullable = true)] public string Device { get; set; } } diff --git a/src/Admin/ThingsGateway.Admin.Application/OAuth/AdminOAuthHandler.cs b/src/Admin/ThingsGateway.Admin.Application/OAuth/AdminOAuthHandler.cs index 313759033..9960b7a55 100644 --- a/src/Admin/ThingsGateway.Admin.Application/OAuth/AdminOAuthHandler.cs +++ b/src/Admin/ThingsGateway.Admin.Application/OAuth/AdminOAuthHandler.cs @@ -145,7 +145,7 @@ public class AdminOAuthHandler( var loginEvent = new LoginEvent { Ip = appService.RemoteIpAddress, - Device = appService.UserAgent?.Platform, + Device = appService.UserAgent?.Platform ?? "Unknown", Expire = expire, SysUser = sysUser, VerificatId = CommonUtils.GetSingleId() @@ -156,7 +156,7 @@ public class AdminOAuthHandler( //生成verificat信息 var verificatInfo = new VerificatInfo { - Device = loginEvent.Device, + Device = loginEvent.Device ?? "Unknown", Expire = loginEvent.Expire, VerificatTimeout = tokenTimeout, Id = loginEvent.VerificatId, diff --git a/src/Admin/ThingsGateway.Admin.Application/Services/Auth/AuthService.cs b/src/Admin/ThingsGateway.Admin.Application/Services/Auth/AuthService.cs index e5bc0fade..a5e2dde0f 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Services/Auth/AuthService.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Services/Auth/AuthService.cs @@ -235,7 +235,7 @@ public class AuthService : IAuthService var logingEvent = new LoginEvent { Ip = _appService.RemoteIpAddress, - Device = _appService.UserAgent?.Platform, + Device = _appService.UserAgent?.Platform ?? "Unknown", Expire = expire, SysUser = sysUser, VerificatId = verificatId @@ -344,7 +344,7 @@ public class AuthService : IAuthService //生成verificat信息 var verificatInfo = new VerificatInfo { - Device = loginEvent.Device, + Device = loginEvent.Device ?? "Unknown", Expire = loginEvent.Expire, VerificatTimeout = tokenTimeout, Id = loginEvent.VerificatId, diff --git a/src/Admin/ThingsGateway.Admin.Razor/Util/AdminResourceUtil.cs b/src/Admin/ThingsGateway.Admin.Razor/Util/AdminResourceUtil.cs index d2590947d..fc4b26597 100644 --- a/src/Admin/ThingsGateway.Admin.Razor/Util/AdminResourceUtil.cs +++ b/src/Admin/ThingsGateway.Admin.Razor/Util/AdminResourceUtil.cs @@ -51,7 +51,7 @@ public static class AdminResourceUtil Target = item.Target.ToString(), Items = BuildMenuTrees(items, item.Id).ToList() }; - if(menu.Url.IsNullOrEmpty()) + if (menu.Url.IsNullOrEmpty()) { menu.Match = Microsoft.AspNetCore.Components.Routing.NavLinkMatch.Prefix; } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index f26e859f4..3e5aa62a0 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,9 +1,9 @@ - 10.11.81 - 10.11.81 - 10.11.81 + 10.11.83 + 10.11.83 + 10.11.83 10.11.6 10.11.6 8.0.20 diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/Extension/ChannelOptionsExtensions.cs b/src/Foundation/ThingsGateway.Foundation/Channel/Extension/ChannelOptionsExtensions.cs index fb64af98a..6d7cc3cba 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/Extension/ChannelOptionsExtensions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/Extension/ChannelOptionsExtensions.cs @@ -8,8 +8,6 @@ // QQ群:605534569 //------------------------------------------------------------------------------ -using System.Buffers; - using ThingsGateway.Foundation.Extension.String; using TouchSocket.SerialPorts; @@ -101,6 +99,22 @@ public static class ChannelOptionsExtensions if (channelOptions.MaxClientCount > 0) config.SetMaxCount(channelOptions.MaxClientCount); + // config.SetTransportOption(new TransportOption() + // { + // MaxBufferSize = 1024, + // MinBufferSize = 512, + // SendPipeOptions = new System.IO.Pipelines.PipeOptions( + // minimumSegmentSize: 512, + // pauseWriterThreshold: 1024, + // resumeWriterThreshold: 512, + // useSynchronizationContext: false), + // ReceivePipeOptions = new System.IO.Pipelines.PipeOptions( + //minimumSegmentSize: 512, + // pauseWriterThreshold: 1024, + // resumeWriterThreshold: 512, + // useSynchronizationContext: false), + // }); + config.SetTransportOption(a => { a.MaxBufferSize = 1024; diff --git a/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs b/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs index 7e7f69a53..ef65e3a5d 100644 --- a/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs +++ b/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs @@ -533,8 +533,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice int timeout = 3000, CancellationToken cancellationToken = default) { - - var waitData = clientChannel.WaitHandlePool.GetWaitDataAsync(out var sign); command.Sign = sign; WaitLock? waitLock = null; @@ -554,19 +552,17 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice if (waitData.Status == WaitDataStatus.Success) return waitData.CompletedData; - bool timeoutStatus = false; - var reusableTimeout = _reusableTimeouts.Get(); try { - var cts = reusableTimeout.GetTokenSource(timeout, cancellationToken, Channel.ClosedToken); + await waitData.WaitAsync(cts.Token).ConfigureAwait(false); + } catch (OperationCanceledException) { - timeoutStatus = reusableTimeout.TimeoutStatus; - return timeoutStatus + return reusableTimeout.TimeoutStatus ? new MessageBase(new TimeoutException()) { ErrorMessage = $"Timeout, sign: {sign}" } : new MessageBase(new OperationCanceledException()); } @@ -577,7 +573,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice finally { reusableTimeout.Set(); - timeoutStatus = reusableTimeout.TimeoutStatus; _reusableTimeouts.Return(reusableTimeout); } @@ -587,7 +582,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice } else { - var operResult = waitData.Check(timeoutStatus); + var operResult = waitData.Check(reusableTimeout.TimeoutStatus); return new MessageBase(operResult) { ErrorMessage = $"{operResult.ErrorMessage}, sign: {sign}" }; } } diff --git a/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs b/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs index 3b25baf21..91d89b8f9 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs @@ -259,7 +259,15 @@ public static class GlobalData } return false; } - + public static bool IsRedundantEnable(long deviceId) + { + if (GlobalData.IdDevices.TryGetValue(deviceId, out var deviceRuntime)) + { + if (deviceRuntime.RedundantEnable && deviceRuntime.RedundantDeviceId != null) + return true; + } + return false; + } public static IEnumerable GetEnableVariables() { return IdVariables.Where(a => a.Value.DeviceRuntime?.Enable != false && a.Value.DeviceRuntime?.ChannelRuntime?.Enable != false && a.Value?.Enable == true).Select(a => a.Value); diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Device/DeviceService.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Device/DeviceService.cs index a40ae99ec..d1b3710ea 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Services/Device/DeviceService.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Services/Device/DeviceService.cs @@ -267,7 +267,7 @@ internal sealed class DeviceService : BaseService, IDeviceService else ManageHelper.CheckDeviceCount(1); - if (input.RedundantEnable && GlobalData.IsRedundant(input.RedundantDeviceId ?? 0)) + if (input.RedundantEnable && GlobalData.IsRedundantEnable(input.RedundantDeviceId ?? 0)) throw Oops.Bah($"Redundancy configuration error, backup device has been planned into another redundancy group"); if (await base.SaveAsync(input, type).ConfigureAwait(false)) diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/ChannelDeviceTree.razor.cs b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/ChannelDeviceTree.razor.cs index e046a6643..ac6978878 100644 --- a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/ChannelDeviceTree.razor.cs +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/ChannelDeviceTree.razor.cs @@ -13,7 +13,6 @@ using Microsoft.AspNetCore.Components.Web; using ThingsGateway.Admin.Application; using ThingsGateway.Admin.Razor; -using ThingsGateway.NewLife; using ThingsGateway.NewLife.Extension; using ThingsGateway.NewLife.Json.Extension; using ThingsGateway.SqlSugar; diff --git a/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/ModbusBenchmark.cs b/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/ModbusBenchmark.cs index 2d0dc484e..45dd902be 100644 --- a/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/ModbusBenchmark.cs +++ b/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/ModbusBenchmark.cs @@ -34,8 +34,8 @@ namespace ThingsGateway.Foundation; public class ModbusBenchmark : IDisposable { public static int ClientCount = 1; - public static int TaskNumberOfItems = 4; - public static int NumberOfItems = 40; + public static int TaskNumberOfItems = 1; + public static int NumberOfItems = 1000; private readonly List _lgbModbusClients = []; private List thingsgatewaymodbuss = new(); diff --git a/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/S7Benchmark.cs b/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/S7Benchmark.cs index a9764088a..76d880426 100644 --- a/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/S7Benchmark.cs +++ b/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/S7Benchmark.cs @@ -10,7 +10,6 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Diagnosers; -using BenchmarkDotNet.Jobs; using HslCommunication.Profinet.Siemens; diff --git a/src/Plugin/ThingsGateway.Foundation.OpcUa/OpcUaMaster/OpcUaMaster.cs b/src/Plugin/ThingsGateway.Foundation.OpcUa/OpcUaMaster/OpcUaMaster.cs index 27cf31297..c775955b0 100644 --- a/src/Plugin/ThingsGateway.Foundation.OpcUa/OpcUaMaster/OpcUaMaster.cs +++ b/src/Plugin/ThingsGateway.Foundation.OpcUa/OpcUaMaster/OpcUaMaster.cs @@ -837,10 +837,16 @@ public class OpcUaMaster : IAsyncDisposable { foreach (var value in monitoreditem.DequeueValues()) { - var variableNode = ReadNode(monitoreditem.StartNodeId.ToString(), false, StatusCode.IsGood(value.StatusCode)).GetAwaiter().GetResult(); + var variableNode = ReadNode(monitoreditem.StartNodeId.ToString(), false, StatusCode.IsGood(value.StatusCode)).GetAwaiter().GetResult(); if (value.Value != null) { + if (value.Value.GetType().IsRichPrimitive()) + { + DataChangedHandler?.Invoke((variableNode, monitoreditem, value, null)); + continue; + } + var data = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value); if (data == null && value.Value != null) { diff --git a/src/Plugin/ThingsGateway.Foundation.OpcUa/Utils/CollectionExtension.cs b/src/Plugin/ThingsGateway.Foundation.OpcUa/Utils/CollectionExtension.cs index 27b4626d6..957b8ded9 100644 --- a/src/Plugin/ThingsGateway.Foundation.OpcUa/Utils/CollectionExtension.cs +++ b/src/Plugin/ThingsGateway.Foundation.OpcUa/Utils/CollectionExtension.cs @@ -12,6 +12,38 @@ namespace ThingsGateway.Foundation.OpcUa; internal static class CollectionExtension { + /// + /// 判断是否是元组类型 + /// + /// 类型 + /// + internal static bool IsValueTuple(this Type type) + { + return type.Namespace == "System" && type.Name.Contains("ValueTuple`"); + } + /// + /// 判断是否是富基元类型 + /// + /// 类型 + /// + public static bool IsRichPrimitive(this Type? type) + { + if (type == null) return false; + + // 处理元组类型 + if (type.IsValueTuple()) return false; + + // 处理数组类型,基元数组类型也可以是基元类型 + if (type.IsArray) return type.GetElementType()?.IsRichPrimitive() ?? false; + + // 基元类型或值类型或字符串类型 + if (type.IsPrimitive || type.IsValueType || type == typeof(string)) return true; + + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) return type.GenericTypeArguments[0].IsRichPrimitive(); + + return false; + } + public static IEnumerable> ChunkBetter(this IEnumerable source, int chunkSize) { if (source == null) throw new ArgumentNullException(nameof(source)); diff --git a/src/Plugin/ThingsGateway.Plugin.OpcDa/OpcDaMaster/OpcDaMaster.cs b/src/Plugin/ThingsGateway.Plugin.OpcDa/OpcDaMaster/OpcDaMaster.cs index 5cd96df86..790b7ff8e 100644 --- a/src/Plugin/ThingsGateway.Plugin.OpcDa/OpcDaMaster/OpcDaMaster.cs +++ b/src/Plugin/ThingsGateway.Plugin.OpcDa/OpcDaMaster/OpcDaMaster.cs @@ -206,7 +206,9 @@ public class OpcDaMaster : CollectBase if (TaskSchedulerLoop?.Stoped == true) return; if (DisposedValue) return; - LogMessage?.Trace($"{ToString()} Change:{Environment.NewLine} {values?.ToSystemTextJsonString()}"); + + if (LogMessage.LogLevel <= LogLevel.Trace) + LogMessage?.Trace($"{ToString()} Change:{Environment.NewLine} {values?.ToSystemTextJsonString()}"); foreach (var data in values) { @@ -215,11 +217,13 @@ public class OpcDaMaster : CollectBase if (TaskSchedulerLoop?.Stoped == true) return; if (DisposedValue) return; - var type = data.Value.GetType(); - if (data.Value is Array) - { - type = type.GetElementType(); - } + + //var type = data.Value.GetType(); + //if (data.Value is Array) + //{ + // type = type.GetElementType(); + //} + if (!VariableAddresDicts.TryGetValue(data.Name, out var itemReads)) return; foreach (var item in itemReads) diff --git a/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaMaster/OpcUaMaster.cs b/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaMaster/OpcUaMaster.cs index 64098f095..1dab1361d 100644 --- a/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaMaster/OpcUaMaster.cs +++ b/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaMaster/OpcUaMaster.cs @@ -164,8 +164,10 @@ public class OpcUaMaster : CollectBase //如果是订阅模式,连接时添加订阅组 if (_plc.OpcUaProperty?.ActiveSubscribe == true && CurrentDevice.VariableSourceReads.Count > 0 && _plc.Session.SubscriptionCount < CurrentDevice.VariableSourceReads.Count) { + if (cancellationToken.IsCancellationRequested) return; foreach (var variableSourceRead in CurrentDevice.VariableSourceReads) { + if (cancellationToken.IsCancellationRequested) return; try { if (_plc.Session.Subscriptions.FirstOrDefault(a => a.DisplayName == variableSourceRead.RegisterAddress) == null) @@ -186,8 +188,8 @@ public class OpcUaMaster : CollectBase checkLog = false; } - LogMessage?.LogInformation("AddSubscriptions done"); } + LogMessage?.LogInformation("AddSubscriptions done"); } } } @@ -329,6 +331,7 @@ public class OpcUaMaster : CollectBase private void DataChangedHandler((Opc.Ua.VariableNode variableNode, MonitoredItem monitoredItem, DataValue dataValue, JToken jToken) data) { + DateTime time = DateTime.Now; try { @@ -338,14 +341,15 @@ public class OpcUaMaster : CollectBase return; if (TaskSchedulerLoop?.Stoped == true) return; - LogMessage?.Trace($"Change: {Environment.NewLine} {data.monitoredItem.StartNodeId} : {data.jToken?.ToString()}"); + if (LogMessage.LogLevel <= LogLevel.Trace) + LogMessage?.Trace($"Change: {Environment.NewLine} {data.monitoredItem.StartNodeId} : {data.jToken?.ToString() ?? data.dataValue?.Value?.ToJsonString()}"); //尝试固定点位的数据类型 - var type = TypeInfo.GetSystemType(TypeInfo.GetBuiltInType(data.variableNode.DataType, _plc.Session.SystemContext.TypeTable), data.variableNode.ValueRank); + //var type = TypeInfo.GetSystemType(TypeInfo.GetBuiltInType(data.variableNode.DataType, _plc.Session.SystemContext.TypeTable), data.variableNode.ValueRank); if (!VariableAddresDicts.TryGetValue(data.monitoredItem.StartNodeId.ToString(), out var itemReads)) return; - object value = data.jToken.GetObjectFromJToken(); + object value = data.jToken?.GetObjectFromJToken() ?? data.dataValue?.Value; var isGood = StatusCode.IsGood(data.dataValue.StatusCode); if (_driverProperties.SourceTimestampEnable) @@ -382,6 +386,8 @@ public class OpcUaMaster : CollectBase LogMessage?.LogWarning(ex); success = false; } + + } #endif diff --git a/src/Plugin/ThingsGateway.Plugin.OpcUa/Pages/OpcUaMaster.razor.cs b/src/Plugin/ThingsGateway.Plugin.OpcUa/Pages/OpcUaMaster.razor.cs index 59263bcad..facc425fa 100644 --- a/src/Plugin/ThingsGateway.Plugin.OpcUa/Pages/OpcUaMaster.razor.cs +++ b/src/Plugin/ThingsGateway.Plugin.OpcUa/Pages/OpcUaMaster.razor.cs @@ -100,7 +100,7 @@ public partial class OpcUaMaster : IAsyncDisposable LogMessage?.AddLogger(logger); _plc.LogEvent = (a, b, c, d) => LogMessage?.Log((LogLevel)a, b, c, d); - _plc.DataChangedHandler += (a) => LogMessage?.Trace($"id:{a.monitoredItem?.StartNodeId};stateCode:{a.dataValue?.StatusCode};value:{a.jToken?.ToString()}"); + _plc.DataChangedHandler += (a) => LogMessage?.Trace($"id:{a.monitoredItem?.StartNodeId};stateCode:{a.dataValue?.StatusCode};value:{a.jToken?.ToString() ?? a.dataValue?.Value?.ToJsonString()}"); base.OnInitialized(); }