mirror of
				https://gitee.com/ThingsGateway/ThingsGateway.git
				synced 2025-10-31 07:33:58 +08:00 
			
		
		
		
	Compare commits
	
		
			3 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 59241b8faa | ||
|   | 52b3097f04 | ||
|   | d922296b70 | 
| @@ -267,7 +267,7 @@ public class RequestAuditFilter : IAsyncActionFilter, IOrderedFilter | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception.ToSystemTextJsonString()}"); | ||||
|             logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception?.ToSystemTextJsonString()}{Environment.NewLine}{logData.Validation?.ToSystemTextJsonString()}"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -156,7 +156,7 @@ public class BlazorAppContext | ||||
|             CurrentUser = (await SysUserService.GetUserByIdAsync(UserManager.UserId))!; | ||||
|         } | ||||
|     } | ||||
|     TimeTick timeTick = new("50000"); | ||||
|     TimeTick timeTick = new("60000"); | ||||
|     /// <summary> | ||||
|     /// 是否拥有按钮授权 | ||||
|     /// </summary> | ||||
|   | ||||
| @@ -37,15 +37,15 @@ public sealed class Retry | ||||
|     { | ||||
|         if (action == null) throw new ArgumentNullException(nameof(action)); | ||||
|  | ||||
|         InvokeAsync(async () => | ||||
|         InvokeAsync(() => | ||||
|         { | ||||
|             action(); | ||||
|             await Task.CompletedTask.ConfigureAwait(false); | ||||
|             return Task.CompletedTask; | ||||
|         }, numRetries, retryTimeout, finalThrow, exceptionTypes, fallbackPolicy == null ? null | ||||
|         : async (ex) => | ||||
|         : (ex) => | ||||
|         { | ||||
|             fallbackPolicy?.Invoke(ex); | ||||
|             await Task.CompletedTask.ConfigureAwait(false); | ||||
|             return Task.CompletedTask; | ||||
|         }, retryAction).GetAwaiter().GetResult(); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -172,7 +172,6 @@ public class TimerScheduler : ILogFeature | ||||
|                         else if (!timer.Async) | ||||
|                             Execute(timer); | ||||
|                         else | ||||
|                             //Task.Factory.StartNew(() => ProcessItem(timer)); | ||||
|                             // 不需要上下文流动,捕获所有异常 | ||||
|                             ThreadPool.UnsafeQueueUserWorkItem(s => | ||||
|                             { | ||||
| @@ -231,8 +230,6 @@ public class TimerScheduler : ILogFeature | ||||
|     { | ||||
|         if (state is not TimerX timer) return; | ||||
|  | ||||
|         TimerX.Current = timer; | ||||
|  | ||||
|         // 控制日志显示 | ||||
|         WriteLogEventArgs.CurrentThreadName = Name == "Default" ? "T" : Name; | ||||
|  | ||||
| @@ -274,7 +271,6 @@ public class TimerScheduler : ILogFeature | ||||
|     { | ||||
|         if (state is not TimerX timer) return; | ||||
|  | ||||
|         TimerX.Current = timer; | ||||
|  | ||||
|         // 控制日志显示 | ||||
|         WriteLogEventArgs.CurrentThreadName = Name == "Default" ? "T" : Name; | ||||
| @@ -322,8 +318,6 @@ public class TimerScheduler : ILogFeature | ||||
|  | ||||
|         timer.Calling = false; | ||||
|  | ||||
|         TimerX.Current = null; | ||||
|  | ||||
|         // 控制日志显示 | ||||
|         WriteLogEventArgs.CurrentThreadName = null; | ||||
|  | ||||
|   | ||||
| @@ -84,15 +84,7 @@ public class TimerX : ITimer, IDisposable | ||||
|     private readonly Cron[]? _crons; | ||||
|     #endregion | ||||
|  | ||||
|     #region 静态 | ||||
| #if NET452 | ||||
|     private static readonly ThreadLocal<TimerX?> _Current = new(); | ||||
| #else | ||||
|     private static readonly AsyncLocal<TimerX?> _Current = new(); | ||||
| #endif | ||||
|     /// <summary>当前定时器</summary> | ||||
|     public static TimerX? Current { get => _Current.Value; set => _Current.Value = value; } | ||||
|     #endregion | ||||
|  | ||||
|  | ||||
|     #region 构造 | ||||
|     private TimerX(Object? target, MethodInfo method, Object? state, String? scheduler = null) | ||||
| @@ -382,19 +374,27 @@ public class TimerX : ITimer, IDisposable | ||||
|     /// <param name="period">构造 Timer 时指定的回调方法调用之间的时间间隔。 指定 InfiniteTimeSpan 可以禁用定期终止。</param> | ||||
|     /// <returns></returns> | ||||
|     public Boolean Change(TimeSpan dueTime, TimeSpan period) | ||||
|     { | ||||
|         return Change((int)dueTime.TotalMilliseconds, (int)period.TotalMilliseconds); | ||||
|     } | ||||
|     /// <summary>更改计时器的启动时间和方法调用之间的时间间隔,使用 TimeSpan 值度量时间间隔。</summary> | ||||
|     /// <param name="dueTime">一个 TimeSpan,表示在调用构造 ITimer 时指定的回调方法之前的延迟时间量。 指定 InfiniteTimeSpan 可防止重新启动计时器。 指定 Zero 可立即重新启动计时器。</param> | ||||
|     /// <param name="period">构造 Timer 时指定的回调方法调用之间的时间间隔。 指定 InfiniteTimeSpan 可以禁用定期终止。</param> | ||||
|     /// <returns></returns> | ||||
|     public Boolean Change(int dueTime, int period) | ||||
|     { | ||||
|         if (Absolutely) return false; | ||||
|         if (Crons?.Length > 0) return false; | ||||
|  | ||||
|         if (period.TotalMilliseconds <= 0) | ||||
|         if (period <= 0) | ||||
|         { | ||||
|             Dispose(); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         Period = (Int32)period.TotalMilliseconds; | ||||
|         Period = period; | ||||
|  | ||||
|         if (dueTime.TotalMilliseconds >= 0) SetNext((Int32)dueTime.TotalMilliseconds); | ||||
|         if (dueTime >= 0) SetNext(dueTime); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|   | ||||
| @@ -280,43 +280,6 @@ public static class ControlHelper | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void ProcessBell(ref String m) | ||||
|     { | ||||
|         var ch = (Char)7; | ||||
|         var p = 0; | ||||
|         while (true) | ||||
|         { | ||||
|             p = m.IndexOf(ch, p); | ||||
|             if (p < 0) break; | ||||
|  | ||||
|             if (p > 0) | ||||
|             { | ||||
|                 var str = m[..p]; | ||||
|                 if (p + 1 < m.Length) str += m[(p + 1)..]; | ||||
|                 m = str; | ||||
|             } | ||||
|  | ||||
|             //Console.Beep(); | ||||
|             // 用定时器来控制Beep,避免被堵塞 | ||||
|             _timer ??= new TimerX(Bell, null, 100, 100); | ||||
|             _Beep = true; | ||||
|             //SystemSounds.Beep.Play(); | ||||
|             p++; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static TimerX? _timer; | ||||
|     private static Boolean _Beep; | ||||
|  | ||||
|     private static void Bell(Object? state) | ||||
|     { | ||||
|         if (_Beep) | ||||
|         { | ||||
|             _Beep = false; | ||||
|             Console.Beep(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [DllImport("user32.dll")] | ||||
|     private static extern Int32 SendMessage(IntPtr hwnd, Int32 wMsg, Int32 wParam, Int32 lParam); | ||||
|     private const Int32 SB_TOP = 6; | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| 	</PropertyGroup> | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="BootstrapBlazor.FontAwesome" Version="9.0.2" /> | ||||
| 		<PackageReference Include="BootstrapBlazor" Version="9.7.3" /> | ||||
| 		<PackageReference Include="BootstrapBlazor" Version="9.7.4-beta07" /> | ||||
| 		<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| <Project> | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<PluginVersion>10.8.2</PluginVersion> | ||||
| 		<ProPluginVersion>10.8.2</ProPluginVersion> | ||||
| 		<PluginVersion>10.8.8</PluginVersion> | ||||
| 		<ProPluginVersion>10.8.8</ProPluginVersion> | ||||
| 		<AuthenticationVersion>2.8.0</AuthenticationVersion> | ||||
| 		<SourceGeneratorVersion>10.8.2</SourceGeneratorVersion> | ||||
| 		<NET8Version>8.0.17</NET8Version> | ||||
|   | ||||
| @@ -549,7 +549,7 @@ public abstract class DeviceBase : DisposableObject, IDevice | ||||
|             Channel.ChannelReceivedWaitDict.TryAdd(sign, ChannelReceived); | ||||
|             var sendOperResult = await SendAsync(command, clientChannel, endPoint, cancellationToken).ConfigureAwait(false); | ||||
|             if (!sendOperResult.IsSuccess) | ||||
|                 throw sendOperResult.Exception ?? new(sendOperResult.ErrorMessage); | ||||
|                 throw sendOperResult.Exception ?? new(sendOperResult.ErrorMessage ?? "unknown error"); | ||||
|  | ||||
|             await waitData.WaitAsync(timeout).ConfigureAwait(false); | ||||
|  | ||||
|   | ||||
| @@ -112,11 +112,18 @@ public static partial class DeviceExtension | ||||
|             int index = variable.Index; | ||||
|             try | ||||
|             { | ||||
|                 var data = byteConverter.GetDataFormBytes(device, variable.RegisterAddress, buffer, index, dataType, variable.ArrayLength ?? 1); | ||||
|                 result = Set(variable, data); | ||||
|                 if (exWhenAny) | ||||
|                     if (!result.IsSuccess) | ||||
|                         return result; | ||||
|                 var changed = byteConverter.GetChangedDataFormBytes(device, variable.RegisterAddress, buffer, index, dataType, variable.ArrayLength ?? 1, variable.Value, out var data); | ||||
|                 if (changed) | ||||
|                 { | ||||
|                     result = variable.SetValue(data, time); | ||||
|                     if (exWhenAny) | ||||
|                         if (!result.IsSuccess) | ||||
|                             return result; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     variable.SetNoChangedValue(time); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
| @@ -124,10 +131,7 @@ public static partial class DeviceExtension | ||||
|             } | ||||
|         } | ||||
|         return result; | ||||
|         OperResult Set(IVariable organizedVariable, object num) | ||||
|         { | ||||
|             return organizedVariable.SetValue(num, time); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|   | ||||
| @@ -241,87 +241,292 @@ public static class ThingsGatewayBitConverterExtension | ||||
|     /// <summary> | ||||
|     /// 根据数据类型获取实际值 | ||||
|     /// </summary> | ||||
|     public static object GetDataFormBytes(this IThingsGatewayBitConverter byteConverter, IDevice device, string address, byte[] buffer, int index, DataTypeEnum dataType, int arrayLength) | ||||
|     public static bool GetChangedDataFormBytes( | ||||
|         this IThingsGatewayBitConverter byteConverter, | ||||
|         IDevice device, | ||||
|         string address, | ||||
|         byte[] buffer, | ||||
|         int index, | ||||
|         DataTypeEnum dataType, | ||||
|         int arrayLength, | ||||
|         object? oldValue, | ||||
|         out object? result) | ||||
|     { | ||||
|         switch (dataType) | ||||
|         { | ||||
|             case DataTypeEnum.Boolean: | ||||
|                 return arrayLength > 1 ? | ||||
|                 byteConverter.ToBoolean(buffer, index, arrayLength, device.BitReverse(address)) : | ||||
|                 byteConverter.ToBoolean(buffer, index, device.BitReverse(address)); | ||||
|                 if (arrayLength > 1) | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToBoolean(buffer, index, arrayLength, device.BitReverse(address)); | ||||
|                     if (oldValue is bool[] oldArr && newVal.SequenceEqual(oldArr)) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToBoolean(buffer, index, device.BitReverse(address)); | ||||
|                     if (oldValue is bool oldVal && oldVal == newVal) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|             case DataTypeEnum.Byte: | ||||
|                 return | ||||
|                 arrayLength > 1 ? | ||||
|                 byteConverter.ToByte(buffer, index, arrayLength) : | ||||
|                 byteConverter.ToByte(buffer, index); | ||||
|                 if (arrayLength > 1) | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToByte(buffer, index, arrayLength); | ||||
|                     if (oldValue is byte[] oldArr && newVal.SequenceEqual(oldArr)) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToByte(buffer, index); | ||||
|                     if (oldValue is byte oldVal && oldVal == newVal) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|             case DataTypeEnum.Int16: | ||||
|                 return | ||||
|                  arrayLength > 1 ? | ||||
|                 byteConverter.ToInt16(buffer, index, arrayLength) : | ||||
|                 byteConverter.ToInt16(buffer, index); | ||||
|                 if (arrayLength > 1) | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToInt16(buffer, index, arrayLength); | ||||
|                     if (oldValue is short[] oldArr && newVal.SequenceEqual(oldArr)) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToInt16(buffer, index); | ||||
|                     if (oldValue is short oldVal && oldVal == newVal) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|             case DataTypeEnum.UInt16: | ||||
|                 return | ||||
|                  arrayLength > 1 ? | ||||
|                 byteConverter.ToUInt16(buffer, index, arrayLength) : | ||||
|                 byteConverter.ToUInt16(buffer, index); | ||||
|                 if (arrayLength > 1) | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToUInt16(buffer, index, arrayLength); | ||||
|                     if (oldValue is ushort[] oldArr && newVal.SequenceEqual(oldArr)) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToUInt16(buffer, index); | ||||
|                     if (oldValue is ushort oldVal && oldVal == newVal) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|             case DataTypeEnum.Int32: | ||||
|                 return | ||||
|                  arrayLength > 1 ? | ||||
|                 byteConverter.ToInt32(buffer, index, arrayLength) : | ||||
|                 byteConverter.ToInt32(buffer, index); | ||||
|                 if (arrayLength > 1) | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToInt32(buffer, index, arrayLength); | ||||
|                     if (oldValue is int[] oldArr && newVal.SequenceEqual(oldArr)) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToInt32(buffer, index); | ||||
|                     if (oldValue is int oldVal && oldVal == newVal) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|             case DataTypeEnum.UInt32: | ||||
|                 return | ||||
|                  arrayLength > 1 ? | ||||
|                 byteConverter.ToUInt32(buffer, index, arrayLength) : | ||||
|                 byteConverter.ToUInt32(buffer, index); | ||||
|                 if (arrayLength > 1) | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToUInt32(buffer, index, arrayLength); | ||||
|                     if (oldValue is uint[] oldArr && newVal.SequenceEqual(oldArr)) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToUInt32(buffer, index); | ||||
|                     if (oldValue is uint oldVal && oldVal == newVal) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|             case DataTypeEnum.Int64: | ||||
|                 return | ||||
|                  arrayLength > 1 ? | ||||
|                 byteConverter.ToInt64(buffer, index, arrayLength) : | ||||
|                 byteConverter.ToInt64(buffer, index); | ||||
|                 if (arrayLength > 1) | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToInt64(buffer, index, arrayLength); | ||||
|                     if (oldValue is long[] oldArr && newVal.SequenceEqual(oldArr)) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToInt64(buffer, index); | ||||
|                     if (oldValue is long oldVal && oldVal == newVal) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|             case DataTypeEnum.UInt64: | ||||
|                 return | ||||
|                 arrayLength > 1 ? | ||||
|                 byteConverter.ToUInt64(buffer, index, arrayLength) : | ||||
|                 byteConverter.ToUInt64(buffer, index); | ||||
|                 if (arrayLength > 1) | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToUInt64(buffer, index, arrayLength); | ||||
|                     if (oldValue is ulong[] oldArr && newVal.SequenceEqual(oldArr)) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToUInt64(buffer, index); | ||||
|                     if (oldValue is ulong oldVal && oldVal == newVal) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|             case DataTypeEnum.Single: | ||||
|                 return | ||||
|                  arrayLength > 1 ? | ||||
|                 byteConverter.ToSingle(buffer, index, arrayLength) : | ||||
|                 byteConverter.ToSingle(buffer, index); | ||||
|                 if (arrayLength > 1) | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToSingle(buffer, index, arrayLength); | ||||
|                     if (oldValue is float[] oldArr && newVal.SequenceEqual(oldArr)) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToSingle(buffer, index); | ||||
|                     if (oldValue is float oldVal && oldVal == newVal) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|             case DataTypeEnum.Double: | ||||
|                 return | ||||
|                  arrayLength > 1 ? | ||||
|                 byteConverter.ToDouble(buffer, index, arrayLength) : | ||||
|                 byteConverter.ToDouble(buffer, index); | ||||
|                 if (arrayLength > 1) | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToDouble(buffer, index, arrayLength); | ||||
|                     if (oldValue is double[] oldArr && newVal.SequenceEqual(oldArr)) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var newVal = byteConverter.ToDouble(buffer, index); | ||||
|                     if (oldValue is double oldVal && oldVal == newVal) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newVal; | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|             case DataTypeEnum.String: | ||||
|             default: | ||||
|                 if (arrayLength > 1) | ||||
|                 { | ||||
|                     List<String> strings = new(); | ||||
|                     var newArr = new string[arrayLength]; | ||||
|                     for (int i = 0; i < arrayLength; i++) | ||||
|                     { | ||||
|                         var data = byteConverter.ToString(buffer, index + i * byteConverter.StringLength ?? 1, byteConverter.StringLength ?? 1); | ||||
|                         strings.Add(data); | ||||
|                         newArr[i] = byteConverter.ToString(buffer, index + i * (byteConverter.StringLength ?? 1), byteConverter.StringLength ?? 1); | ||||
|                     } | ||||
|                     return strings.ToArray(); | ||||
|  | ||||
|                     if (oldValue is string[] oldArr && newArr.SequenceEqual(oldArr)) | ||||
|                     { | ||||
|                         result = oldValue; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = newArr; | ||||
|                     return true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return byteConverter.ToString(buffer, index, byteConverter.StringLength ?? 1); | ||||
|                     var str = byteConverter.ToString(buffer, index, byteConverter.StringLength ?? 1); | ||||
|                     if (oldValue is string oldStr && oldStr == str) | ||||
|                     { | ||||
|                         result = oldStr; | ||||
|                         return false; | ||||
|                     } | ||||
|                     result = str; | ||||
|                     return true; | ||||
|                 } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     #endregion 获取对应数据类型的数据 | ||||
| } | ||||
|   | ||||
| @@ -55,6 +55,8 @@ public interface IVariable | ||||
|     /// </summary> | ||||
|     IVariableSource VariableSource { get; set; } | ||||
|  | ||||
|     void SetNoChangedValue(DateTime dateTime); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 赋值变量,返回是否成功,一般在实体内部需要做异常保存 | ||||
|     /// </summary> | ||||
|   | ||||
| @@ -8,8 +8,6 @@ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using ThingsGateway.NewLife; | ||||
|  | ||||
| namespace ThingsGateway.Foundation; | ||||
|  | ||||
| /// <summary> | ||||
| @@ -33,9 +31,9 @@ public interface IVariableSource | ||||
|     string RegisterAddress { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// TimeTick | ||||
|     /// IntervalTime | ||||
|     /// </summary> | ||||
|     TimeTick TimeTick { get; set; } | ||||
|     string IntervalTime { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 添加变量 | ||||
|   | ||||
| @@ -62,6 +62,10 @@ public class VariableClass : IVariable | ||||
|     /// </summary> | ||||
|     public IVariableSource VariableSource { get; set; } | ||||
|  | ||||
|     public void SetNoChangedValue(DateTime dateTime) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 赋值变量 | ||||
|     /// </summary> | ||||
|   | ||||
| @@ -8,8 +8,6 @@ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using ThingsGateway.NewLife; | ||||
|  | ||||
| namespace ThingsGateway.Foundation; | ||||
|  | ||||
| /// <summary> | ||||
| @@ -28,8 +26,10 @@ public class VariableSourceClass : IVariableSource | ||||
|     /// <inheritdoc/> | ||||
|     public string RegisterAddress { get; set; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public TimeTick TimeTick { get; set; } | ||||
|     /// <summary> | ||||
|     /// IntervalTime | ||||
|     /// </summary> | ||||
|     public string IntervalTime { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 已打包变量 | ||||
|   | ||||
| @@ -0,0 +1,143 @@ | ||||
| using ThingsGateway.NewLife; | ||||
| using ThingsGateway.NewLife.Threading; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
|  | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
|  | ||||
| public class CronScheduledTask : DisposeBase, IScheduledTask | ||||
| { | ||||
|     private int _interval10MS = 10; | ||||
|     private string _interval; | ||||
|     private readonly Func<object?, CancellationToken, Task> _taskFunc; | ||||
|     private readonly Action<object?, CancellationToken> _taskAction; | ||||
|     private readonly CancellationToken _token; | ||||
|     private TimerX? _timer; | ||||
|     private object? _state; | ||||
|     private ILog LogMessage; | ||||
|     private volatile int _isRunning = 0; | ||||
|     private volatile int _pendingTriggers = 0; | ||||
|  | ||||
|     public CronScheduledTask(string interval, Func<object?, CancellationToken, Task> taskFunc, object? state, ILog log, CancellationToken token) | ||||
|     { | ||||
|         _interval = interval; | ||||
|         LogMessage = log; | ||||
|         _state = state; | ||||
|         _taskFunc = taskFunc; | ||||
|         _token = token; | ||||
|     } | ||||
|     public CronScheduledTask(string interval, Action<object?, CancellationToken> taskAction, object? state, ILog log, CancellationToken token) | ||||
|     { | ||||
|         _interval = interval; | ||||
|         LogMessage = log; | ||||
|         _state = state; | ||||
|         _taskAction = taskAction; | ||||
|         _token = token; | ||||
|     } | ||||
|  | ||||
|     public void Start() | ||||
|     { | ||||
|         _timer?.Dispose(); | ||||
|         if (_token.IsCancellationRequested) return; | ||||
|         if (_taskAction == null) | ||||
|             _timer = new TimerX(TimerCallback, _state, _interval, nameof(IScheduledTask)) { Async = true }; | ||||
|         else | ||||
|             _timer = new TimerX(TimerCallbackAsync, _state, _interval, nameof(IScheduledTask)) { Async = true }; | ||||
|     } | ||||
|  | ||||
|     private async Task TimerCallbackAsync(object? state) | ||||
|     { | ||||
|         if (_token.IsCancellationRequested) | ||||
|             return; | ||||
|  | ||||
|         Interlocked.Increment(ref _pendingTriggers); | ||||
|  | ||||
|         if (Interlocked.Exchange(ref _isRunning, 1) == 1) | ||||
|             return; | ||||
|  | ||||
|         // 减少一个触发次数 | ||||
|         Interlocked.Decrement(ref _pendingTriggers); | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             await _taskFunc(state, _token).ConfigureAwait(false); | ||||
|         } | ||||
|         catch (OperationCanceledException) | ||||
|         { | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage.LogWarning(ex); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             Interlocked.Exchange(ref _isRunning, 0); | ||||
|         } | ||||
|  | ||||
|         if (Interlocked.Exchange(ref _pendingTriggers, 0) >= 1) | ||||
|         { | ||||
|             if (!_token.IsCancellationRequested) | ||||
|             { | ||||
|                 DelayDo(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void TimerCallback(object? state) | ||||
|     { | ||||
|         if (_token.IsCancellationRequested) | ||||
|             return; | ||||
|  | ||||
|         Interlocked.Increment(ref _pendingTriggers); | ||||
|  | ||||
|         if (Interlocked.Exchange(ref _isRunning, 1) == 1) | ||||
|             return; | ||||
|  | ||||
|         // 减少一个触发次数 | ||||
|         Interlocked.Decrement(ref _pendingTriggers); | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             _taskAction(state, _token); | ||||
|         } | ||||
|         catch (OperationCanceledException) | ||||
|         { | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage.LogWarning(ex); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             Interlocked.Exchange(ref _isRunning, 0); | ||||
|         } | ||||
|  | ||||
|         if (Interlocked.Exchange(ref _pendingTriggers, 0) >= 1) | ||||
|         { | ||||
|             if (!_token.IsCancellationRequested) | ||||
|             { | ||||
|                 DelayDo(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void DelayDo() | ||||
|     { | ||||
|         // 延迟触发下一次 | ||||
|         if (!_token.IsCancellationRequested) | ||||
|             _timer?.SetNext(_interval10MS); | ||||
|     } | ||||
|  | ||||
|     public void Stop() | ||||
|     { | ||||
|         _timer?.Dispose(); | ||||
|         _timer = null; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         Stop(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,7 @@ | ||||
| namespace ThingsGateway.Gateway.Application | ||||
| { | ||||
|     public interface IScheduledIntIntervalTask | ||||
|     { | ||||
|         int IntervalMS { get; } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,13 @@ | ||||
| namespace ThingsGateway.Gateway.Application | ||||
| { | ||||
|     public interface IScheduledTask | ||||
|     { | ||||
|         void Start(); | ||||
|         void Stop(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -1,26 +1,27 @@ | ||||
| using TouchSocket.Core; | ||||
| using ThingsGateway.NewLife; | ||||
| using ThingsGateway.NewLife.Threading; | ||||
| 
 | ||||
| using TouchSocket.Core; | ||||
| 
 | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
| 
 | ||||
| public class ScheduledTask | ||||
| public class ScheduledAsyncTask : DisposeBase, IScheduledTask, IScheduledIntIntervalTask | ||||
| { | ||||
|     private TimeSpan _interval { get; } | ||||
|     private TimeSpan _interval10 = TimeSpan.FromMilliseconds(10); | ||||
|     private double _intervalMS { get; } | ||||
|     private int _interval10MS = 10; | ||||
|     public int IntervalMS { get; } | ||||
|     private readonly Func<object?, CancellationToken, Task> _taskFunc; | ||||
|     private readonly CancellationToken _token; | ||||
|     private Timer? _timer; | ||||
|     private TimerX? _timer; | ||||
|     private object? _state; | ||||
|     private ILog LogMessage; | ||||
|     private volatile int _isRunning = 0; | ||||
|     private volatile int _pendingTriggers = 0; | ||||
| 
 | ||||
|     public ScheduledTask(TimeSpan interval, Func<object?, CancellationToken, Task> taskFunc, object? state, ILog log, CancellationToken token) | ||||
|     public ScheduledAsyncTask(int interval, Func<object?, CancellationToken, Task> taskFunc, object? state, ILog log, CancellationToken token) | ||||
|     { | ||||
|         IntervalMS = interval; | ||||
|         LogMessage = log; | ||||
|         _state = state; | ||||
|         _interval = interval; | ||||
|         _intervalMS = interval.TotalMilliseconds; | ||||
|         _taskFunc = taskFunc; | ||||
|         _token = token; | ||||
|     } | ||||
| @@ -28,24 +29,23 @@ public class ScheduledTask | ||||
|     public void Start() | ||||
|     { | ||||
|         _timer?.Dispose(); | ||||
|         _timer = new Timer(TimerCallback, _state, TimeSpan.Zero, _interval); | ||||
|         if (!_token.IsCancellationRequested) | ||||
|             _timer = new TimerX(DoAsync, _state, IntervalMS, IntervalMS, nameof(IScheduledTask)) { Async = true }; | ||||
|     } | ||||
| 
 | ||||
|     private void TimerCallback(object? state) | ||||
|     { | ||||
|         _ = Do(state); | ||||
|     } | ||||
| 
 | ||||
|     private async Task Do(object? state) | ||||
|     private async Task DoAsync(object? state) | ||||
|     { | ||||
|         if (_token.IsCancellationRequested) | ||||
|             return; | ||||
| 
 | ||||
|         Interlocked.Exchange(ref _pendingTriggers, 1); | ||||
|         Interlocked.Increment(ref _pendingTriggers); | ||||
| 
 | ||||
|         if (Interlocked.Exchange(ref _isRunning, 1) == 1) | ||||
|             return; | ||||
| 
 | ||||
|         // 减少一个触发次数 | ||||
|         Interlocked.Decrement(ref _pendingTriggers); | ||||
| 
 | ||||
|         try | ||||
|         { | ||||
|             await _taskFunc(state, _token).ConfigureAwait(false); | ||||
| @@ -62,7 +62,7 @@ public class ScheduledTask | ||||
|             Interlocked.Exchange(ref _isRunning, 0); | ||||
|         } | ||||
| 
 | ||||
|         if (Interlocked.Exchange(ref _pendingTriggers, 0) == 1) | ||||
|         if (Interlocked.Exchange(ref _pendingTriggers, 0) >= 1) | ||||
|         { | ||||
|             if (!_token.IsCancellationRequested) | ||||
|             { | ||||
| @@ -74,13 +74,10 @@ public class ScheduledTask | ||||
|     private void DelayDo() | ||||
|     { | ||||
|         // 延迟触发下一次 | ||||
|         _timer?.Change(_interval10, _interval); | ||||
|         if (!_token.IsCancellationRequested) | ||||
|             _timer?.SetNext(_interval10MS); | ||||
|     } | ||||
| 
 | ||||
|     public void Change(int dueTime, int period) | ||||
|     { | ||||
|         _timer?.Change(dueTime, period); | ||||
|     } | ||||
| 
 | ||||
|     public void Stop() | ||||
|     { | ||||
| @@ -88,4 +85,9 @@ public class ScheduledTask | ||||
|         _timer = null; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         Stop(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,96 @@ | ||||
| using ThingsGateway.NewLife; | ||||
| using ThingsGateway.NewLife.Threading; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
|  | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
|  | ||||
| public class ScheduledSyncTask : DisposeBase, IScheduledTask, IScheduledIntIntervalTask | ||||
| { | ||||
|     private int _interval10MS = 10; | ||||
|     public int IntervalMS { get; } | ||||
|     private readonly Action<object?, CancellationToken> _taskAction; | ||||
|     private readonly CancellationToken _token; | ||||
|     private TimerX? _timer; | ||||
|     private object? _state; | ||||
|     private ILog LogMessage; | ||||
|     private volatile int _isRunning = 0; | ||||
|     private volatile int _pendingTriggers = 0; | ||||
|  | ||||
|     public ScheduledSyncTask(int interval, Action<object?, CancellationToken> taskFunc, object? state, ILog log, CancellationToken token) | ||||
|     { | ||||
|         IntervalMS = interval; | ||||
|         LogMessage = log; | ||||
|         _state = state; | ||||
|         _taskAction = taskFunc; | ||||
|         _token = token; | ||||
|     } | ||||
|  | ||||
|     public void Start() | ||||
|     { | ||||
|         _timer?.Dispose(); | ||||
|         if (!_token.IsCancellationRequested) | ||||
|             _timer = new TimerX(TimerCallback, _state, IntervalMS, IntervalMS, nameof(IScheduledTask)) { Async = true }; | ||||
|     } | ||||
|  | ||||
|     private void TimerCallback(object? state) | ||||
|     { | ||||
|         if (_token.IsCancellationRequested) | ||||
|             return; | ||||
|  | ||||
|         Interlocked.Increment(ref _pendingTriggers); | ||||
|  | ||||
|         if (Interlocked.Exchange(ref _isRunning, 1) == 1) | ||||
|             return; | ||||
|         Do(state); | ||||
|     } | ||||
|  | ||||
|     private void Do(object? state) | ||||
|     { | ||||
|         // 减少一个触发次数 | ||||
|         Interlocked.Decrement(ref _pendingTriggers); | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             _taskAction(state, _token); | ||||
|         } | ||||
|         catch (OperationCanceledException) | ||||
|         { | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage.LogWarning(ex); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             Interlocked.Exchange(ref _isRunning, 0); | ||||
|         } | ||||
|  | ||||
|         if (Interlocked.Exchange(ref _pendingTriggers, 0) >= 1) | ||||
|         { | ||||
|             if (!_token.IsCancellationRequested) | ||||
|             { | ||||
|                 DelayDo(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void DelayDo() | ||||
|     { | ||||
|         // 延迟触发下一次 | ||||
|         if (!_token.IsCancellationRequested) | ||||
|             _timer?.SetNext(_interval10MS); | ||||
|     } | ||||
|  | ||||
|     public void Stop() | ||||
|     { | ||||
|         _timer?.Dispose(); | ||||
|         _timer = null; | ||||
|     } | ||||
|  | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         Stop(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,29 @@ | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
|  | ||||
| public static class ScheduledTaskHelper | ||||
| { | ||||
|     public static IScheduledTask GetTask(string interval, Func<object?, CancellationToken, Task> func, object? state, TouchSocket.Core.ILog log, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (int.TryParse(interval, out int intervalV)) | ||||
|         { | ||||
|             var intervalMilliseconds = intervalV < 10 ? 10 : intervalV; | ||||
|             return new ScheduledAsyncTask(intervalMilliseconds, func, state, log, cancellationToken); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return new CronScheduledTask(interval, func, state, log, cancellationToken); | ||||
|         } | ||||
|     } | ||||
|     public static IScheduledTask GetTask(string interval, Action<object?, CancellationToken> action, object? state, TouchSocket.Core.ILog log, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (int.TryParse(interval, out int intervalV)) | ||||
|         { | ||||
|             var intervalMilliseconds = intervalV < 10 ? 10 : intervalV; | ||||
|             return new ScheduledSyncTask(intervalMilliseconds, action, state, log, cancellationToken); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return new CronScheduledTask(interval, action, state, log, cancellationToken); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -2,12 +2,16 @@ | ||||
|  | ||||
| public class TaskSchedulerLoop | ||||
| { | ||||
|     public readonly List<ScheduledTask> Tasks; | ||||
|     private readonly List<IScheduledTask> Tasks; | ||||
|  | ||||
|     public TaskSchedulerLoop(List<ScheduledTask> tasks) | ||||
|     public TaskSchedulerLoop(List<IScheduledTask> tasks) | ||||
|     { | ||||
|         Tasks = tasks; | ||||
|     } | ||||
|     public int Count() | ||||
|     { | ||||
|         return Tasks.Count; | ||||
|     } | ||||
|  | ||||
|     public void Start() | ||||
|     { | ||||
| @@ -24,11 +28,5 @@ public class TaskSchedulerLoop | ||||
|             task.Stop(); | ||||
|         } | ||||
|     } | ||||
|     public void Change(int dueTime, int period) | ||||
|     { | ||||
|         foreach (var task in Tasks) | ||||
|         { | ||||
|             task.Change(dueTime, period); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
| using BootstrapBlazor.Components; | ||||
|  | ||||
| using ThingsGateway.Extension.Generic; | ||||
| using ThingsGateway.NewLife; | ||||
| using ThingsGateway.NewLife.Extension; | ||||
| using ThingsGateway.NewLife.Threading; | ||||
|  | ||||
| @@ -96,87 +95,42 @@ public abstract class BusinessBase : DriverBase | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 循环任务 | ||||
|     /// 获取任务 | ||||
|     /// </summary> | ||||
|     /// <param name="cancellationToken">取消操作的令牌。</param> | ||||
|     /// <returns>表示异步操作结果的枚举。</returns> | ||||
|     internal override async ValueTask<ThreadRunReturnTypeEnum> ExecuteAsync(CancellationToken cancellationToken) | ||||
|     protected override List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|  | ||||
|         var setDeviceStatusTask = new ScheduledSyncTask(3000, SetDeviceStatus, null, LogMessage, cancellationToken); | ||||
|  | ||||
|         var executeTask = ScheduledTaskHelper.GetTask(CurrentDevice.IntervalTime, ProtectedExecuteAsync, null, LogMessage, cancellationToken); | ||||
|  | ||||
|         return new List<IScheduledTask>() | ||||
|             { | ||||
|                 setDeviceStatusTask, | ||||
|                 executeTask | ||||
|             }; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 间隔执行 | ||||
|     /// </summary> | ||||
|     protected abstract Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken); | ||||
|  | ||||
|     private void SetDeviceStatus(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         // 获取设备连接状态并更新设备活动时间 | ||||
|         if (IsConnected()) | ||||
|         { | ||||
|             // 如果取消操作被请求,则返回中断状态 | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|             { | ||||
|                 return ThreadRunReturnTypeEnum.Break; | ||||
|             } | ||||
|  | ||||
|             // 如果标志为停止,则暂停执行 | ||||
|             if (Pause) | ||||
|             { | ||||
|                 // 暂停 | ||||
|                 return ThreadRunReturnTypeEnum.Continue; | ||||
|             } | ||||
|  | ||||
|             // 再次检查取消操作是否被请求 | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|             { | ||||
|                 return ThreadRunReturnTypeEnum.Break; | ||||
|             } | ||||
|  | ||||
|             // 获取设备连接状态并更新设备活动时间 | ||||
|             if (IsConnected()) | ||||
|             { | ||||
|                 CurrentDevice.SetDeviceStatus(TimerX.Now, false); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 CurrentDevice.SetDeviceStatus(TimerX.Now, true); | ||||
|             } | ||||
|  | ||||
|             // 再次检查取消操作是否被请求 | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|             { | ||||
|                 return ThreadRunReturnTypeEnum.Break; | ||||
|             } | ||||
|  | ||||
|             // 执行任务操作 | ||||
|             if (TimeTick.IsTickHappen()) | ||||
|                 await ProtectedExecuteAsync(cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|             // 再次检查取消操作是否被请求 | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|             { | ||||
|                 return ThreadRunReturnTypeEnum.Break; | ||||
|             } | ||||
|  | ||||
|             // 正常返回None状态 | ||||
|             return ThreadRunReturnTypeEnum.None; | ||||
|             CurrentDevice.SetDeviceStatus(TimerX.Now, false); | ||||
|         } | ||||
|         catch (OperationCanceledException) | ||||
|         else | ||||
|         { | ||||
|             return ThreadRunReturnTypeEnum.Break; | ||||
|         } | ||||
|         catch (ObjectDisposedException) | ||||
|         { | ||||
|             return ThreadRunReturnTypeEnum.Break; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             // 记录异常信息,并更新设备状态为异常 | ||||
|             LogMessage?.LogError(ex, "Execute"); | ||||
|             CurrentDevice.SetDeviceStatus(TimerX.Now, true, ex.Message); | ||||
|             return ThreadRunReturnTypeEnum.None; | ||||
|             CurrentDevice.SetDeviceStatus(TimerX.Now, true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal override Task StartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         TimeTick = new TimeTick(CurrentDevice.IntervalTime); | ||||
|         return base.StartAsync(cancellationToken); | ||||
|     } | ||||
|  | ||||
|     private TimeTick TimeTick; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
| using Mapster; | ||||
|  | ||||
| using ThingsGateway.Extension.Generic; | ||||
| using ThingsGateway.NewLife; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
|  | ||||
| @@ -22,9 +21,6 @@ namespace ThingsGateway.Gateway.Application; | ||||
| /// </summary> | ||||
| public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel, AlarmModel> : BusinessBaseWithCacheAlarmModel<VarModel, DevModel, AlarmModel> | ||||
| { | ||||
|     protected TimeTick _exT2TimerTick;  // 用于设备上传的时间间隔定时器 | ||||
|     protected TimeTick _exTTimerTick;   // 用于变量上传的时间间隔定时器 | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 业务属性 | ||||
|     /// </summary> | ||||
| @@ -37,10 +33,6 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel | ||||
|  | ||||
|     protected internal override async Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken) | ||||
|     { | ||||
|         // 初始化 | ||||
|         _exTTimerTick = new(_businessPropertyWithCacheInterval.BusinessInterval); | ||||
|         _exT2TimerTick = new(_businessPropertyWithCacheInterval.BusinessInterval); | ||||
|  | ||||
|         GlobalData.AlarmChangedEvent -= AlarmValueChange; | ||||
|         GlobalData.ReadOnlyRealAlarmIdVariables?.ForEach(a => | ||||
|         { | ||||
| @@ -145,69 +137,55 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel | ||||
|     /// <summary> | ||||
|     /// 间隔上传数据的方法 | ||||
|     /// </summary> | ||||
|     protected virtual async Task IntervalInsert(CancellationToken cancellationToken) | ||||
|     protected void IntervalInsert(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         while (!DisposedValue) | ||||
|         if (CurrentDevice.Pause == true) | ||||
|         { | ||||
|             if (CurrentDevice.Pause == true) | ||||
|             { | ||||
|                 await Task.Delay(1000, cancellationToken).ConfigureAwait(false); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // 如果业务属性的缓存为间隔上传,则根据定时器间隔执行相应操作 | ||||
|             if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Change) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     if (_exTTimerTick.IsTickHappen()) | ||||
|                     { | ||||
|                         if (LogMessage?.LogLevel <= LogLevel.Debug) | ||||
|                             LogMessage?.LogDebug($"Interval {typeof(VarModel).Name} data, count {IdVariableRuntimes.Count}"); | ||||
|                         // 间隔推送全部变量 | ||||
|                         var variableRuntimes = IdVariableRuntimes.Select(a => a.Value); | ||||
|                         VariableTimeInterval(variableRuntimes, variableRuntimes.Adapt<List<VariableBasicData>>()); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage?.LogWarning(ex, AppResource.IntervalInsertVariableFail); | ||||
|                 } | ||||
|                 try | ||||
|                 { | ||||
|                     if (_exT2TimerTick.IsTickHappen()) | ||||
|                     { | ||||
|                         if (CollectDevices != null) | ||||
|                         { | ||||
|                             if (LogMessage?.LogLevel <= LogLevel.Debug) | ||||
|                                 LogMessage?.LogDebug($"Interval {typeof(DevModel).Name} data, count {CollectDevices.Count}"); | ||||
|  | ||||
|                             // 间隔推送全部设备 | ||||
|                             foreach (var deviceRuntime in CollectDevices.Select(a => a.Value)) | ||||
|                             { | ||||
|                                 DeviceTimeInterval(deviceRuntime, deviceRuntime.Adapt<DeviceBasicData>()); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage?.LogWarning(ex, AppResource.IntervalInsertDeviceFail); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             await Task.Delay(100, cancellationToken).ConfigureAwait(false); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // 如果业务属性的缓存为间隔上传,则根据定时器间隔执行相应操作 | ||||
|         if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Change) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 if (LogMessage?.LogLevel <= LogLevel.Debug) | ||||
|                     LogMessage?.LogDebug($"Interval {typeof(VarModel).Name} data, count {IdVariableRuntimes.Count}"); | ||||
|                 // 间隔推送全部变量 | ||||
|                 var variableRuntimes = IdVariableRuntimes.Select(a => a.Value); | ||||
|                 VariableTimeInterval(variableRuntimes, variableRuntimes.Adapt<List<VariableBasicData>>()); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 LogMessage?.LogWarning(ex, AppResource.IntervalInsertVariableFail); | ||||
|             } | ||||
|             try | ||||
|             { | ||||
|                 if (CollectDevices != null) | ||||
|                 { | ||||
|                     if (LogMessage?.LogLevel <= LogLevel.Debug) | ||||
|                         LogMessage?.LogDebug($"Interval {typeof(DevModel).Name} data, count {CollectDevices.Count}"); | ||||
|  | ||||
|                     // 间隔推送全部设备 | ||||
|                     foreach (var deviceRuntime in CollectDevices.Select(a => a.Value)) | ||||
|                     { | ||||
|                         DeviceTimeInterval(deviceRuntime, deviceRuntime.Adapt<DeviceBasicData>()); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 LogMessage?.LogWarning(ex, AppResource.IntervalInsertDeviceFail); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 启动前异步方法 | ||||
|     /// </summary> | ||||
|     protected override Task ProtectedStartAsync(CancellationToken cancellationToken) | ||||
|     protected override List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken) | ||||
|     { | ||||
|         // 启动间隔上传的数据获取线程 | ||||
|         _ = IntervalInsert(cancellationToken); | ||||
|         return base.ProtectedStartAsync(cancellationToken); | ||||
|         var list = base.ProtectedGetTasks(cancellationToken); | ||||
|         list.Add(ScheduledTaskHelper.GetTask(_businessPropertyWithCacheInterval.BusinessInterval, IntervalInsert, null, LogMessage, cancellationToken)); | ||||
|         return list; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
| using Mapster; | ||||
|  | ||||
| using ThingsGateway.Extension.Generic; | ||||
| using ThingsGateway.NewLife; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
|  | ||||
| @@ -24,12 +23,6 @@ namespace ThingsGateway.Gateway.Application; | ||||
| /// <typeparam name="DevModel">设备数据类型</typeparam> | ||||
| public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevModel> : BusinessBaseWithCacheDeviceModel<VarModel, DevModel> | ||||
| { | ||||
|     // 用于控制设备上传的定时器 | ||||
|     protected TimeTick _exT2TimerTick; | ||||
|  | ||||
|     // 用于控制变量上传的定时器 | ||||
|     protected TimeTick _exTTimerTick; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取具体业务属性的缓存设置。 | ||||
|     /// </summary> | ||||
| @@ -44,12 +37,6 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode | ||||
|  | ||||
|     protected internal override async Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken) | ||||
|     { | ||||
|         // 初始化设备和变量上传的定时器 | ||||
|         _exTTimerTick = new(_businessPropertyWithCacheInterval.BusinessInterval); | ||||
|         _exT2TimerTick = new(_businessPropertyWithCacheInterval.BusinessInterval); | ||||
|  | ||||
|  | ||||
|  | ||||
|         // 如果不是间隔上传,则订阅全局变量值改变事件和设备状态改变事件,并触发一次事件处理 | ||||
|         if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval) | ||||
|         { | ||||
| @@ -126,77 +113,59 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 执行间隔插入任务的方法,用于定期上传设备和变量信息。 | ||||
|     /// </summary> | ||||
|     /// <returns>异步任务</returns> | ||||
|     protected virtual async Task IntervalInsert(CancellationToken cancellationToken) | ||||
|     { | ||||
|  | ||||
|         while (!cancellationToken.IsCancellationRequested) | ||||
|     /// <summary> | ||||
|     /// 间隔上传数据的方法 | ||||
|     /// </summary> | ||||
|     protected void IntervalInsert(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (CurrentDevice.Pause == true) | ||||
|         { | ||||
|             if (CurrentDevice.Pause == true) | ||||
|             { | ||||
|                 await Task.Delay(1000, cancellationToken).ConfigureAwait(false); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // 如果是间隔上传,根据定时器触发事件上传设备和变量信息 | ||||
|             if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Change) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     if (_exTTimerTick.IsTickHappen()) | ||||
|                     { | ||||
|                         if (LogMessage?.LogLevel <= LogLevel.Debug) | ||||
|                             LogMessage?.LogDebug($"Interval  {typeof(VarModel).Name}  data, count {IdVariableRuntimes.Count}"); | ||||
|                         // 上传所有变量信息 | ||||
|                         var variableRuntimes = IdVariableRuntimes.Select(a => a.Value); | ||||
|                         VariableTimeInterval(variableRuntimes, variableRuntimes.Adapt<List<VariableBasicData>>()); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage?.LogWarning(ex, AppResource.IntervalInsertVariableFail); | ||||
|                 } | ||||
|  | ||||
|                 try | ||||
|                 { | ||||
|                     if (_exT2TimerTick.IsTickHappen()) | ||||
|                     { | ||||
|                         if (CollectDevices != null) | ||||
|                         { | ||||
|                             if (LogMessage?.LogLevel <= LogLevel.Debug) | ||||
|                                 LogMessage?.LogDebug($"Interval  {typeof(DevModel).Name}  data, count {CollectDevices.Count}"); | ||||
|                             // 上传所有设备信息 | ||||
|                             foreach (var deviceRuntime in CollectDevices.Select(a => a.Value)) | ||||
|                             { | ||||
|                                 DeviceTimeInterval(deviceRuntime, deviceRuntime.Adapt<DeviceBasicData>()); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage?.LogWarning(ex, AppResource.IntervalInsertDeviceFail); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             await Task.Delay(100, cancellationToken).ConfigureAwait(false); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // 如果业务属性的缓存为间隔上传,则根据定时器间隔执行相应操作 | ||||
|         if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Change) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 if (LogMessage?.LogLevel <= LogLevel.Debug) | ||||
|                     LogMessage?.LogDebug($"Interval  {typeof(VarModel).Name}  data, count {IdVariableRuntimes.Count}"); | ||||
|                 // 上传所有变量信息 | ||||
|                 var variableRuntimes = IdVariableRuntimes.Select(a => a.Value); | ||||
|                 VariableTimeInterval(variableRuntimes, variableRuntimes.Adapt<List<VariableBasicData>>()); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 LogMessage?.LogWarning(ex, AppResource.IntervalInsertVariableFail); | ||||
|             } | ||||
|             try | ||||
|             { | ||||
|                 if (CollectDevices != null) | ||||
|                 { | ||||
|                     if (LogMessage?.LogLevel <= LogLevel.Debug) | ||||
|                         LogMessage?.LogDebug($"Interval  {typeof(DevModel).Name}  data, count {CollectDevices.Count}"); | ||||
|                     // 上传所有设备信息 | ||||
|                     foreach (var deviceRuntime in CollectDevices.Select(a => a.Value)) | ||||
|                     { | ||||
|                         DeviceTimeInterval(deviceRuntime, deviceRuntime.Adapt<DeviceBasicData>()); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 LogMessage?.LogWarning(ex, AppResource.IntervalInsertDeviceFail); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 在开始前的保护方法,异步执行间隔插入任务。 | ||||
|     /// </summary> | ||||
|     /// <param name="cancellationToken">取消令牌</param> | ||||
|     /// <returns>异步任务</returns> | ||||
|     protected override Task ProtectedStartAsync(CancellationToken cancellationToken) | ||||
|     protected override List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken) | ||||
|     { | ||||
|         _ = IntervalInsert(cancellationToken); | ||||
|         return base.ProtectedStartAsync(cancellationToken); | ||||
|         var list = base.ProtectedGetTasks(cancellationToken); | ||||
|         list.Add(ScheduledTaskHelper.GetTask(_businessPropertyWithCacheInterval.BusinessInterval, IntervalInsert, null, LogMessage, cancellationToken)); | ||||
|         return list; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 变量状态变化时发生的虚拟方法,用于处理变量状态变化事件。 | ||||
|     /// </summary> | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
| using Mapster; | ||||
|  | ||||
| using ThingsGateway.Extension.Generic; | ||||
| using ThingsGateway.NewLife; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
|  | ||||
| @@ -23,10 +22,6 @@ namespace ThingsGateway.Gateway.Application; | ||||
| /// <typeparam name="VarModel">变量模型类型</typeparam> | ||||
| public abstract class BusinessBaseWithCacheIntervalVariableModel<VarModel> : BusinessBaseWithCacheVariableModel<VarModel> | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 用于定时触发的时间间隔。 | ||||
|     /// </summary> | ||||
|     protected TimeTick _exTTimerTick; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取具体业务属性的缓存设置。 | ||||
| @@ -40,9 +35,6 @@ public abstract class BusinessBaseWithCacheIntervalVariableModel<VarModel> : Bus | ||||
|  | ||||
|     protected internal override async Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken) | ||||
|     { | ||||
|         // 初始化定时器 | ||||
|         _exTTimerTick = new TimeTick(_businessPropertyWithCacheInterval.BusinessInterval); | ||||
|  | ||||
|         // 注册变量值变化事件处理程序 | ||||
|         if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval) | ||||
|         { | ||||
| @@ -90,54 +82,39 @@ public abstract class BusinessBaseWithCacheIntervalVariableModel<VarModel> : Bus | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 间隔插入操作,用于周期性地插入变量。 | ||||
|     /// 间隔上传数据的方法 | ||||
|     /// </summary> | ||||
|     /// <returns>表示异步操作的任务</returns> | ||||
|     protected virtual async Task IntervalInsert(CancellationToken cancellationToken) | ||||
|     protected void IntervalInsert(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         while (!cancellationToken.IsCancellationRequested) | ||||
|         if (CurrentDevice.Pause == true) | ||||
|         { | ||||
|             if (CurrentDevice.Pause == true) | ||||
|             { | ||||
|                 await Task.Delay(1000, cancellationToken).ConfigureAwait(false); | ||||
|                 continue; | ||||
|             } | ||||
|             //间隔上传 | ||||
|             if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Change) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     if (_exTTimerTick.IsTickHappen()) | ||||
|                     { | ||||
|                         if (LogMessage?.LogLevel <= LogLevel.Debug) | ||||
|                             LogMessage?.LogDebug($"Interval  {typeof(VarModel).Name}  data, count {IdVariableRuntimes.Count}"); | ||||
|                         //间隔推送全部变量 | ||||
|                         var variableRuntimes = IdVariableRuntimes.Select(a => a.Value); | ||||
|                         VariableTimeInterval(variableRuntimes, variableRuntimes.Adapt<List<VariableBasicData>>()); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage?.LogWarning(ex, AppResource.IntervalInsertVariableFail); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             await Task.Delay(100, cancellationToken).ConfigureAwait(false); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // 如果业务属性的缓存为间隔上传,则根据定时器间隔执行相应操作 | ||||
|         if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Change) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 if (LogMessage?.LogLevel <= LogLevel.Debug) | ||||
|                     LogMessage?.LogDebug($"Interval  {typeof(VarModel).Name}  data, count {IdVariableRuntimes.Count}"); | ||||
|                 // 上传所有变量信息 | ||||
|                 var variableRuntimes = IdVariableRuntimes.Select(a => a.Value); | ||||
|                 VariableTimeInterval(variableRuntimes, variableRuntimes.Adapt<List<VariableBasicData>>()); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 LogMessage?.LogWarning(ex, AppResource.IntervalInsertVariableFail); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 在启动前执行的异步操作。 | ||||
|     /// </summary> | ||||
|     /// <param name="cancellationToken">取消令牌</param> | ||||
|     /// <returns>表示异步操作的任务</returns> | ||||
|     protected override Task ProtectedStartAsync(CancellationToken cancellationToken) | ||||
|     protected override List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken) | ||||
|     { | ||||
|         // 启动间隔插入操作 | ||||
|         _ = IntervalInsert(cancellationToken); | ||||
|         return base.ProtectedStartAsync(cancellationToken); | ||||
|         var list = base.ProtectedGetTasks(cancellationToken); | ||||
|         list.Add(ScheduledTaskHelper.GetTask(_businessPropertyWithCacheInterval.BusinessInterval, IntervalInsert, null, LogMessage, cancellationToken)); | ||||
|         return list; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|   | ||||
| @@ -89,7 +89,7 @@ public abstract class CollectBase : DriverBase, IRpcDriver | ||||
|             { | ||||
|                 var data = new VariableScriptRead(); | ||||
|                 data.VariableRuntime = a; | ||||
|                 data.TimeTick = new(a.IntervalTime ?? currentDevice.IntervalTime); | ||||
|                 data.IntervalTime = a.IntervalTime ?? currentDevice.IntervalTime; | ||||
|                 return data; | ||||
|             }).ToList(); | ||||
|  | ||||
| @@ -164,353 +164,233 @@ public abstract class CollectBase : DriverBase, IRpcDriver | ||||
|     { | ||||
|         return string.Empty; | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 循环任务 | ||||
|     /// </summary> | ||||
|     /// <param name="cancellationToken">取消操作的令牌。</param> | ||||
|     /// <returns>表示异步操作结果的枚举。</returns> | ||||
|     internal override async ValueTask<ThreadRunReturnTypeEnum> ExecuteAsync(CancellationToken cancellationToken) | ||||
|     protected virtual bool VariableSourceReadsEnable => true; | ||||
|     protected override List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         var tasks = new List<IScheduledTask>(); | ||||
|  | ||||
|         var setDeviceStatusTask = new ScheduledSyncTask(3000, SetDeviceStatus, null, LogMessage, cancellationToken); | ||||
|         tasks.Add(setDeviceStatusTask); | ||||
|  | ||||
|         var testOnline = new ScheduledAsyncTask(30000, TestOnline, null, LogMessage, cancellationToken); | ||||
|         tasks.Add(testOnline); | ||||
|  | ||||
|         if (VariableSourceReadsEnable) | ||||
|         { | ||||
|             // 如果取消操作被请求,则返回中断状态 | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|             for (int i = 0; i < CurrentDevice.VariableSourceReads.Count; i++) | ||||
|             { | ||||
|                 return ThreadRunReturnTypeEnum.Break; | ||||
|             } | ||||
|                 var variableSourceRead = CurrentDevice.VariableSourceReads[i]; | ||||
|  | ||||
|             // 如果标志为停止,则暂停执行 | ||||
|             if (Pause) | ||||
|             { | ||||
|                 // 暂停 | ||||
|                 return ThreadRunReturnTypeEnum.Continue; | ||||
|             } | ||||
|                 var executeTask = ScheduledTaskHelper.GetTask(variableSourceRead.IntervalTime, ReadVariableSource, variableSourceRead, LogMessage, cancellationToken); | ||||
|                 tasks.Add(executeTask); | ||||
|  | ||||
|             // 再次检查取消操作是否被请求 | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|             { | ||||
|                 return ThreadRunReturnTypeEnum.Break; | ||||
|             } | ||||
|  | ||||
|             // 获取设备连接状态并更新设备活动时间 | ||||
|             if (IsConnected()) | ||||
|             { | ||||
|                 CurrentDevice.SetDeviceStatus(TimerX.Now); | ||||
|             } | ||||
|  | ||||
|             // 再次检查取消操作是否被请求 | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|             { | ||||
|                 return ThreadRunReturnTypeEnum.Break; | ||||
|             } | ||||
|  | ||||
|             // 执行任务操作 | ||||
|             await ProtectedExecuteAsync(cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|             // 再次检查取消操作是否被请求 | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|             { | ||||
|                 return ThreadRunReturnTypeEnum.Break; | ||||
|             } | ||||
|  | ||||
|             // 正常返回None状态 | ||||
|             return ThreadRunReturnTypeEnum.None; | ||||
|         } | ||||
|         catch (OperationCanceledException) | ||||
|         { | ||||
|             return ThreadRunReturnTypeEnum.Break; | ||||
|         } | ||||
|         catch (ObjectDisposedException) | ||||
|         { | ||||
|             return ThreadRunReturnTypeEnum.Break; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|                 return ThreadRunReturnTypeEnum.Break; | ||||
|             // 记录异常信息,并更新设备状态为异常 | ||||
|             LogMessage?.LogError(ex, "Execute"); | ||||
|             CurrentDevice.SetDeviceStatus(TimerX.Now, true, ex.Message); | ||||
|             return ThreadRunReturnTypeEnum.None; | ||||
|         } | ||||
|     } | ||||
|     ReadResultCount readResultCount = new(); | ||||
|     /// <summary> | ||||
|     /// 执行读取等方法,如果插件不支持读取,而是自更新值的话,需重写此方法 | ||||
|     /// </summary> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             readResultCount.Reset(); | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|                 return; | ||||
|  | ||||
|             if (CollectProperties.MaxConcurrentCount > 1) | ||||
|             { | ||||
|                 // 并行处理每个变量读取 | ||||
|                 await CurrentDevice.VariableSourceReads.ParallelForEachAsync(async (variableSourceRead, cancellationToken) => | ||||
|                 { | ||||
|                     if (cancellationToken.IsCancellationRequested) | ||||
|                         return; | ||||
|                     if (await ReadVariableSource(readResultCount, variableSourceRead, cancellationToken).ConfigureAwait(false)) | ||||
|                         return; | ||||
|                 } | ||||
|                 , CollectProperties.MaxConcurrentCount, cancellationToken).ConfigureAwait(false); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 for (int i = 0; i < CurrentDevice.VariableSourceReads.Count; i++) | ||||
|                 { | ||||
|                     if (cancellationToken.IsCancellationRequested) | ||||
|                         return; | ||||
|                     if (await ReadVariableSource(readResultCount, CurrentDevice.VariableSourceReads[i], cancellationToken).ConfigureAwait(false)) | ||||
|                         return; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (CollectProperties.MaxConcurrentCount > 1) | ||||
|             { | ||||
|                 // 并行处理每个方法调用 | ||||
|                 await CurrentDevice.ReadVariableMethods.ParallelForEachAsync(async (readVariableMethods, cancellationToken) => | ||||
|                 { | ||||
|                     if (cancellationToken.IsCancellationRequested) | ||||
|                         return; | ||||
|                     if (await ReadVariableMed(readResultCount, readVariableMethods, cancellationToken).ConfigureAwait(false)) | ||||
|                         return; | ||||
|                 } | ||||
|             , CollectProperties.MaxConcurrentCount, cancellationToken).ConfigureAwait(false); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 for (int i = 0; i < CurrentDevice.ReadVariableMethods.Count; i++) | ||||
|                 { | ||||
|                     if (cancellationToken.IsCancellationRequested) | ||||
|                         return; | ||||
|                     if (await ReadVariableMed(readResultCount, CurrentDevice.ReadVariableMethods[i], cancellationToken).ConfigureAwait(false)) | ||||
|                         return; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // 如果所有方法和变量读取都成功,则清零错误计数器 | ||||
|             if (readResultCount.deviceMethodsVariableFailedNum == 0 && readResultCount.deviceSourceVariableFailedNum == 0 && (readResultCount.deviceMethodsVariableSuccessNum != 0 || readResultCount.deviceSourceVariableSuccessNum != 0)) | ||||
|             { | ||||
|                 //只有成功读取一次,失败次数都会清零 | ||||
|                 CurrentDevice.SetDeviceStatus(TimerX.Now, false); | ||||
|             } | ||||
|         } | ||||
|         finally | ||||
|  | ||||
|         for (int i = 0; i < CurrentDevice.ReadVariableMethods.Count; i++) | ||||
|         { | ||||
|             ScriptVariableRun(cancellationToken); | ||||
|             var variableMethod = CurrentDevice.ReadVariableMethods[i]; | ||||
|  | ||||
|             var executeTask = ScheduledTaskHelper.GetTask(variableMethod.IntervalTime, ReadVariableMed, variableMethod, LogMessage, cancellationToken); | ||||
|             tasks.Add(executeTask); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         for (int i = 0; i < CurrentDevice.VariableScriptReads.Count; i++) | ||||
|         { | ||||
|             var variableScriptRead = CurrentDevice.VariableScriptReads[i]; | ||||
|  | ||||
|             var executeTask = ScheduledTaskHelper.GetTask(variableScriptRead.IntervalTime, ScriptVariableRun, variableScriptRead, LogMessage, cancellationToken); | ||||
|             tasks.Add(executeTask); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return tasks; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private void SetDeviceStatus(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (IsConnected()) | ||||
|         { | ||||
|             CurrentDevice.SetDeviceStatus(TimerX.Now); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     #region private | ||||
|  | ||||
|     #region 执行方法 | ||||
|  | ||||
|     async ValueTask<bool> ReadVariableMed(ReadResultCount readResultCount, VariableMethod readVariableMethods, CancellationToken cancellationToken) | ||||
|     async Task ReadVariableMed(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (state is not VariableMethod readVariableMethods) return; | ||||
|         if (Pause) | ||||
|             return true; | ||||
|             return; | ||||
|         if (cancellationToken.IsCancellationRequested) | ||||
|             return true; | ||||
|             return; | ||||
|  | ||||
|         // 如果请求更新时间已到,则执行方法调用 | ||||
|         if (readVariableMethods.CheckIfRequestAndUpdateTime()) | ||||
|         var readErrorCount = 0; | ||||
|  | ||||
|         //if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|         //    LogMessage?.Trace(string.Format("{0} - Executing method [{1}]", DeviceName, readVariableMethods.MethodInfo.Name)); | ||||
|         var readResult = await InvokeMethodAsync(readVariableMethods, cancellationToken: cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|         // 方法调用失败时重试一定次数 | ||||
|         while (!readResult.IsSuccess && readErrorCount < CollectProperties.RetryCount) | ||||
|         { | ||||
|             if (Pause) | ||||
|                 return; | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|                 return; | ||||
|  | ||||
|             readErrorCount++; | ||||
|             if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                 LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - failed - {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage)); | ||||
|  | ||||
|             //if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|             //    LogMessage?.Trace(string.Format("{0} - Executing method [{1}]", DeviceName, readVariableMethods.MethodInfo.Name)); | ||||
|             readResult = await InvokeMethodAsync(readVariableMethods, cancellationToken: cancellationToken).ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         if (readResult.IsSuccess) | ||||
|         { | ||||
|             // 方法调用成功时记录日志并增加成功计数器 | ||||
|             if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                 LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - Succeeded {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.Content?.ToSystemTextJsonString())); | ||||
|             CurrentDevice.SetDeviceStatus(TimerX.Now, false); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|                 return true; | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|                 return true; | ||||
|             if (await TestOnline(cancellationToken).ConfigureAwait(false)) | ||||
|                 return true; | ||||
|             var readErrorCount = 0; | ||||
|                 return; | ||||
|  | ||||
|             if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                 LogMessage?.Trace(string.Format("{0} - Executing method [{1}]", DeviceName, readVariableMethods.MethodInfo.Name)); | ||||
|             var readResult = await InvokeMethodAsync(readVariableMethods, cancellationToken: cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|             // 方法调用失败时重试一定次数 | ||||
|             while (!readResult.IsSuccess && readErrorCount < CollectProperties.RetryCount) | ||||
|             // 方法调用失败时记录日志并增加失败计数器,更新错误信息 | ||||
|             if (readVariableMethods.LastErrorMessage != readResult.ErrorMessage) | ||||
|             { | ||||
|                 if (Pause) | ||||
|                     return true; | ||||
|                 if (cancellationToken.IsCancellationRequested) | ||||
|                     return true; | ||||
|                 if (await TestOnline(cancellationToken).ConfigureAwait(false)) | ||||
|                     return true; | ||||
|                 readErrorCount++; | ||||
|                 if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                     LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - failed - {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage)); | ||||
|  | ||||
|                 if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                     LogMessage?.Trace(string.Format("{0} - Executing method [{1}]", DeviceName, readVariableMethods.MethodInfo.Name)); | ||||
|                 readResult = await InvokeMethodAsync(readVariableMethods, cancellationToken: cancellationToken).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             if (readResult.IsSuccess) | ||||
|             { | ||||
|                 // 方法调用成功时记录日志并增加成功计数器 | ||||
|                 if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                     LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - Succeeded {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.Content?.ToSystemTextJsonString())); | ||||
|                 readResultCount.deviceMethodsVariableSuccessNum++; | ||||
|                 CurrentDevice.SetDeviceStatus(TimerX.Now, false); | ||||
|                 if (!cancellationToken.IsCancellationRequested) | ||||
|                     LogMessage?.LogWarning(readResult.Exception, string.Format(AppResource.MethodFail, DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (cancellationToken.IsCancellationRequested) | ||||
|                     return true; | ||||
|  | ||||
|                 // 方法调用失败时记录日志并增加失败计数器,更新错误信息 | ||||
|                 if (readVariableMethods.LastErrorMessage != readResult.ErrorMessage) | ||||
|                 if (!cancellationToken.IsCancellationRequested) | ||||
|                 { | ||||
|                     if (!cancellationToken.IsCancellationRequested) | ||||
|                         LogMessage?.LogWarning(readResult.Exception, string.Format(AppResource.MethodFail, DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage)); | ||||
|                     if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                         LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - failed - {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage)); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     if (!cancellationToken.IsCancellationRequested) | ||||
|                     { | ||||
|                         if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                             LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - failed - {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage)); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 readResultCount.deviceMethodsVariableFailedNum++; | ||||
|                 readVariableMethods.LastErrorMessage = readResult.ErrorMessage; | ||||
|                 CurrentDevice.SetDeviceStatus(TimerX.Now, false); | ||||
|             } | ||||
|  | ||||
|             readVariableMethods.LastErrorMessage = readResult.ErrorMessage; | ||||
|             CurrentDevice.SetDeviceStatus(TimerX.Now, false); | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     #endregion | ||||
|  | ||||
|     #region 执行默认读取 | ||||
|  | ||||
|     async ValueTask<bool> ReadVariableSource(ReadResultCount readResultCount, VariableSourceRead? variableSourceRead, CancellationToken cancellationToken) | ||||
|     async Task ReadVariableSource(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (state is not VariableSourceRead variableSourceRead) return; | ||||
|  | ||||
|         if (Pause) | ||||
|             return true; | ||||
|             return; | ||||
|         if (cancellationToken.IsCancellationRequested) | ||||
|             return true; | ||||
|         // 如果请求更新时间已到,则执行变量读取 | ||||
|         if (variableSourceRead.CheckIfRequestAndUpdateTime()) | ||||
|             return; | ||||
|  | ||||
|         var readErrorCount = 0; | ||||
|  | ||||
|         //if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|         //    LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length)); | ||||
|         var readResult = await ReadSourceAsync(variableSourceRead, cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|         // 读取失败时重试一定次数 | ||||
|         while (!readResult.IsSuccess && readErrorCount < CollectProperties.RetryCount) | ||||
|         { | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|                 return true; | ||||
|             if (Pause) | ||||
|                 return true; | ||||
|             if (await TestOnline(cancellationToken).ConfigureAwait(false)) | ||||
|                 return true; | ||||
|  | ||||
|             var readErrorCount = 0; | ||||
|                 return; | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|                 return; | ||||
|  | ||||
|             readErrorCount++; | ||||
|             if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                 LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length)); | ||||
|             var readResult = await ReadSourceAsync(variableSourceRead, cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|             // 读取失败时重试一定次数 | ||||
|             while (!readResult.IsSuccess && readErrorCount < CollectProperties.RetryCount) | ||||
|             { | ||||
|                 if (Pause) | ||||
|                     return true; | ||||
|                 if (cancellationToken.IsCancellationRequested) | ||||
|                     return true; | ||||
|                 if (await TestOnline(cancellationToken).ConfigureAwait(false)) | ||||
|                     return true; | ||||
|                 readErrorCount++; | ||||
|                 if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                     LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] failed - {3}", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage)); | ||||
|                 LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] failed - {3}", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage)); | ||||
|  | ||||
|  | ||||
|                 if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                     LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length)); | ||||
|                 readResult = await ReadSourceAsync(variableSourceRead, cancellationToken).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             if (readResult.IsSuccess) | ||||
|             { | ||||
|                 // 读取成功时记录日志并增加成功计数器 | ||||
|                 if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                     LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data succeeded {3}", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.Content?.ToHexString(' '))); | ||||
|                 readResultCount.deviceSourceVariableSuccessNum++; | ||||
|                 CurrentDevice.SetDeviceStatus(TimerX.Now, false); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 { | ||||
|                     if (cancellationToken.IsCancellationRequested) | ||||
|                         return true; | ||||
|  | ||||
|                     // 读取失败时记录日志并增加失败计数器,更新错误信息并清除变量状态 | ||||
|                     if (variableSourceRead.LastErrorMessage != readResult.ErrorMessage) | ||||
|                     { | ||||
|                         if (!cancellationToken.IsCancellationRequested) | ||||
|                             LogMessage?.LogWarning(readResult.Exception, string.Format(AppResource.CollectFail, DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage)); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         if (!cancellationToken.IsCancellationRequested) | ||||
|                         { | ||||
|                             if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                                 LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data failed - {3}", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage)); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     readResultCount.deviceSourceVariableFailedNum++; | ||||
|                     variableSourceRead.LastErrorMessage = readResult.ErrorMessage; | ||||
|                     CurrentDevice.SetDeviceStatus(TimerX.Now, true, readResult.ErrorMessage); | ||||
|                     var time = DateTime.Now; | ||||
|                     variableSourceRead.VariableRuntimes.ForEach(a => a.SetValue(null, time, isOnline: false)); | ||||
|                 } | ||||
|             } | ||||
|             //if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|             //    LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length)); | ||||
|             readResult = await ReadSourceAsync(variableSourceRead, cancellationToken).ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     #endregion | ||||
|  | ||||
|     #endregion | ||||
|  | ||||
|     protected virtual ValueTask<bool> TestOnline(CancellationToken cancellationToken) | ||||
|     { | ||||
|         return ValueTask.FromResult(false); | ||||
|     } | ||||
|  | ||||
|     protected void ScriptVariableRun(CancellationToken cancellationToken) | ||||
|     { | ||||
|         DateTime dateTime = TimerX.Now; | ||||
|         //特殊地址变量 | ||||
|         for (int i = 0; i < CurrentDevice.VariableScriptReads.Count; i++) | ||||
|         if (readResult.IsSuccess) | ||||
|         { | ||||
|             // 读取成功时记录日志并增加成功计数器 | ||||
|             if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                 LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data succeeded {3}", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.Content?.ToHexString(' '))); | ||||
|             CurrentDevice.SetDeviceStatus(TimerX.Now, false); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|                 return; | ||||
|  | ||||
|             if (CurrentDevice.VariableScriptReads[i].CheckIfRequestAndUpdateTime()) | ||||
|             // 读取失败时记录日志并增加失败计数器,更新错误信息并清除变量状态 | ||||
|             if (variableSourceRead.LastErrorMessage != readResult.ErrorMessage) | ||||
|             { | ||||
|  | ||||
|                 var variableRuntime = CurrentDevice.VariableScriptReads[i].VariableRuntime; | ||||
|                 if (variableRuntime.RegisterAddress.Equals(nameof(DeviceRuntime.DeviceStatus), StringComparison.OrdinalIgnoreCase)) | ||||
|                 { | ||||
|                     variableRuntime.SetValue(variableRuntime.DeviceRuntime.DeviceStatus, dateTime); | ||||
|                 } | ||||
|                 else if (variableRuntime.RegisterAddress.Equals("ScriptRead", StringComparison.OrdinalIgnoreCase)) | ||||
|                 { | ||||
|                     variableRuntime.SetValue(variableRuntime.Value, dateTime); | ||||
|                 } | ||||
|  | ||||
|                 if (!cancellationToken.IsCancellationRequested) | ||||
|                     LogMessage?.LogWarning(readResult.Exception, string.Format(AppResource.CollectFail, DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (!cancellationToken.IsCancellationRequested) | ||||
|                 { | ||||
|                     if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace) | ||||
|                         LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data failed - {3}", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage)); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             variableSourceRead.LastErrorMessage = readResult.ErrorMessage; | ||||
|             CurrentDevice.SetDeviceStatus(TimerX.Now, true, readResult.ErrorMessage); | ||||
|             var time = DateTime.Now; | ||||
|             variableSourceRead.VariableRuntimes.ForEach(a => a.SetValue(null, time, isOnline: false)); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     #endregion | ||||
|  | ||||
|     #endregion | ||||
|  | ||||
|     protected virtual Task TestOnline(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     protected void ScriptVariableRun(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         DateTime dateTime = TimerX.Now; | ||||
|         if (state is not VariableScriptRead variableScriptRead) return; | ||||
|         //特殊地址变量 | ||||
|  | ||||
|         if (cancellationToken.IsCancellationRequested) | ||||
|             return; | ||||
|         { | ||||
|  | ||||
|             var variableRuntime = variableScriptRead.VariableRuntime; | ||||
|             if (variableRuntime.RegisterAddress.Equals(nameof(DeviceRuntime.DeviceStatus), StringComparison.OrdinalIgnoreCase)) | ||||
|             { | ||||
|                 variableRuntime.SetValue(variableRuntime.DeviceRuntime.DeviceStatus, dateTime); | ||||
|             } | ||||
|             else if (variableRuntime.RegisterAddress.Equals("ScriptRead", StringComparison.OrdinalIgnoreCase)) | ||||
|             { | ||||
|                 variableRuntime.SetValue(variableRuntime.Value, dateTime); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 连读打包,返回实际通讯包信息<see cref="VariableSourceRead"/> | ||||
|     /// <br></br>每个驱动打包方法不一样,所以需要实现这个接口 | ||||
| @@ -540,21 +420,6 @@ public abstract class CollectBase : DriverBase, IRpcDriver | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private sealed class ReadResultCount | ||||
|     { | ||||
|         public int deviceMethodsVariableFailedNum = 0; | ||||
|         public int deviceMethodsVariableSuccessNum = 0; | ||||
|         public int deviceSourceVariableFailedNum = 0; | ||||
|         public int deviceSourceVariableSuccessNum = 0; | ||||
|         public void Reset() | ||||
|         { | ||||
|             deviceMethodsVariableFailedNum = 0; | ||||
|             deviceMethodsVariableSuccessNum = 0; | ||||
|             deviceSourceVariableFailedNum = 0; | ||||
|             deviceSourceVariableSuccessNum = 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #region 写入方法 | ||||
|  | ||||
|     /// <summary> | ||||
|   | ||||
| @@ -68,10 +68,8 @@ public abstract class CollectFoundationBase : CollectBase | ||||
|     } | ||||
|  | ||||
|  | ||||
|     protected override async ValueTask<bool> TestOnline(CancellationToken cancellationToken) | ||||
|     protected override async Task TestOnline(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         //设备无法连接时 | ||||
|         // 检查协议是否为空,如果为空则抛出异常 | ||||
|         if (FoundationDevice != null) | ||||
|         { | ||||
|             if (FoundationDevice.OnLine == false) | ||||
| @@ -79,7 +77,6 @@ public abstract class CollectFoundationBase : CollectBase | ||||
|                 Exception exception = null; | ||||
|                 try | ||||
|                 { | ||||
|                     await Task.Delay(1000, cancellationToken).ConfigureAwait(false); | ||||
|                     if (!cancellationToken.IsCancellationRequested) | ||||
|                     { | ||||
|                         await FoundationDevice.Channel.ConnectAsync(FoundationDevice.Channel.ChannelOptions.ConnectTimeout, cancellationToken).ConfigureAwait(false); | ||||
| @@ -91,7 +88,7 @@ public abstract class CollectFoundationBase : CollectBase | ||||
|                 } | ||||
|                 if (cancellationToken.IsCancellationRequested) | ||||
|                 { | ||||
|                     return true; | ||||
|                     return; | ||||
|                 } | ||||
|                 if (FoundationDevice.OnLine == false && exception != null) | ||||
|                 { | ||||
| @@ -120,13 +117,12 @@ public abstract class CollectFoundationBase : CollectBase | ||||
|                         item.Variable.SetValue(null, time, isOnline: false); | ||||
|                     } | ||||
|  | ||||
|                     await Task.Delay(3000, cancellationToken).ConfigureAwait(false); | ||||
|                     return true; | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -144,9 +140,6 @@ public abstract class CollectFoundationBase : CollectBase | ||||
|             // 从协议读取数据 | ||||
|             var read = await FoundationDevice.ReadAsync(variableSourceRead.RegisterAddress, variableSourceRead.Length, cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|             // 增加变量源的读取次数 | ||||
|             Interlocked.Increment(ref variableSourceRead.ReadCount); | ||||
|  | ||||
|             // 如果读取成功且有有效内容,则解析结构化内容 | ||||
|             if (read.IsSuccess) | ||||
|             { | ||||
| @@ -216,12 +209,4 @@ public abstract class CollectFoundationBase : CollectBase | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private sealed class ReadResultCount | ||||
|     { | ||||
|         public int deviceMethodsVariableFailedNum = 0; | ||||
|         public int deviceMethodsVariableSuccessNum = 0; | ||||
|         public int deviceSourceVariableFailedNum = 0; | ||||
|         public int deviceSourceVariableSuccessNum = 0; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -40,12 +40,6 @@ public abstract class CollectPropertyBase : DriverPropertyBase | ||||
| /// </summary> | ||||
| public abstract class CollectPropertyRetryBase : CollectPropertyBase | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 离线后恢复运行的间隔时间 | ||||
|     /// </summary> | ||||
|     [DynamicProperty] | ||||
|     public override int ReIntervalTime { get; set; } = 0; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 失败重试次数,默认3 | ||||
|     /// </summary> | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
| using BootstrapBlazor.Components; | ||||
|  | ||||
| using Microsoft.Extensions.Localization; | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using System.Text; | ||||
|  | ||||
| @@ -20,6 +21,8 @@ using ThingsGateway.Razor; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
|  | ||||
| using LogLevel = TouchSocket.Core.LogLevel; | ||||
|  | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
|  | ||||
| /// <summary> | ||||
| @@ -124,6 +127,11 @@ public abstract class DriverBase : DisposableObject, IDriver | ||||
|             if (CurrentDevice == null) return; | ||||
|             LogMessage?.LogInformation(pause == true ? string.Format(AppResource.DeviceTaskPause, DeviceName) : string.Format(AppResource.DeviceTaskContinue, DeviceName)); | ||||
|             CurrentDevice.Pause = pause; | ||||
|  | ||||
|             if (CurrentDevice.Pause) | ||||
|                 TaskSchedulerLoop.Stop(); | ||||
|             else | ||||
|                 TaskSchedulerLoop.Start(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -233,7 +241,7 @@ public abstract class DriverBase : DisposableObject, IDriver | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 在循环任务开始之前 | ||||
|     /// 在任务开始之前 | ||||
|     /// </summary> | ||||
|     /// <param name="cancellationToken">取消操作的令牌。</param> | ||||
|     /// <returns>表示异步操作的任务。</returns> | ||||
| @@ -289,15 +297,36 @@ public abstract class DriverBase : DisposableObject, IDriver | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected internal TaskSchedulerLoop TaskSchedulerLoop; | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 循环任务 | ||||
|     /// 获取任务 | ||||
|     /// </summary> | ||||
|     /// <param name="cancellationToken">取消操作的令牌。</param> | ||||
|     /// <returns>表示异步操作结果的枚举。</returns> | ||||
|     internal abstract ValueTask<ThreadRunReturnTypeEnum> ExecuteAsync(CancellationToken cancellationToken); | ||||
|     internal virtual TaskSchedulerLoop GetTasks(CancellationToken cancellationToken) | ||||
|     { | ||||
|         TaskSchedulerLoop = new(ProtectedGetTasks(cancellationToken)); | ||||
|  | ||||
|  | ||||
|         //var count = GlobalData.ChannelThreadManage.DeviceThreadManages.Select(a => a.Value.TaskCount).Sum(); | ||||
|         //ThreadPool.GetMinThreads(out var wt, out var io); | ||||
|         //if (wt < count + 128) | ||||
|         //{ | ||||
|         //    wt = count + 256; | ||||
|         //    ThreadPool.SetMinThreads(wt, io); | ||||
|         //    GlobalData.GatewayMonitorHostedService.Logger.LogInformation($"set min threads count {wt}, device tasks count {count}"); | ||||
|         //} | ||||
|  | ||||
|  | ||||
|         return TaskSchedulerLoop; | ||||
|     } | ||||
|  | ||||
|     protected abstract List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 已停止循环任务,释放插件 | ||||
|     /// 已停止任务,释放插件 | ||||
|     /// </summary> | ||||
|     internal virtual void Stop() | ||||
|     { | ||||
| @@ -423,12 +452,5 @@ public abstract class DriverBase : DisposableObject, IDriver | ||||
|     /// </summary> | ||||
|     public abstract Task AfterVariablesChangedAsync(CancellationToken cancellationToken); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 间隔执行 | ||||
|     /// </summary> | ||||
|     protected abstract Task ProtectedExecuteAsync(CancellationToken cancellationToken); | ||||
|  | ||||
|  | ||||
|  | ||||
|     #endregion 插件重写 | ||||
| } | ||||
|   | ||||
| @@ -298,11 +298,9 @@ | ||||
|   }, | ||||
|   "ThingsGateway.Gateway.Application.CollectPropertyBase": { | ||||
|     "ConcurrentCount": "ConcurrentCount", | ||||
|     "ReIntervalTime": "ReIntervalTime", | ||||
|     "RetryCount": "RetryCount" | ||||
|   }, | ||||
|   "ThingsGateway.Gateway.Application.CollectPropertyRetryBase": { | ||||
|     "ReIntervalTime": "ReIntervalTime", | ||||
|     "RetryCount": "RetryCount" | ||||
|   }, | ||||
|   "ThingsGateway.Gateway.Application.ControlController": { | ||||
|   | ||||
| @@ -297,11 +297,9 @@ | ||||
|   }, | ||||
|   "ThingsGateway.Gateway.Application.CollectPropertyBase": { | ||||
|     "ConcurrentCount": "最大并发数量", | ||||
|     "ReIntervalTime": "离线恢复时间", | ||||
|     "RetryCount": "失败重试次数" | ||||
|   }, | ||||
|   "ThingsGateway.Gateway.Application.CollectPropertyRetryBase": { | ||||
|     "ReIntervalTime": "离线恢复时间", | ||||
|     "RetryCount": "失败重试次数" | ||||
|   }, | ||||
|   "ThingsGateway.Gateway.Application.ControlController": { | ||||
|   | ||||
| @@ -8,8 +8,6 @@ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using ThingsGateway.NewLife; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
|  | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
| @@ -19,16 +17,14 @@ namespace ThingsGateway.Gateway.Application; | ||||
| /// </summary> | ||||
| public class VariableMethod | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 间隔时间实现 | ||||
|     /// </summary> | ||||
|     private readonly TimeTick _timeTick; | ||||
|  | ||||
|     public readonly string IntervalTime; | ||||
|  | ||||
|     private object?[]? OS; | ||||
|  | ||||
|     public VariableMethod(Method method, VariableRuntime variable, string delay) | ||||
|     { | ||||
|         _timeTick = new TimeTick(delay); | ||||
|         IntervalTime = delay; | ||||
|         MethodInfo = method; | ||||
|         Variable = variable; | ||||
|         variable.VariableMethod = this; | ||||
| @@ -49,12 +45,6 @@ public class VariableMethod | ||||
|     /// </summary> | ||||
|     public VariableRuntime Variable { get; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 检测是否达到读取间隔 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public bool CheckIfRequestAndUpdateTime() => _timeTick.IsTickHappen(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 执行方法 | ||||
|     /// </summary> | ||||
|   | ||||
| @@ -102,6 +102,17 @@ public partial class VariableRuntime : Variable, IVariable, IDisposable | ||||
|         return new(); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 设置变量值与时间/质量戳 | ||||
|     /// </summary> | ||||
|     /// <param name="dateTime"></param> | ||||
|     public void SetNoChangedValue(DateTime dateTime) | ||||
|     { | ||||
|         DateTime time = dateTime != default ? dateTime : DateTime.Now; | ||||
|         CollectTime = time; | ||||
|         GlobalData.VariableCollectChange(this); | ||||
|     } | ||||
|  | ||||
|     private void Set(object data, DateTime dateTime) | ||||
|     { | ||||
|         DateTime time = dateTime != default ? dateTime : DateTime.Now; | ||||
| @@ -159,7 +170,6 @@ public partial class VariableRuntime : Variable, IVariable, IDisposable | ||||
|         GlobalData.VariableCollectChange(this); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void Init(DeviceRuntime deviceRuntime) | ||||
|     { | ||||
|  | ||||
|   | ||||
| @@ -8,8 +8,6 @@ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using ThingsGateway.NewLife; | ||||
|  | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
|  | ||||
| /// <summary> | ||||
| @@ -17,28 +15,13 @@ namespace ThingsGateway.Gateway.Application; | ||||
| /// </summary> | ||||
| public class VariableScriptRead | ||||
| { | ||||
|     public long ReadCount { get; set; } | ||||
|     /// <summary> | ||||
|     /// 间隔时间实现 | ||||
|     /// </summary> | ||||
|     public TimeTick TimeTick { get; set; } | ||||
|  | ||||
|     public string IntervalTime { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 需分配的变量列表 | ||||
|     /// </summary> | ||||
|     public VariableRuntime VariableRuntime; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 检测是否达到读取间隔 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public bool CheckIfRequestAndUpdateTime() | ||||
|     { | ||||
|         var result = TimeTick.IsTickHappen(); | ||||
|         if (result) | ||||
|         { | ||||
|             ReadCount++; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -8,8 +8,6 @@ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using ThingsGateway.NewLife; | ||||
|  | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
|  | ||||
| /// <summary> | ||||
| @@ -17,10 +15,6 @@ namespace ThingsGateway.Gateway.Application; | ||||
| /// </summary> | ||||
| public class VariableSourceRead : IVariableSource | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 读取次数 | ||||
|     /// </summary> | ||||
|     public ulong ReadCount; | ||||
|  | ||||
|     private List<IVariable> _variableRuntimes = new List<IVariable>(); | ||||
|  | ||||
| @@ -39,10 +33,7 @@ public class VariableSourceRead : IVariableSource | ||||
|     /// </summary> | ||||
|     public string RegisterAddress { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 间隔时间实现 | ||||
|     /// </summary> | ||||
|     public TimeTick TimeTick { get; set; } | ||||
|     public string IntervalTime { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 需分配的变量列表 | ||||
| @@ -66,17 +57,4 @@ public class VariableSourceRead : IVariableSource | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 检测是否达到读取间隔 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public bool CheckIfRequestAndUpdateTime() | ||||
|     { | ||||
|         var result = TimeTick.IsTickHappen(); | ||||
|         if (result) | ||||
|         { | ||||
|             ReadCount++; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -207,8 +207,9 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage | ||||
|     /// <summary> | ||||
|     /// 任务 | ||||
|     /// </summary> | ||||
|     internal ConcurrentDictionary<long, DoTask> DriverTasks { get; set; } = new(); | ||||
|     internal ConcurrentDictionary<long, TaskSchedulerLoop> DriverTasks { get; } = new(); | ||||
|  | ||||
|     public int TaskCount => DriverTasks.Count; | ||||
|     /// <summary> | ||||
|     /// 取消令箭列表 | ||||
|     /// </summary> | ||||
| @@ -388,17 +389,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage | ||||
|                 token.Register(driver.Stop); | ||||
|  | ||||
|  | ||||
|                 if (driver.IsInitSuccess) | ||||
|                 { | ||||
|  | ||||
|                     // 初始化业务线程 | ||||
|                     var driverTask = new DoTask(DoWork, driver.LogMessage, driver); | ||||
|                     DriverTasks.TryAdd(driver.DeviceId, driverTask); | ||||
|  | ||||
|  | ||||
|                     driverTask.Start(token); | ||||
|  | ||||
|                 } | ||||
|                 _ = Task.Factory.StartNew((state) => DriverStart(state, token), driver, token); | ||||
|  | ||||
|             }).ConfigureAwait(false); | ||||
|  | ||||
| @@ -466,33 +457,33 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage | ||||
|         try | ||||
|         { | ||||
|             ConcurrentList<VariableRuntime> saveVariableRuntimes = new(); | ||||
|             await deviceIds.ParallelForEachAsync(async (deviceId, cancellationToken) => | ||||
|             { | ||||
|                 // 查找具有指定设备ID的驱动程序对象 | ||||
|                 if (Drivers.TryRemove(deviceId, out var driver)) | ||||
|                 { | ||||
|                     if (IsCollectChannel == true) | ||||
|                     { | ||||
|                         saveVariableRuntimes.AddRange(driver.IdVariableRuntimes.Where(a => a.Value.SaveValue && !a.Value.DynamicVariable).Select(a => a.Value)); | ||||
|                     } | ||||
|                 } | ||||
|             deviceIds.ParallelForEach((deviceId) => | ||||
|            { | ||||
|                // 查找具有指定设备ID的驱动程序对象 | ||||
|                if (Drivers.TryRemove(deviceId, out var driver)) | ||||
|                { | ||||
|                    if (IsCollectChannel == true) | ||||
|                    { | ||||
|                        saveVariableRuntimes.AddRange(driver.IdVariableRuntimes.Where(a => a.Value.SaveValue && !a.Value.DynamicVariable).Select(a => a.Value)); | ||||
|                    } | ||||
|                } | ||||
|  | ||||
|                 // 取消驱动程序的操作 | ||||
|                 if (CancellationTokenSources.TryRemove(deviceId, out var token)) | ||||
|                 { | ||||
|                     if (token != null) | ||||
|                     { | ||||
|                         token.Cancel(); | ||||
|                         token.Dispose(); | ||||
|                     } | ||||
|                 } | ||||
|                // 取消驱动程序的操作 | ||||
|                if (CancellationTokenSources.TryRemove(deviceId, out var token)) | ||||
|                { | ||||
|                    if (token != null) | ||||
|                    { | ||||
|                        token.Cancel(); | ||||
|                        token.Dispose(); | ||||
|                    } | ||||
|                } | ||||
|  | ||||
|                 if (DriverTasks.TryRemove(deviceId, out var task)) | ||||
|                 { | ||||
|                     await task.StopAsync().ConfigureAwait(false); | ||||
|                 } | ||||
|                if (DriverTasks.TryRemove(deviceId, out var task)) | ||||
|                { | ||||
|                    task.Stop(); | ||||
|                } | ||||
|  | ||||
|             }).ConfigureAwait(false); | ||||
|            }); | ||||
|  | ||||
|  | ||||
|             await Task.Delay(100).ConfigureAwait(false); | ||||
| @@ -543,7 +534,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage | ||||
|         return driver; | ||||
|     } | ||||
|  | ||||
|     private static async Task DoWork(object? state, CancellationToken token) | ||||
|     private async Task DriverStart(object? state, CancellationToken token) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
| @@ -554,41 +545,12 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage | ||||
|                 if (!driver.IsStarted) | ||||
|                     await driver.StartAsync(token).ConfigureAwait(false); | ||||
|  | ||||
|                 var result = await driver.ExecuteAsync(token).ConfigureAwait(false); // 执行驱动的异步执行操作 | ||||
|                 var driverTask = driver.GetTasks(token); // 执行驱动的异步执行操作 | ||||
|  | ||||
|                 DriverTasks.TryAdd(driver.DeviceId, driverTask); | ||||
|  | ||||
|                 driverTask.Start(); | ||||
|  | ||||
|                 // 根据执行结果进行不同的处理 | ||||
|                 if (result == ThreadRunReturnTypeEnum.None) | ||||
|                 { | ||||
|                     // 如果驱动处于离线状态且为采集驱动,则根据配置的间隔时间进行延迟 | ||||
|                     if (driver.CurrentDevice.DeviceStatus == DeviceStatusEnum.OffLine && driver.IsCollectDevice == true) | ||||
|                     { | ||||
|                         var collectBase = (CollectBase)driver; | ||||
|                         if (collectBase.CollectProperties.ReIntervalTime > 0) | ||||
|                         { | ||||
|                             await Task.Delay(Math.Max(Math.Min(collectBase.CollectProperties.ReIntervalTime, ManageHelper.ChannelThreadOptions.CheckInterval / 2) - CycleInterval, 3000), token).ConfigureAwait(false); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             await Task.Delay(CycleInterval, token).ConfigureAwait(false); | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         await Task.Delay(CycleInterval, token).ConfigureAwait(false); | ||||
|                     } | ||||
|                 } | ||||
|                 else if (result == ThreadRunReturnTypeEnum.Continue) | ||||
|                 { | ||||
|                     await Task.Delay(1000, token).ConfigureAwait(false); // 如果执行结果为继续,则延迟一段较短的时间后再继续执行 | ||||
|                 } | ||||
|                 else if (result == ThreadRunReturnTypeEnum.Break && token.IsCancellationRequested) | ||||
|                 { | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 await Task.Delay(60000, token).ConfigureAwait(false); | ||||
|             } | ||||
|         } | ||||
|         catch (OperationCanceledException) | ||||
| @@ -826,10 +788,14 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|  | ||||
|  | ||||
|                 //检测设备线程假死 | ||||
|                 await Task.Delay(ManageHelper.ChannelThreadOptions.CheckInterval, cancellationToken).ConfigureAwait(false); | ||||
|                 if (Disposed) return; | ||||
|  | ||||
|  | ||||
|  | ||||
|                 var num = Drivers.Count; | ||||
|                 foreach (var driver in Drivers.Select(a => a.Value).ToList()) | ||||
|                 { | ||||
|   | ||||
| @@ -21,6 +21,7 @@ public interface IDeviceThreadManage : IAsyncDisposable | ||||
|     string LogPath { get; } | ||||
|     IChannelThreadManage ChannelThreadManage { get; } | ||||
|     IChannel? Channel { get; } | ||||
|     int TaskCount { get; } | ||||
|  | ||||
|     Task SetLogAsync(LogLevel? logLevel = null, bool upDataBase = true); | ||||
|     Task RestartDeviceAsync(DeviceRuntime deviceRuntime, bool deleteCache); | ||||
|   | ||||
| @@ -21,11 +21,11 @@ namespace ThingsGateway.Gateway.Application; | ||||
| /// </summary> | ||||
| internal sealed class GatewayMonitorHostedService : BackgroundService, IGatewayMonitorHostedService | ||||
| { | ||||
|     private readonly ILogger _logger; | ||||
|     public ILogger Logger { get; } | ||||
|     /// <inheritdoc cref="AlarmHostedService"/> | ||||
|     public GatewayMonitorHostedService(ILogger<GatewayMonitorHostedService> logger, IStringLocalizer<GatewayMonitorHostedService> localizer, IChannelThreadManage channelThreadManage) | ||||
|     { | ||||
|         _logger = logger; | ||||
|         Logger = logger; | ||||
|         Localizer = localizer; | ||||
|         ChannelThreadManage = channelThreadManage; | ||||
|     } | ||||
| @@ -67,7 +67,7 @@ internal sealed class GatewayMonitorHostedService : BackgroundService, IGatewayM | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     _logger.LogWarning(ex, "Init Channel"); | ||||
|                     Logger.LogWarning(ex, "Init Channel"); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -80,7 +80,7 @@ internal sealed class GatewayMonitorHostedService : BackgroundService, IGatewayM | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             _logger.LogWarning(ex, "Start error"); | ||||
|             Logger.LogWarning(ex, "Start error"); | ||||
|         } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -9,9 +9,11 @@ | ||||
| // ------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.Extensions.Hosting; | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
|  | ||||
| public interface IGatewayMonitorHostedService : IHostedService | ||||
| { | ||||
|     public ILogger Logger { get; } | ||||
| } | ||||
|   | ||||
| @@ -22,7 +22,7 @@ public interface IActuatorNode : INode | ||||
|  | ||||
| public interface ITriggerNode : INode | ||||
| { | ||||
|     public Task StartAsync(Func<NodeOutput, Task> func); | ||||
|     public Task StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken); | ||||
| } | ||||
| public interface IExexcuteExpressionsBase | ||||
| { | ||||
|   | ||||
| @@ -13,8 +13,8 @@ public class AlarmChangedTriggerNode : VariableNode, ITriggerNode, IDisposable | ||||
|     public AlarmChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "AlarmChangedTriggerNode"; } | ||||
|  | ||||
|  | ||||
|     private Func<NodeOutput, Task> Func { get; set; } | ||||
|     Task ITriggerNode.StartAsync(Func<NodeOutput, Task> func) | ||||
|     private Func<NodeOutput, CancellationToken, Task> Func { get; set; } | ||||
|     Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken) | ||||
|     { | ||||
|         Func = func; | ||||
|         FuncDict.TryAdd(this, func); | ||||
| @@ -43,12 +43,12 @@ public class AlarmChangedTriggerNode : VariableNode, ITriggerNode, IDisposable | ||||
|  | ||||
|     public static ConcurrentDictionary<string, ConcurrentDictionary<string, ConcurrentList<AlarmChangedTriggerNode>>> AlarmChangedTriggerNodeDict = new(); | ||||
|  | ||||
|     public static ConcurrentDictionary<AlarmChangedTriggerNode, Func<NodeOutput, Task>> FuncDict = new(); | ||||
|     public static ConcurrentDictionary<AlarmChangedTriggerNode, Func<NodeOutput, CancellationToken, Task>> FuncDict = new(); | ||||
|  | ||||
|     public static BlockingCollection<AlarmVariable> AlarmVariables = new(); | ||||
|     static AlarmChangedTriggerNode() | ||||
|     { | ||||
|         _ = RunAsync(); | ||||
|         Task.Factory.StartNew(RunAsync); | ||||
|         GlobalData.AlarmChangedEvent -= AlarmHostedService_OnAlarmChanged; | ||||
|         GlobalData.ReadOnlyRealAlarmIdVariables?.ForEach(a => | ||||
|         { | ||||
| @@ -88,7 +88,7 @@ public class AlarmChangedTriggerNode : VariableNode, ITriggerNode, IDisposable | ||||
|                                if (FuncDict.TryGetValue(item, out var func)) | ||||
|                                { | ||||
|                                    item.Logger?.Trace($"Alarm changed: {item.Text}"); | ||||
|                                    await func.Invoke(new NodeOutput() { Value = alarmVariable }).ConfigureAwait(false); | ||||
|                                    await func.Invoke(new NodeOutput() { Value = alarmVariable }, token).ConfigureAwait(false); | ||||
|                                } | ||||
|                            } | ||||
|                            catch (Exception ex) | ||||
|   | ||||
| @@ -13,8 +13,8 @@ public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable | ||||
|     public DeviceChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "DeviceChangedTriggerNode"; Placeholder = "Device.Placeholder"; } | ||||
|  | ||||
|  | ||||
|     private Func<NodeOutput, Task> Func { get; set; } | ||||
|     Task ITriggerNode.StartAsync(Func<NodeOutput, Task> func) | ||||
|     private Func<NodeOutput, CancellationToken, Task> Func { get; set; } | ||||
|     Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken) | ||||
|     { | ||||
|         Func = func; | ||||
|         FuncDict.Add(this, func); | ||||
| @@ -31,13 +31,13 @@ public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|     public static Dictionary<string, ConcurrentList<DeviceChangedTriggerNode>> DeviceChangedTriggerNodeDict = new(); | ||||
|     public static Dictionary<DeviceChangedTriggerNode, Func<NodeOutput, Task>> FuncDict = new(); | ||||
|     public static Dictionary<DeviceChangedTriggerNode, Func<NodeOutput, CancellationToken, Task>> FuncDict = new(); | ||||
|  | ||||
|     public static BlockingCollection<DeviceBasicData> DeviceDatas = new(); | ||||
|  | ||||
|     static DeviceChangedTriggerNode() | ||||
|     { | ||||
|         _ = RunAsync(); | ||||
|         Task.Factory.StartNew(RunAsync); | ||||
|         GlobalData.DeviceStatusChangeEvent += GlobalData_DeviceStatusChangeEvent; | ||||
|     } | ||||
|  | ||||
| @@ -71,7 +71,7 @@ public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable | ||||
|                              if (FuncDict.TryGetValue(item, out var func)) | ||||
|                              { | ||||
|                                  item.Logger?.Trace($"Device changed: {item.Text}"); | ||||
|                                  await func.Invoke(new NodeOutput() { Value = deviceDatas }).ConfigureAwait(false); | ||||
|                                  await func.Invoke(new NodeOutput() { Value = deviceDatas }, token).ConfigureAwait(false); | ||||
|  | ||||
|                              } | ||||
|                          } | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
|  | ||||
| using ThingsGateway.Blazor.Diagrams.Core.Geometry; | ||||
| using ThingsGateway.Blazor.Diagrams.Core.Geometry; | ||||
| using ThingsGateway.NewLife; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
| @@ -9,12 +8,15 @@ namespace ThingsGateway.Gateway.Application; | ||||
| [CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/TimeInterval.svg", Desc = nameof(TimeIntervalTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.DefaultDiagram), WidgetType = "ThingsGateway.Gateway.Razor.TextWidget,ThingsGateway.Gateway.Razor")] | ||||
| public class TimeIntervalTriggerNode : TextNode, ITriggerNode, IDisposable | ||||
| { | ||||
|     ~TimeIntervalTriggerNode() | ||||
|     { | ||||
|         this.SafeDispose(); | ||||
|     } | ||||
|     public TimeIntervalTriggerNode(string id, Point? position = null) : base(id, position) { Title = "TimeIntervalTriggerNode"; Placeholder = "TimeIntervalTriggerNode.Placeholder"; } | ||||
|  | ||||
|     private TimeTick TimeTick; | ||||
|     private Func<NodeOutput, Task> Func { get; set; } | ||||
|     private bool Disposed; | ||||
|     Task ITriggerNode.StartAsync(Func<NodeOutput, Task> func) | ||||
|     private IScheduledTask _task; | ||||
|     private Func<NodeOutput, CancellationToken, Task> Func { get; set; } | ||||
|     Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken) | ||||
|     { | ||||
|         Func = func; | ||||
|         if (int.TryParse(Text, out int delay)) | ||||
| @@ -22,40 +24,32 @@ public class TimeIntervalTriggerNode : TextNode, ITriggerNode, IDisposable | ||||
|             if (delay <= 500) | ||||
|                 Text = "500"; | ||||
|         } | ||||
|         TimeTick = new TimeTick(Text); | ||||
|         _ = Timer(); | ||||
|         _task = ScheduledTaskHelper.GetTask(Text, Timer, null, Logger, cancellationToken); | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     private async Task Timer() | ||||
|     private async Task Timer(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         while (!Disposed) | ||||
|         try | ||||
|         { | ||||
|             try | ||||
|             if (Func != null) | ||||
|             { | ||||
|                 if (TimeTick.IsTickHappen()) | ||||
|                 { | ||||
|                     if (Func != null) | ||||
|                     { | ||||
|                         Logger?.Trace($"Timer: {Text}"); | ||||
|                         await Func.Invoke(new NodeOutput() { Value = TimeTick.LastTime }).ConfigureAwait(false); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 Logger?.LogWarning(ex); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 await Task.Delay(100).ConfigureAwait(false); | ||||
|                 Logger?.Trace($"Timer: {Text}"); | ||||
|                 await Func.Invoke(new NodeOutput() { }, cancellationToken).ConfigureAwait(false); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             Logger?.LogWarning(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void Dispose() | ||||
|     { | ||||
|         Disposed = true; | ||||
|         _task?.Stop(); | ||||
|         _task.TryDispose(); | ||||
|  | ||||
|         GC.SuppressFinalize(this); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -12,8 +12,8 @@ public class ValueChangedTriggerNode : VariableNode, ITriggerNode, IDisposable | ||||
| { | ||||
|     public ValueChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "ValueChangedTriggerNode"; } | ||||
|  | ||||
|     private Func<NodeOutput, Task> Func { get; set; } | ||||
|     Task ITriggerNode.StartAsync(Func<NodeOutput, Task> func) | ||||
|     private Func<NodeOutput, CancellationToken, Task> Func { get; set; } | ||||
|     Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken) | ||||
|     { | ||||
|         Func = func; | ||||
|         FuncDict.TryAdd(this, func); | ||||
| @@ -40,12 +40,12 @@ public class ValueChangedTriggerNode : VariableNode, ITriggerNode, IDisposable | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|     public static ConcurrentDictionary<string, ConcurrentDictionary<string, ConcurrentList<ValueChangedTriggerNode>>> ValueChangedTriggerNodeDict = new(); | ||||
|     public static ConcurrentDictionary<ValueChangedTriggerNode, Func<NodeOutput, Task>> FuncDict = new(); | ||||
|     public static ConcurrentDictionary<ValueChangedTriggerNode, Func<NodeOutput, CancellationToken, Task>> FuncDict = new(); | ||||
|  | ||||
|     public static BlockingCollection<VariableBasicData> VariableBasicDatas = new(); | ||||
|     static ValueChangedTriggerNode() | ||||
|     { | ||||
|         _ = RunAsync(); | ||||
|         Task.Factory.StartNew(RunAsync); | ||||
|         GlobalData.VariableValueChangeEvent += GlobalData_VariableValueChangeEvent; | ||||
|     } | ||||
|     private static void GlobalData_VariableValueChangeEvent(VariableRuntime variableRuntime, VariableBasicData variableData) | ||||
| @@ -81,7 +81,7 @@ public class ValueChangedTriggerNode : VariableNode, ITriggerNode, IDisposable | ||||
|                     if (FuncDict.TryGetValue(item, out var func)) | ||||
|                     { | ||||
|                         item.Logger?.Trace($"Variable changed: {item.Text}"); | ||||
|                         await func.Invoke(new NodeOutput() { Value = variableBasicData }).ConfigureAwait(false); | ||||
|                         await func.Invoke(new NodeOutput() { Value = variableBasicData }, token).ConfigureAwait(false); | ||||
|  | ||||
|                     } | ||||
|                 } | ||||
|   | ||||
| @@ -168,14 +168,14 @@ internal sealed class RulesEngineHostedService : BackgroundService, IRulesEngine | ||||
|             else if (targetNode is ITriggerNode triggerNode) | ||||
|             { | ||||
|  | ||||
|                 Func<NodeOutput, Task> func = (async a => | ||||
|                 Func<NodeOutput, CancellationToken, Task> func = (async (a, token) => | ||||
|                 { | ||||
|                     foreach (var link in targetNode.PortLinks.Where(a => ((a.Target.Model as PortModel)?.Parent) != targetNode)) | ||||
|                     { | ||||
|                         await Analysis((link.Target.Model as PortModel)?.Parent, new NodeInput() { Value = a.Value }, rulesLog, cancellationToken).ConfigureAwait(false); | ||||
|                         await Analysis((link.Target.Model as PortModel)?.Parent, new NodeInput() { Value = a.Value }, rulesLog, token).ConfigureAwait(false); | ||||
|                     } | ||||
|                 }); | ||||
|                 await triggerNode.StartAsync(func).ConfigureAwait(false); | ||||
|                 await triggerNode.StartAsync(func, cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|             } | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
| using ThingsGateway.NewLife.Caching; | ||||
| using ThingsGateway.NewLife.Extension; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Dlt645; | ||||
| @@ -40,10 +39,10 @@ public class Dlt645_2007Address : Dlt645_2007Request | ||||
|     /// </summary> | ||||
|     public static Dlt645_2007Address ParseFrom(string address, string defaultStation = null, bool isCache = true) | ||||
|     { | ||||
|         var cacheKey = $"{nameof(ParseFrom)}_{typeof(Dlt645_2007Address).FullName}_{typeof(Dlt645_2007Address).TypeHandle.Value}_{address}_{defaultStation}"; | ||||
|         if (isCache) | ||||
|             if (MemoryCache.Instance.TryGetValue(cacheKey, out Dlt645_2007Address dAddress)) | ||||
|                 return new(dAddress); | ||||
|         //var cacheKey = $"{nameof(ParseFrom)}_{typeof(Dlt645_2007Address).FullName}_{typeof(Dlt645_2007Address).TypeHandle.Value}_{address}_{defaultStation}"; | ||||
|         //if (isCache) | ||||
|         //    if (MemoryCache.Instance.TryGetValue(cacheKey, out Dlt645_2007Address dAddress)) | ||||
|         //        return new(dAddress); | ||||
|  | ||||
|         Dlt645_2007Address dlt645_2007Address = new(); | ||||
|         if (!string.IsNullOrEmpty(defaultStation)) | ||||
| @@ -78,10 +77,11 @@ public class Dlt645_2007Address : Dlt645_2007Request | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (isCache) | ||||
|             MemoryCache.Instance.Set(cacheKey, dlt645_2007Address, 3600); | ||||
|         //if (isCache) | ||||
|         //    MemoryCache.Instance.Set(cacheKey, dlt645_2007Address, 3600); | ||||
|  | ||||
|         return new(dlt645_2007Address); | ||||
|         return dlt645_2007Address; | ||||
|         //return new(dlt645_2007Address); | ||||
|     } | ||||
|  | ||||
|     public void SetDataId(string dataId) | ||||
|   | ||||
| @@ -43,7 +43,7 @@ internal static class PackHelper | ||||
|             { | ||||
|                 RegisterAddress = item.Key!, | ||||
|                 Length = 1, | ||||
|                 TimeTick = new(string.IsNullOrWhiteSpace(item.FirstOrDefault().IntervalTime) ? defaultIntervalTime : item.FirstOrDefault().IntervalTime), | ||||
|                 IntervalTime = string.IsNullOrWhiteSpace(item.FirstOrDefault().IntervalTime) ? defaultIntervalTime : item.FirstOrDefault().IntervalTime, | ||||
|             }; | ||||
|             r.AddVariableRange(item); | ||||
|             result.Add(r); | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
| using ThingsGateway.NewLife.Caching; | ||||
| using ThingsGateway.NewLife.Extension; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Modbus; | ||||
| @@ -68,10 +67,10 @@ public class ModbusAddress : ModbusRequest | ||||
|     public static ModbusAddress? ParseFrom(string address, byte? station = null, bool isCache = true) | ||||
|     { | ||||
|         if (string.IsNullOrWhiteSpace(address)) { return null; } | ||||
|         var cacheKey = $"{nameof(ParseFrom)}_{typeof(ModbusAddress).FullName}_{typeof(ModbusAddress).TypeHandle.Value}_{station}_{address}"; | ||||
|         if (isCache) | ||||
|             if (MemoryCache.Instance.TryGetValue(cacheKey, out ModbusAddress mAddress)) | ||||
|                 return new(mAddress); | ||||
|         //var cacheKey = $"{nameof(ParseFrom)}_{typeof(ModbusAddress).FullName}_{typeof(ModbusAddress).TypeHandle.Value}_{station}_{address}"; | ||||
|         //if (isCache) | ||||
|         //    if (MemoryCache.Instance.TryGetValue(cacheKey, out ModbusAddress mAddress)) | ||||
|         //        return new(mAddress); | ||||
|  | ||||
|         var modbusAddress = new ModbusAddress(); | ||||
|         if (station != null) | ||||
| @@ -103,10 +102,11 @@ public class ModbusAddress : ModbusRequest | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (isCache) | ||||
|             MemoryCache.Instance.Set(cacheKey, modbusAddress, 3600); | ||||
|         //if (isCache) | ||||
|         //    MemoryCache.Instance.Set(cacheKey, modbusAddress, 3600); | ||||
|  | ||||
|         return new(modbusAddress); | ||||
|         //return new(modbusAddress); | ||||
|         return modbusAddress; | ||||
|  | ||||
|         void Address(string address) | ||||
|         { | ||||
|   | ||||
| @@ -8,7 +8,6 @@ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using ThingsGateway.NewLife; | ||||
| using ThingsGateway.NewLife.Extension; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Modbus; | ||||
| @@ -165,7 +164,7 @@ public static class PackHelper | ||||
|             // 创建一个新的变量源读取对象 | ||||
|             T sourceRead = new() | ||||
|             { | ||||
|                 TimeTick = new TimeTick(intervalTime), | ||||
|                 IntervalTime = intervalTime, | ||||
|                 // 将当前组打包地址中的起始地址作为实际打包报文中的起始地址 | ||||
|                 RegisterAddress = startAddress.ToString(), | ||||
|                 Length = sourceLen.ToInt() | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
| using ThingsGateway.NewLife.Caching; | ||||
| using ThingsGateway.NewLife.Collections; | ||||
| using ThingsGateway.NewLife.Extension; | ||||
|  | ||||
| @@ -144,10 +143,10 @@ public class SiemensS7Address : S7Request | ||||
|     public static SiemensS7Address ParseFrom(string address, bool isCache = true) | ||||
|     { | ||||
|         if (string.IsNullOrWhiteSpace(address)) { return null; } | ||||
|         var cacheKey = $"{nameof(ParseFrom)}_{typeof(SiemensS7Address).FullName}_{typeof(SiemensS7Address).TypeHandle.Value}_{address}"; | ||||
|         if (isCache) | ||||
|             if (MemoryCache.Instance.TryGetValue(cacheKey, out SiemensS7Address sAddress)) | ||||
|                 return new(sAddress); | ||||
|         //var cacheKey = $"{nameof(ParseFrom)}_{typeof(SiemensS7Address).FullName}_{typeof(SiemensS7Address).TypeHandle.Value}_{address}"; | ||||
|         //if (isCache) | ||||
|         //    if (MemoryCache.Instance.TryGetValue(cacheKey, out SiemensS7Address sAddress)) | ||||
|         //        return new(sAddress); | ||||
|  | ||||
|         SiemensS7Address s7AddressData = new(); | ||||
|         address = address.ToUpper(); | ||||
| @@ -276,8 +275,8 @@ public class SiemensS7Address : S7Request | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (isCache) | ||||
|             MemoryCache.Instance.Set(cacheKey, new SiemensS7Address(s7AddressData), 3600); | ||||
|         //if (isCache) | ||||
|         //    MemoryCache.Instance.Set(cacheKey, new SiemensS7Address(s7AddressData), 3600); | ||||
|  | ||||
|         return s7AddressData; | ||||
|     } | ||||
|   | ||||
| @@ -228,7 +228,7 @@ internal static class PackHelper | ||||
|  | ||||
|             T sourceRead = new() // 创建一个新的源读取对象 | ||||
|             { | ||||
|                 TimeTick = new(intervalTime), // 设置时间戳 | ||||
|                 IntervalTime = intervalTime, // 设置时间戳 | ||||
|                 RegisterAddress = tempAddresses.OrderBy(it => it.AddressStart).First().ToString(), // 获取地址并按地址排序 | ||||
|                 Length = sourceLen // 设置源长度 | ||||
|             }; | ||||
|   | ||||
| @@ -180,7 +180,8 @@ public partial class QuestDBProducer : BusinessBaseWithCacheIntervalVariableMode | ||||
|         await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|  | ||||
|     protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         await UpdateVarModelMemory(cancellationToken).ConfigureAwait(false); | ||||
|         await UpdateVarModelsMemory(cancellationToken).ConfigureAwait(false); | ||||
|   | ||||
| @@ -227,7 +227,7 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel< | ||||
|         await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|     protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (_driverPropertys.IsReadDB) | ||||
|         { | ||||
|   | ||||
| @@ -91,9 +91,9 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheVariableModel<Histor | ||||
|         return base.ProtectedStartAsync(cancellationToken); | ||||
|     } | ||||
|  | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|     protected override Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         await Update(cancellationToken).ConfigureAwait(false); | ||||
|         return Update(cancellationToken); | ||||
|     } | ||||
|  | ||||
|     #region 数据查询 | ||||
|   | ||||
| @@ -199,7 +199,7 @@ public partial class TDengineDBProducer : BusinessBaseWithCacheIntervalVariableM | ||||
|         await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|     protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         await UpdateVarModelMemory(cancellationToken).ConfigureAwait(false); | ||||
|         await UpdateVarModelsMemory(cancellationToken).ConfigureAwait(false); | ||||
|   | ||||
| @@ -63,9 +63,8 @@ public class Dlt645_2007Master : CollectFoundationBase | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables) | ||||
|     protected override Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables) | ||||
|     { | ||||
|         await Task.CompletedTask.ConfigureAwait(false); | ||||
|         return _plc.LoadSourceRead<VariableSourceRead>(deviceVariables, 0, CurrentDevice.IntervalTime); | ||||
|         return Task.FromResult(_plc.LoadSourceRead<VariableSourceRead>(deviceVariables, 0, CurrentDevice.IntervalTime)); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -23,9 +23,9 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableBasic | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsConnected() => success; | ||||
|  | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|     protected override Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         await Update(cancellationToken).ConfigureAwait(false); | ||||
|         return Update(cancellationToken); | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -87,9 +87,9 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|  | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|     protected override Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         await Update(cancellationToken).ConfigureAwait(false); | ||||
|         return Update(cancellationToken); | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -77,14 +77,13 @@ public class ModbusMaster : CollectFoundationBase | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables) | ||||
|     protected override Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables) | ||||
|     { | ||||
|         await Task.CompletedTask.ConfigureAwait(false); | ||||
|         List<VariableSourceRead> variableSourceReads = new(); | ||||
|         foreach (var deviceVariable in deviceVariables.GroupBy(a => a.CollectGroup)) | ||||
|         { | ||||
|             variableSourceReads.AddRange(_plc.LoadSourceRead<VariableSourceRead>(deviceVariable, _driverPropertys.MaxPack, CurrentDevice.IntervalTime)); | ||||
|         } | ||||
|         return variableSourceReads; | ||||
|         return Task.FromResult(variableSourceReads); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -150,7 +150,7 @@ public class ModbusSlave : BusinessBase | ||||
|     } | ||||
|  | ||||
|  | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|     protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         //获取设备连接状态 | ||||
|         if (IsConnected()) | ||||
| @@ -233,7 +233,7 @@ public class ModbusSlave : BusinessBase | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var data = thingsGatewayBitConverter.GetDataFormBytes(_plc, addressStr, writeData, 0, dType, item.Value.ArrayLength ?? 1); | ||||
|                     _ = thingsGatewayBitConverter.GetChangedDataFormBytes(_plc, addressStr, writeData, 0, dType, item.Value.ArrayLength ?? 1, null, out var data); | ||||
|  | ||||
|                     var result = await item.Value.RpcAsync(data.ToSystemTextJsonString(), $"{nameof(ModbusSlave)}-{CurrentDevice.Name}-{$"{channel}"}").ConfigureAwait(false); | ||||
|  | ||||
|   | ||||
| @@ -176,7 +176,7 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBa | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|     protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         var clientResult = await TryMqttClientAsync(cancellationToken).ConfigureAwait(false); | ||||
|         if (!clientResult.IsSuccess) | ||||
|   | ||||
| @@ -98,9 +98,8 @@ public partial class MqttCollect : CollectBase | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected override async Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables) | ||||
|     protected override Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables) | ||||
|     { | ||||
|         await Task.CompletedTask.ConfigureAwait(false); | ||||
|         TopicItemDict.Clear(); | ||||
|         if (deviceVariables.Count > 0) | ||||
|         { | ||||
| @@ -128,7 +127,7 @@ public partial class MqttCollect : CollectBase | ||||
|                 TopicItemDict[group.Key] = new(); | ||||
|                 var sourVars = new VariableSourceRead() | ||||
|                 { | ||||
|                     TimeTick = new("1000"), | ||||
|                     IntervalTime = "1000", | ||||
|                     RegisterAddress = group.Key, | ||||
|                 }; | ||||
|                 foreach (var item in group) | ||||
| @@ -170,11 +169,11 @@ public partial class MqttCollect : CollectBase | ||||
|                     _mqttSubscribeOptions = mqttClientSubscribeOptions; | ||||
|             } | ||||
|  | ||||
|             return dataResult; | ||||
|             return Task.FromResult(dataResult); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return new(); | ||||
|             return Task.FromResult(new List<VariableSourceRead>()); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| @@ -230,11 +229,12 @@ public partial class MqttCollect : CollectBase | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     var now = DateTime.Now; | ||||
|                     foreach (var item in IdVariableRuntimes) | ||||
|                     { | ||||
|                         if (DateTime.Now - item.Value.CollectTime > ETime) | ||||
|                         if (now - item.Value.CollectTime > ETime) | ||||
|                         { | ||||
|                             item.Value.SetValue(null, DateTime.Now, false); | ||||
|                             item.Value.SetValue(null, now, false); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| @@ -260,7 +260,18 @@ public partial class MqttCollect : CollectBase | ||||
|  | ||||
|  | ||||
|     private volatile bool success; | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|  | ||||
|     protected override bool VariableSourceReadsEnable => false; | ||||
|     protected override List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken) | ||||
|     { | ||||
|         var list = base.ProtectedGetTasks(cancellationToken); | ||||
|  | ||||
|         var check = ScheduledTaskHelper.GetTask("3000", CheckAsync, null, LogMessage, cancellationToken); | ||||
|         list.Add(check); | ||||
|  | ||||
|         return list; | ||||
|     } | ||||
|     private async Task CheckAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         var clientResult = await TryMqttClientAsync(cancellationToken).ConfigureAwait(false); | ||||
|         if (!clientResult.IsSuccess) | ||||
| @@ -287,7 +298,6 @@ public partial class MqttCollect : CollectBase | ||||
|             CurrentDevice.SetDeviceStatus(TimerX.Now, true); | ||||
|         } | ||||
|  | ||||
|         ScriptVariableRun(cancellationToken); | ||||
|  | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -102,9 +102,9 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa | ||||
|  | ||||
|     } | ||||
|  | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|     protected override Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         await Update(cancellationToken).ConfigureAwait(false); | ||||
|         return Update(cancellationToken); | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,7 +14,6 @@ using ThingsGateway.Foundation.OpcDa; | ||||
| using ThingsGateway.Foundation.OpcDa.Da; | ||||
| using ThingsGateway.Gateway.Application; | ||||
| using ThingsGateway.NewLife.Json.Extension; | ||||
| using ThingsGateway.NewLife.Threading; | ||||
|  | ||||
| using TouchSocket.Core; | ||||
|  | ||||
| @@ -90,34 +89,11 @@ public class OpcDaMaster : CollectBase | ||||
|     } | ||||
|  | ||||
|  | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (_driverProperties.ActiveSubscribe) | ||||
|         { | ||||
|             //获取设备连接状态 | ||||
|             if (IsConnected()) | ||||
|             { | ||||
|                 //更新设备活动时间 | ||||
|                 CurrentDevice.SetDeviceStatus(TimerX.Now, false); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 CurrentDevice.SetDeviceStatus(TimerX.Now, true); | ||||
|             } | ||||
|  | ||||
|             ScriptVariableRun(cancellationToken); | ||||
|  | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             await base.ProtectedExecuteAsync(cancellationToken).ConfigureAwait(false); | ||||
|         } | ||||
|     } | ||||
|     protected override bool VariableSourceReadsEnable => !_driverProperties.ActiveSubscribe; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables) | ||||
|     protected override Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables) | ||||
|     { | ||||
|         await Task.CompletedTask.ConfigureAwait(false); | ||||
|         try | ||||
|         { | ||||
|             if (deviceVariables.Count > 0) | ||||
| @@ -132,7 +108,7 @@ public class OpcDaMaster : CollectBase | ||||
|               { | ||||
|                   var read = new VariableSourceRead() | ||||
|                   { | ||||
|                       TimeTick = new(_driverProperties.UpdateRate.ToString()), | ||||
|                       IntervalTime = _driverProperties.UpdateRate.ToString(), | ||||
|                       RegisterAddress = it.Key, | ||||
|                   }; | ||||
|                   HashSet<string> ids = new(it.Value.Select(b => b.ItemID)); | ||||
| @@ -146,11 +122,11 @@ public class OpcDaMaster : CollectBase | ||||
|               }).ToList(); | ||||
|                     variableSourceReads.AddRange(sourVars); | ||||
|                 } | ||||
|                 return variableSourceReads; | ||||
|                 return Task.FromResult(variableSourceReads); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new(); | ||||
|                 return Task.FromResult(new List<VariableSourceRead>()); | ||||
|             } | ||||
|         } | ||||
|         finally | ||||
|   | ||||
| @@ -16,7 +16,6 @@ using Opc.Ua.Client; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
| using ThingsGateway.Foundation.OpcUa; | ||||
| using ThingsGateway.Gateway.Application; | ||||
| using ThingsGateway.NewLife; | ||||
| using ThingsGateway.NewLife.Json.Extension; | ||||
| using ThingsGateway.NewLife.Threading; | ||||
|  | ||||
| @@ -102,15 +101,30 @@ public class OpcUaMaster : CollectBase | ||||
|     { | ||||
|         return _plc?.GetAddressDescription(); | ||||
|     } | ||||
|     protected override bool VariableSourceReadsEnable => !_driverProperties.ActiveSubscribe; | ||||
|     protected override List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken) | ||||
|     { | ||||
|         var list = base.ProtectedGetTasks(cancellationToken); | ||||
|  | ||||
|     private TimeTick checkTimeTick = new("60000"); | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|         var check = ScheduledTaskHelper.GetTask("3000", CheckAsync, null, LogMessage, cancellationToken); | ||||
|         list.Add(check); | ||||
|         var checkConnec = ScheduledTaskHelper.GetTask("10000", CheckConnectAsync, null, LogMessage, cancellationToken); | ||||
|         list.Add(checkConnec); | ||||
|         return list; | ||||
|     } | ||||
|  | ||||
|     protected override async Task ProtectedStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         await CheckConnectAsync(null, cancellationToken).ConfigureAwait(false); | ||||
|         await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     private async Task CheckConnectAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (_plc.Session == null) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await Task.Delay(100, cancellationToken).ConfigureAwait(false); | ||||
|                 if (_plc.Session == null) | ||||
|                     await _plc.ConnectAsync(cancellationToken).ConfigureAwait(false); | ||||
|             } | ||||
| @@ -121,68 +135,60 @@ public class OpcUaMaster : CollectBase | ||||
|  | ||||
|                 connectFirstFailLoged = true; | ||||
|                 CurrentDevice.SetDeviceStatus(TimerX.Now, true, ex.Message); | ||||
|                 await Task.Delay(10000, cancellationToken).ConfigureAwait(false); | ||||
|             } | ||||
|         } | ||||
|         if (_driverProperties.ActiveSubscribe) | ||||
|     } | ||||
|     private async Task CheckAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (_plc.Session != null) | ||||
|         { | ||||
|  | ||||
|             //获取设备连接状态 | ||||
|             if (IsConnected()) | ||||
|             if (_driverProperties.ActiveSubscribe) | ||||
|             { | ||||
|  | ||||
|  | ||||
|                 //更新设备活动时间 | ||||
|                 CurrentDevice.SetDeviceStatus(TimerX.Now, false); | ||||
|                 if (checkTimeTick.IsTickHappen()) | ||||
|                 //获取设备连接状态 | ||||
|                 if (IsConnected()) | ||||
|                 { | ||||
|  | ||||
|                     //如果是订阅模式,连接时添加订阅组 | ||||
|                     if (_plc.OpcUaProperty?.ActiveSubscribe == true && CurrentDevice.VariableSourceReads.Count > 0 && _plc.Session.SubscriptionCount < CurrentDevice.VariableSourceReads.Count) | ||||
|                     //更新设备活动时间 | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|  | ||||
|                             foreach (var variableSourceRead in CurrentDevice.VariableSourceReads) | ||||
|                         //如果是订阅模式,连接时添加订阅组 | ||||
|                         if (_plc.OpcUaProperty?.ActiveSubscribe == true && CurrentDevice.VariableSourceReads.Count > 0 && _plc.Session.SubscriptionCount < CurrentDevice.VariableSourceReads.Count) | ||||
|                         { | ||||
|                             try | ||||
|                             { | ||||
|                                 if (_plc.Session.Subscriptions.FirstOrDefault(a => a.DisplayName == variableSourceRead.RegisterAddress) == null) | ||||
|  | ||||
|                                 foreach (var variableSourceRead in CurrentDevice.VariableSourceReads) | ||||
|                                 { | ||||
|                                     await _plc.AddSubscriptionAsync(variableSourceRead.RegisterAddress, variableSourceRead.VariableRuntimes.Where(a => !a.RegisterAddress.IsNullOrEmpty()).Select(a => a.RegisterAddress!).ToHashSet().ToArray(), _plc.OpcUaProperty.LoadType, cancellationToken).ConfigureAwait(false); | ||||
|                                     if (_plc.Session.Subscriptions.FirstOrDefault(a => a.DisplayName == variableSourceRead.RegisterAddress) == null) | ||||
|                                     { | ||||
|                                         await _plc.AddSubscriptionAsync(variableSourceRead.RegisterAddress, variableSourceRead.VariableRuntimes.Where(a => !a.RegisterAddress.IsNullOrEmpty()).Select(a => a.RegisterAddress!).ToHashSet().ToArray(), _plc.OpcUaProperty.LoadType, cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|                                     LogMessage?.LogInformation($"AddSubscription index  {CurrentDevice.VariableSourceReads.IndexOf(variableSourceRead)}  done"); | ||||
|                                         LogMessage?.LogInformation($"AddSubscription index  {CurrentDevice.VariableSourceReads.IndexOf(variableSourceRead)}  done"); | ||||
|  | ||||
|                                     } | ||||
|                                 } | ||||
|                                 LogMessage?.LogInformation("AddSubscriptions done"); | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex, "AddSubscriptions"); | ||||
|                             } | ||||
|                             finally | ||||
|                             { | ||||
|                             } | ||||
|                             LogMessage?.LogInformation("AddSubscriptions done"); | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             LogMessage?.LogWarning(ex, "AddSubscriptions"); | ||||
|                         } | ||||
|                         finally | ||||
|                         { | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 CurrentDevice.SetDeviceStatus(TimerX.Now, true); | ||||
|             } | ||||
|  | ||||
|             ScriptVariableRun(cancellationToken); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             await base.ProtectedExecuteAsync(cancellationToken).ConfigureAwait(false); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables) | ||||
|     protected override Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables) | ||||
|     { | ||||
|         await Task.CompletedTask.ConfigureAwait(false); | ||||
|         if (deviceVariables.Count > 0) | ||||
|         { | ||||
|             List<VariableSourceRead> variableSourceReads = new List<VariableSourceRead>(); | ||||
| @@ -194,7 +200,7 @@ public class OpcUaMaster : CollectBase | ||||
|                 { | ||||
|                     var sourVars = new VariableSourceRead() | ||||
|                     { | ||||
|                         TimeTick = new(_driverProperties.UpdateRate.ToString()), | ||||
|                         IntervalTime = _driverProperties.UpdateRate.ToString(), | ||||
|                         RegisterAddress = Guid.NewGuid().ToString(), | ||||
|                     }; | ||||
|                     foreach (var item in variable) | ||||
| @@ -205,11 +211,11 @@ public class OpcUaMaster : CollectBase | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             return variableSourceReads; | ||||
|             return Task.FromResult(variableSourceReads); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return new(); | ||||
|             return Task.FromResult(new List<VariableSourceRead>()); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|   | ||||
| @@ -153,7 +153,7 @@ public partial class OpcUaServer : BusinessBase | ||||
|         await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|     protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|   | ||||
| @@ -62,7 +62,7 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<Vari | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|  | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|     protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (_channel == null) | ||||
|         { | ||||
|   | ||||
| @@ -59,7 +59,7 @@ public partial class Synchronization : BusinessBase, IRpcDriver | ||||
|  | ||||
|  | ||||
|  | ||||
|     protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken) | ||||
|     protected override async Task ProtectedExecuteAsync(object? state, CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|   | ||||
| @@ -66,6 +66,7 @@ public partial class UpdateZipFilePage | ||||
|  | ||||
|     private Task<bool> OnDeleteAsync(IEnumerable<UpdateZipFile> updateZipFiles) | ||||
|     { | ||||
|         return UpdateZipFileHostedService.DeleteAsync(updateZipFiles); | ||||
|         UpdateZipFileHostedService.Delete(updateZipFiles); | ||||
|         return Task.FromResult(true); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ namespace ThingsGateway.Upgrade; | ||||
|  | ||||
| public interface IUpdateZipFileHostedService | ||||
| { | ||||
|     Task<bool> DeleteAsync(IEnumerable<UpdateZipFile> updateZipFiles); | ||||
|     void Delete(IEnumerable<UpdateZipFile> updateZipFiles); | ||||
|     List<UpdateZipFile>? GetList(UpdateZipFileInput input); | ||||
|     Task<QueryData<UpdateZipFile>>? Page(QueryPageOptions options); | ||||
|     Task SaveUpdateZipFile(UpdateZipFileAddInput input); | ||||
|   | ||||
| @@ -20,15 +20,14 @@ public class UpdateZipFileHostedService : IUpdateZipFileHostedService | ||||
|         var data = files.Select(a => Encoding.UTF8.GetString(a.ReadBytes()).FromJsonNetString<UpdateZipFile>()); | ||||
|         return data.Where(a => a.Version > input.Version && a.MinimumCompatibleVersion <= input.Version && a.AppName == input.AppName && a.DotNetVersion.Major == input.DotNetVersion.Major && a.OSPlatform.EqualIgnoreCase(input.OSPlatform) && a.Architecture == input.Architecture).ToList(); | ||||
|     } | ||||
|     public async Task<QueryData<UpdateZipFile>>? Page(QueryPageOptions options) | ||||
|     public Task<QueryData<UpdateZipFile>>? Page(QueryPageOptions options) | ||||
|     { | ||||
|         var path = Path.Combine(AppContext.BaseDirectory, FileConst.ServerDir); | ||||
|  | ||||
|         var files = path.AsDirectory().GetAllFiles("*.json", true); | ||||
|         var data = files.Select(a => Encoding.UTF8.GetString(a.ReadBytes()).FromJsonNetString<UpdateZipFile>()).GetQueryData(options); | ||||
|  | ||||
|         await Task.CompletedTask.ConfigureAwait(false); | ||||
|         return data; | ||||
|         return Task.FromResult(data); | ||||
|     } | ||||
|     public async Task SaveUpdateZipFile(UpdateZipFileAddInput input) | ||||
|     { | ||||
| @@ -126,9 +125,8 @@ public class UpdateZipFileHostedService : IUpdateZipFileHostedService | ||||
|         using var fs1 = new FileStream(Path.Combine(path, $"{model.AppName}.json"), FileMode.Create); | ||||
|         await stream1.CopyToAsync(fs1).ConfigureAwait(false); | ||||
|     } | ||||
|     public async Task<bool> DeleteAsync(IEnumerable<UpdateZipFile> updateZipFiles) | ||||
|     public void Delete(IEnumerable<UpdateZipFile> updateZipFiles) | ||||
|     { | ||||
|         await Task.CompletedTask.ConfigureAwait(false); | ||||
|         var path = Path.Combine(AppContext.BaseDirectory, FileConst.ServerDir); | ||||
|         foreach (var item in updateZipFiles) | ||||
|         { | ||||
| @@ -138,6 +136,5 @@ public class UpdateZipFileHostedService : IUpdateZipFileHostedService | ||||
|                 filePath.AsDirectory().Delete(true); | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <Project> | ||||
|   <PropertyGroup> | ||||
|     <Version>10.8.2</Version> | ||||
|     <Version>10.8.8</Version> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user