Compare commits

...

2 Commits

Author SHA1 Message Date
Diego
aec91da28b 10.8.2
feat: 优化闭包导致的状态机内存占用,高并发时内存显著下降
fix(sqldb): 定时上传模式时,实时表时效
fix(taos): 初始化失败
2025-06-17 17:09:05 +08:00
Diego
013ff394be 10.8.1 2025-06-16 18:25:55 +08:00
49 changed files with 305 additions and 159 deletions

View File

@@ -61,7 +61,7 @@ public class HardwareJob : IJob, IHardwareJob
var historyHardwareInfos = MemoryCache.Get<List<HistoryHardwareInfo>>(CacheKey);
if (historyHardwareInfos == null)
{
using var db = _db;
using var db = DbContext.GetDB<HistoryHardwareInfo>(); ;
historyHardwareInfos = await db.Queryable<HistoryHardwareInfo>().Where(a => a.Date > DateTime.Now.AddDays(-3)).ToListAsync().ConfigureAwait(false);
MemoryCache.Set(CacheKey, historyHardwareInfos);
@@ -81,7 +81,7 @@ public class HardwareJob : IJob, IHardwareJob
{
if (HardwareInfo.MachineInfo == null)
{
await MachineInfo.RegisterAsync().ConfigureAwait(false);
MachineInfo.Register();
HardwareInfo.MachineInfo = MachineInfo.Current;
string currentPath = Directory.GetCurrentDirectory();

View File

@@ -22,12 +22,11 @@ public partial class SessionPage
#region
private async Task<QueryData<SessionOutput>> OnQueryAsync(QueryPageOptions options)
private Task<QueryData<SessionOutput>> OnQueryAsync(QueryPageOptions options)
{
return await Task.Run(async () =>
return Task.Run(() =>
{
var data = await SessionService.PageAsync(options);
return data;
return SessionService.PageAsync(options);
});
}

View File

@@ -33,6 +33,12 @@
<None Remove="$(SolutionDir)..\README.md" Pack="false" PackagePath="\" />
<None Remove="$(SolutionDir)..\README.zh-CN.md" Pack="false" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BlazorSetParametersAsyncGenerator\BlazorSetParametersAsyncGenerator.csproj" PrivateAssets="all" OutputItemType="Analyzer" />
</ItemGroup>
</Project>

View File

@@ -22,8 +22,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ThingsGateway.Razor" Version="$(SourceGeneratorVersion)" />
<!--<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />-->
<!--<PackageReference Include="ThingsGateway.Razor" Version="$(SourceGeneratorVersion)" />-->
<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />
<ProjectReference Include="..\ThingsGateway.SqlSugar\ThingsGateway.SqlSugar.csproj" />
<!--<PackageReference Include="SqlSugarCore" Version="5.1.4.195" />-->
</ItemGroup>

View File

@@ -160,8 +160,8 @@ public sealed class DatabaseLoggerProvider : ILoggerProvider, ISupportExternalSc
_databaseLoggingWriter = _serviceScope.ServiceProvider.GetRequiredService(databaseLoggingWriterType) as IDatabaseLoggingWriter;
// 创建长时间运行的后台任务,并将日志消息队列中数据写入存储中
_processQueueTask = Task.Factory.StartNew(async state => await ((DatabaseLoggerProvider)state).ProcessQueueAsync().ConfigureAwait(false)
, this, TaskCreationOptions.LongRunning);
_processQueueTask = Task.Factory.StartNew(ProcessQueueAsync
, TaskCreationOptions.LongRunning);
}
/// <summary>

View File

@@ -90,8 +90,7 @@ public sealed class FileLoggerProvider : ILoggerProvider, ISupportExternalScope
_fileLoggingWriter = new FileLoggingWriter(this);
// 创建长时间运行的后台任务,并将日志消息队列中数据写入文件中
_processQueueTask = Task.Factory.StartNew(async state => await ((FileLoggerProvider)state).ProcessQueueAsync().ConfigureAwait(false)
, this, TaskCreationOptions.LongRunning);
_processQueueTask = Task.Factory.StartNew(ProcessQueueAsync, TaskCreationOptions.LongRunning);
}
/// <summary>

View File

@@ -110,8 +110,7 @@ internal sealed partial class SchedulerFactory : ISchedulerFactory
if (Persistence is not null)
{
// 创建长时间运行的后台任务,并将作业运行消息写入持久化中
_processQueueTask = Task.Factory.StartNew(async state => await ((SchedulerFactory)state).ProcessQueueAsync().ConfigureAwait(false)
, this, TaskCreationOptions.LongRunning);
_processQueueTask = Task.Factory.StartNew(ProcessQueueAsync, TaskCreationOptions.LongRunning);
}
}

View File

@@ -127,63 +127,57 @@ public class MachineInfo
//static MachineInfo() => RegisterAsync().Wait(100);
private static Task<MachineInfo>? _task;
/// <summary>异步注册一个初始化后的机器信息实例</summary>
/// <returns></returns>
public static Task<MachineInfo> RegisterAsync()
public static MachineInfo Register()
{
if (_task != null) return _task;
return _task = Task.Factory.StartNew(() =>
if (Current != null) return Current;
// 文件缓存加快机器信息获取。在Linux下可能StarAgent以root权限写入缓存文件其它应用以普通用户访问
var file = Path.GetTempPath().CombinePath("machine_info.json");
var json = "";
if (Current == null)
{
// 文件缓存加快机器信息获取。在Linux下可能StarAgent以root权限写入缓存文件其它应用以普通用户访问
var file = Path.GetTempPath().CombinePath("machine_info.json");
var json = "";
if (Current == null)
var f = file;
if (File.Exists(f))
{
var f = file;
if (File.Exists(f))
try
{
try
{
//XTrace.WriteLine("Load MachineInfo {0}", f);
json = File.ReadAllText(f);
Current = json.FromJsonNetString<MachineInfo>();
}
catch (Exception ex)
{
if (XTrace.Log.Level <= LogLevel.Debug) NewLife.Log.XTrace.WriteException(ex);
}
//XTrace.WriteLine("Load MachineInfo {0}", f);
json = File.ReadAllText(f);
Current = json.FromJsonNetString<MachineInfo>();
}
catch (Exception ex)
{
if (XTrace.Log.Level <= LogLevel.Debug) NewLife.Log.XTrace.WriteException(ex);
}
}
}
var mi = Current ?? new MachineInfo();
var mi = Current ?? new MachineInfo();
mi.Init();
Current = mi;
mi.Init();
Current = mi;
try
try
{
var json2 = mi.ToJsonNetString();
if (json != json2)
{
var json2 = mi.ToJsonNetString();
if (json != json2)
{
File.WriteAllText(file.EnsureDirectory(true), json2);
}
}
catch (Exception ex)
{
if (XTrace.Log.Level <= LogLevel.Debug) NewLife.Log.XTrace.WriteException(ex);
File.WriteAllText(file.EnsureDirectory(true), json2);
}
}
catch (Exception ex)
{
if (XTrace.Log.Level <= LogLevel.Debug) NewLife.Log.XTrace.WriteException(ex);
}
return mi;
});
return mi;
}
/// <summary>获取当前信息,如果未设置则等待异步注册结果</summary>
/// <returns></returns>
public static MachineInfo GetCurrent() => Current ?? RegisterAsync().ConfigureAwait(false).GetAwaiter().GetResult();
public static MachineInfo GetCurrent() => Current ?? Register();
#endregion

View File

@@ -47,7 +47,7 @@ public class Startup : AppStartup
// 缓存
services.AddSingleton<ICache, MemoryCache>();
MachineInfo.RegisterAsync();
MachineInfo.Register();
// 配置雪花Id算法机器码
YitIdHelper.SetIdGenerator(new IdGeneratorOptions

View File

@@ -1,10 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<Import Project="$(SolutionDir)Version.props" />
<Import Project="$(SolutionDir)PackNuget.props" />
<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
<Version>$(SourceGeneratorVersion)</Version>
<!--<UseRazorSourceGenerator>false</UseRazorSourceGenerator>-->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BootstrapBlazor.FontAwesome" Version="9.0.2" />
@@ -31,10 +29,10 @@
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' != 'Debug' ">
<!--<ItemGroup Condition="'$(Configuration)' != 'Debug' ">
<None Include="..\BlazorSetParametersAsyncGenerator\tools\*.ps1" PackagePath="tools" Pack="true" Visible="false" />
<None Include="..\BlazorSetParametersAsyncGenerator\bin\$(Configuration)\netstandard2.0\BlazorSetParametersAsyncGenerator.dll" PackagePath="analyzers\dotnet\cs" Pack="true" Visible="false" />
</ItemGroup>
</ItemGroup>-->
<ItemGroup>
<ProjectReference Include="..\BlazorSetParametersAsyncGenerator\BlazorSetParametersAsyncGenerator.csproj" PrivateAssets="all" OutputItemType="Analyzer" />

View File

@@ -685,17 +685,20 @@ namespace ThingsGateway.SqlSugar
private static Type GetCustomDbType(string className, Type type)
{
if (className.Replace(".", "").Length + 1 == className.Length)
//命名空间相关
if (className.Replace(".", "").Length + 2 == className.Length)
{
var array = className.Split('.');
foreach (var item in UtilMethods.EnumToDictionary<DbType>())
if (array.Length >= 3)
{
if (array.Last().StartsWith(item.Value.ToString()))
foreach (var item in UtilMethods.EnumToDictionary<DbType>())
{
var newName = array.First() + "." + item.Value.ToString() + "." + array.Last();
type = GetCustomTypeByClass(newName);
break;
if (array.Last().StartsWith(item.Value.ToString()))
{
var newName = $"{array[0]}.{array[1]}.{item.Value}.{array.Last()}";
type = GetCustomTypeByClass(newName);
break;
}
}
}

View File

@@ -23,7 +23,7 @@
<ItemGroup>
<PackageReference Include="SqlSugarCore.Dm" Version="8.8.0" />
<PackageReference Include="SqlSugarCore.Kdbndp" Version="9.3.7.605" />
<PackageReference Include="SqlSugarCore.Kdbndp" Version="9.3.7.613" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="$(NET9Version)" />
<PackageReference Include="MySqlConnector" Version="2.4.0" />
<PackageReference Include="Npgsql" Version="9.0.3" />

View File

@@ -1,10 +1,10 @@
<Project>
<PropertyGroup>
<PluginVersion>10.8.0</PluginVersion>
<ProPluginVersion>10.8.0</ProPluginVersion>
<PluginVersion>10.8.2</PluginVersion>
<ProPluginVersion>10.8.2</ProPluginVersion>
<AuthenticationVersion>2.8.0</AuthenticationVersion>
<SourceGeneratorVersion>10.8.0</SourceGeneratorVersion>
<SourceGeneratorVersion>10.8.2</SourceGeneratorVersion>
<NET8Version>8.0.17</NET8Version>
<NET9Version>9.0.6</NET9Version>
</PropertyGroup>

View File

@@ -7,8 +7,8 @@
<!--<UseRazorSourceGenerator>false</UseRazorSourceGenerator>-->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ThingsGateway.Razor" Version="$(SourceGeneratorVersion)" />
<!--<ProjectReference Include="..\..\Admin\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />-->
<!--<PackageReference Include="ThingsGateway.Razor" Version="$(SourceGeneratorVersion)" />-->
<ProjectReference Include="..\..\Admin\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />
<ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
</ItemGroup>
@@ -22,8 +22,10 @@
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Admin\BlazorSetParametersAsyncGenerator\BlazorSetParametersAsyncGenerator.csproj" PrivateAssets="all" OutputItemType="Analyzer" />
</ItemGroup>
</Project>

View File

@@ -23,16 +23,17 @@ public class DoTask
/// 取消令牌
/// </summary>
private CancellationTokenSource? _cancelTokenSource;
private object? _state;
public DoTask(Func<CancellationToken, ValueTask> doWork, ILog logger, string taskName = null)
public DoTask(Func<object?, CancellationToken, Task> doWork, ILog logger, object? state = null, string taskName = null)
{
DoWork = doWork; Logger = logger; TaskName = taskName;
DoWork = doWork; Logger = logger; TaskName = taskName; _state = state;
}
/// <summary>
/// 执行任务方法
/// </summary>
public Func<CancellationToken, ValueTask> DoWork { get; }
public Func<object?, CancellationToken, Task> DoWork { get; }
private ILog Logger { get; }
private Task PrivateTask { get; set; }
private string TaskName { get; }
@@ -74,7 +75,7 @@ public class DoTask
{
if (_cancelTokenSource.IsCancellationRequested)
return;
await DoWork(_cancelTokenSource.Token).ConfigureAwait(false);
await DoWork(_state, _cancelTokenSource.Token).ConfigureAwait(false);
}
catch (OperationCanceledException)
{

View File

@@ -0,0 +1,91 @@
using TouchSocket.Core;
namespace ThingsGateway.Gateway.Application;
public class ScheduledTask
{
private TimeSpan _interval { get; }
private TimeSpan _interval10 = TimeSpan.FromMilliseconds(10);
private double _intervalMS { get; }
private readonly Func<object?, CancellationToken, Task> _taskFunc;
private readonly CancellationToken _token;
private Timer? _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)
{
LogMessage = log;
_state = state;
_interval = interval;
_intervalMS = interval.TotalMilliseconds;
_taskFunc = taskFunc;
_token = token;
}
public void Start()
{
_timer?.Dispose();
_timer = new Timer(TimerCallback, _state, TimeSpan.Zero, _interval);
}
private void TimerCallback(object? state)
{
_ = Do(state);
}
private async Task Do(object? state)
{
if (_token.IsCancellationRequested)
return;
Interlocked.Exchange(ref _pendingTriggers, 1);
if (Interlocked.Exchange(ref _isRunning, 1) == 1)
return;
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 DelayDo()
{
// 延迟触发下一次
_timer?.Change(_interval10, _interval);
}
public void Change(int dueTime, int period)
{
_timer?.Change(dueTime, period);
}
public void Stop()
{
_timer?.Dispose();
_timer = null;
}
}

View File

@@ -41,7 +41,7 @@ public class SmartTriggerScheduler
// 否则启动执行任务
_isRunning = true;
_ = Task.Run(ExecuteLoop); // 开启异步执行循环(非阻塞)
_ = Task.Run(ExecuteLoop);
}
}

View File

@@ -0,0 +1,34 @@
namespace ThingsGateway.Gateway.Application;
public class TaskSchedulerLoop
{
public readonly List<ScheduledTask> Tasks;
public TaskSchedulerLoop(List<ScheduledTask> tasks)
{
Tasks = tasks;
}
public void Start()
{
foreach (var task in Tasks)
{
task.Start();
}
}
public void Stop()
{
foreach (var task in Tasks)
{
task.Stop();
}
}
public void Change(int dueTime, int period)
{
foreach (var task in Tasks)
{
task.Change(dueTime, period);
}
}
}

View File

@@ -171,7 +171,7 @@ public abstract class BusinessBase : DriverBase
}
}
internal override ValueTask StartAsync(CancellationToken cancellationToken)
internal override Task StartAsync(CancellationToken cancellationToken)
{
TimeTick = new TimeTick(CurrentDevice.IntervalTime);
return base.StartAsync(cancellationToken);

View File

@@ -49,8 +49,7 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
GlobalData.AlarmChangedEvent += AlarmValueChange;
// 解绑全局数据的事件
GlobalData.VariableValueChangeEvent -= VariableValueChange;
GlobalData.DeviceStatusChangeEvent -= DeviceStatusChange;
// 根据业务属性的缓存是否为间隔上传来决定事件绑定
if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval)
@@ -129,6 +128,7 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
/// </summary>
protected override void Dispose(bool disposing)
{
// 解绑事件
GlobalData.AlarmChangedEvent -= AlarmValueChange;
GlobalData.VariableValueChangeEvent -= VariableValueChange;

View File

@@ -50,10 +50,6 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
// 注销全局变量值改变事件和设备状态改变事件的订阅,以防止重复订阅
GlobalData.VariableValueChangeEvent -= VariableValueChange;
GlobalData.DeviceStatusChangeEvent -= DeviceStatusChange;
// 如果不是间隔上传,则订阅全局变量值改变事件和设备状态改变事件,并触发一次事件处理
if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval)
{

View File

@@ -44,7 +44,6 @@ public abstract class BusinessBaseWithCacheIntervalVariableModel<VarModel> : Bus
_exTTimerTick = new TimeTick(_businessPropertyWithCacheInterval.BusinessInterval);
// 注册变量值变化事件处理程序
GlobalData.VariableValueChangeEvent -= VariableValueChange;
if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval)
{
GlobalData.VariableValueChangeEvent += VariableValueChange;
@@ -164,7 +163,7 @@ public abstract class BusinessBaseWithCacheIntervalVariableModel<VarModel> : Bus
/// </summary>
/// <param name="variableRuntime">变量运行时对象</param>
/// <param name="variable">变量数据</param>
private void VariableValueChange(VariableRuntime variableRuntime, VariableBasicData variable)
protected void VariableValueChange(VariableRuntime variableRuntime, VariableBasicData variable)
{
if (CurrentDevice.Pause == true)
return;

View File

@@ -234,16 +234,17 @@ public abstract class CollectBase : DriverBase, IRpcDriver
return ThreadRunReturnTypeEnum.None;
}
}
ReadResultCount readResultCount = new();
/// <summary>
/// 执行读取等方法,如果插件不支持读取,而是自更新值的话,需重写此方法
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
try
{
ReadResultCount readResultCount = new();
readResultCount.Reset();
if (cancellationToken.IsCancellationRequested)
return;
@@ -545,6 +546,13 @@ public abstract class CollectBase : DriverBase, IRpcDriver
public int deviceMethodsVariableSuccessNum = 0;
public int deviceSourceVariableFailedNum = 0;
public int deviceSourceVariableSuccessNum = 0;
public void Reset()
{
deviceMethodsVariableFailedNum = 0;
deviceMethodsVariableSuccessNum = 0;
deviceSourceVariableFailedNum = 0;
deviceSourceVariableSuccessNum = 0;
}
}
#region

View File

@@ -237,7 +237,7 @@ public abstract class DriverBase : DisposableObject, IDriver
/// </summary>
/// <param name="cancellationToken">取消操作的令牌。</param>
/// <returns>表示异步操作的任务。</returns>
internal virtual async ValueTask StartAsync(CancellationToken cancellationToken)
internal virtual async Task StartAsync(CancellationToken cancellationToken)
{
// 如果已经执行过初始化,则直接返回
if (IsStarted)
@@ -426,7 +426,7 @@ public abstract class DriverBase : DisposableObject, IDriver
/// <summary>
/// 间隔执行
/// </summary>
protected abstract ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken);
protected abstract Task ProtectedExecuteAsync(CancellationToken cancellationToken);

View File

@@ -39,7 +39,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
static DeviceThreadManage()
{
Task.Factory.StartNew(async () => await SetCycleInterval().ConfigureAwait(false), TaskCreationOptions.LongRunning);
Task.Factory.StartNew(SetCycleInterval, TaskCreationOptions.LongRunning);
}
private static async Task SetCycleInterval()
@@ -385,14 +385,20 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
}
}
// 初始化业务线程
var driverTask = new DoTask(t => DoWork(driver, IsCollectChannel, t), driver.LogMessage, null);
DriverTasks.TryAdd(driver.DeviceId, driverTask);
token.Register(driver.Stop);
driverTask.Start(token);
if (driver.IsInitSuccess)
{
// 初始化业务线程
var driverTask = new DoTask(DoWork, driver.LogMessage, driver);
DriverTasks.TryAdd(driver.DeviceId, driverTask);
driverTask.Start(token);
}
}).ConfigureAwait(false);
@@ -463,12 +469,12 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
await deviceIds.ParallelForEachAsync(async (deviceId, cancellationToken) =>
{
// 查找具有指定设备ID的驱动程序对象
if (!Drivers.TryRemove(deviceId, out var driver)) return;
if (!DriverTasks.TryRemove(deviceId, out var task)) return;
if (IsCollectChannel == true)
if (Drivers.TryRemove(deviceId, out var driver))
{
saveVariableRuntimes.AddRange(driver.IdVariableRuntimes.Where(a => a.Value.SaveValue && !a.Value.DynamicVariable).Select(a => a.Value));
if (IsCollectChannel == true)
{
saveVariableRuntimes.AddRange(driver.IdVariableRuntimes.Where(a => a.Value.SaveValue && !a.Value.DynamicVariable).Select(a => a.Value));
}
}
// 取消驱动程序的操作
@@ -480,7 +486,12 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
token.Dispose();
}
}
await task.StopAsync().ConfigureAwait(false);
if (DriverTasks.TryRemove(deviceId, out var task))
{
await task.StopAsync().ConfigureAwait(false);
}
}).ConfigureAwait(false);
@@ -532,16 +543,16 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
return driver;
}
private static async ValueTask DoWork(DriverBase driver, bool? isCollectChannel, CancellationToken token)
private static async Task DoWork(object? state, CancellationToken token)
{
try
{
if (state is not DriverBase driver) return;
// 只有当驱动成功初始化后才执行操作
if (driver.IsInitSuccess)
{
if (!driver.IsStarted)
await driver.StartAsync(token).ConfigureAwait(false); // 调用驱动的启动前异步方法,如果已经执行,会直接返回
await driver.StartAsync(token).ConfigureAwait(false);
var result = await driver.ExecuteAsync(token).ConfigureAwait(false); // 执行驱动的异步执行操作
@@ -549,7 +560,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
if (result == ThreadRunReturnTypeEnum.None)
{
// 如果驱动处于离线状态且为采集驱动,则根据配置的间隔时间进行延迟
if (driver.CurrentDevice.DeviceStatus == DeviceStatusEnum.OffLine && isCollectChannel == true)
if (driver.CurrentDevice.DeviceStatus == DeviceStatusEnum.OffLine && driver.IsCollectDevice == true)
{
var collectBase = (CollectBase)driver;
if (collectBase.CollectProperties.ReIntervalTime > 0)

View File

@@ -134,11 +134,7 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
/// <summary>
/// 主站
/// </summary>
/// <param name="tcpDmtpService">服务</param>
/// <param name="syncInterval">同步间隔</param>
/// <param name="log">log</param>
/// <param name="stoppingToken">取消任务的 CancellationToken</param>
private static async ValueTask DoMasterWork(TcpDmtpService tcpDmtpService, int syncInterval, ILog log, CancellationToken stoppingToken)
private async Task DoMasterWork(object? state, CancellationToken stoppingToken)
{
// 延迟一段时间,避免过于频繁地执行任务
await Task.Delay(500, stoppingToken).ConfigureAwait(false);
@@ -155,7 +151,7 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
try
{
if (tcpDmtpService.Clients.Count != 0)
if (TcpDmtpService.Clients.Count != 0)
{
online = true;
}
@@ -164,12 +160,12 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
{
var deviceRunTimes = GlobalData.ReadOnlyIdDevices.Where(a => a.Value.IsCollect == true).Select(a => a.Value).Adapt<List<DeviceDataWithValue>>();
foreach (var item in tcpDmtpService.Clients)
foreach (var item in TcpDmtpService.Clients)
{
// 将 GlobalData.CollectDevices 和 GlobalData.Variables 同步到从站
await item.GetDmtpRpcActor().InvokeAsync(
nameof(ReverseCallbackServer.UpData), null, waitInvoke, deviceRunTimes).ConfigureAwait(false);
log?.LogTrace($"{item.GetIPPort()} Update StandbyStation data success");
LogMessage?.LogTrace($"{item.GetIPPort()} Update StandbyStation data success");
}
}
@@ -177,9 +173,9 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
catch (Exception ex)
{
// 输出警告日志,指示同步数据到从站时发生错误
log?.LogWarning(ex, "Synchronize data to standby site error");
LogMessage?.LogWarning(ex, "Synchronize data to standby site error");
}
await Task.Delay(syncInterval, stoppingToken).ConfigureAwait(false);
await Task.Delay(RedundancyOptions.SyncInterval, stoppingToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@@ -189,17 +185,14 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
}
catch (Exception ex)
{
log?.LogWarning(ex, "Execute");
LogMessage?.LogWarning(ex, "Execute");
}
}
/// <summary>
/// 从站
/// </summary>
/// <param name="tcpDmtpClient">服务</param>
/// <param name="redundancy">冗余配置</param>
/// <param name="stoppingToken">取消任务的 CancellationToken</param>
private async ValueTask DoSlaveWork(TcpDmtpClient tcpDmtpClient, RedundancyOptions redundancy, CancellationToken stoppingToken)
private async Task DoSlaveWork(object? state, CancellationToken stoppingToken)
{
// 延迟一段时间,避免过于频繁地执行任务
await Task.Delay(5000, stoppingToken).ConfigureAwait(false);
@@ -216,31 +209,31 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
try
{
await tcpDmtpClient.TryConnectAsync().ConfigureAwait(false);
await TcpDmtpClient.TryConnectAsync().ConfigureAwait(false);
{
// 初始化读取错误计数器
var readErrorCount = 0;
// 当读取错误次数小于最大错误计数时循环执行
while (readErrorCount < redundancy.MaxErrorCount)
while (readErrorCount < RedundancyOptions.MaxErrorCount)
{
try
{
// 发送 Ping 请求以检查设备是否在线,超时时间为 10000 毫秒
online = await tcpDmtpClient.PingAsync(10000).ConfigureAwait(false);
online = await TcpDmtpClient.PingAsync(10000).ConfigureAwait(false);
if (online)
break;
else
{
readErrorCount++;
await Task.Delay(redundancy.SyncInterval, stoppingToken).ConfigureAwait(false);
await Task.Delay(RedundancyOptions.SyncInterval, stoppingToken).ConfigureAwait(false);
}
}
catch
{
// 捕获异常,增加读取错误计数器
readErrorCount++;
await Task.Delay(redundancy.SyncInterval, stoppingToken).ConfigureAwait(false);
await Task.Delay(RedundancyOptions.SyncInterval, stoppingToken).ConfigureAwait(false);
}
}
}
@@ -255,7 +248,7 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
else
{
// 如果设备在线
LogMessage?.LogTrace($"Ping ActiveStation {redundancy.MasterUri} success");
LogMessage?.LogTrace($"Ping ActiveStation {RedundancyOptions.MasterUri} success");
await StandbyAsync().ConfigureAwait(false);
}
}
@@ -350,12 +343,11 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
{
if (RedundancyOptions.IsMaster)
{
RedundancyTask = new DoTask(a => DoMasterWork(TcpDmtpService, RedundancyOptions.SyncInterval, LogMessage, a), LogMessage); // 创建新的任务
RedundancyTask = new DoTask(DoMasterWork, LogMessage); // 创建新的任务
}
else
{
RedundancyTask = new DoTask(a => DoSlaveWork(TcpDmtpClient, RedundancyOptions, a), LogMessage); // 创建新的任务
RedundancyTask = new DoTask(DoSlaveWork, LogMessage); // 创建新的任务
}
RedundancyTask?.Start(default); // 启动任务

View File

@@ -35,7 +35,7 @@ internal sealed class RpcService : IRpcService
public RpcService(IStringLocalizer<RpcService> localizer)
{
Localizer = localizer;
Task.Factory.StartNew(async () => await RpcLogInsertAsync().ConfigureAwait(false), TaskCreationOptions.LongRunning);
Task.Factory.StartNew(RpcLogInsertAsync, TaskCreationOptions.LongRunning);
_rpcLogOptions = App.GetOptions<RpcLogOptions>();
}

View File

@@ -205,11 +205,15 @@ internal sealed class RulesEngineHostedService : BackgroundService, IRulesEngine
dispatchService.Dispatch(null);
_ = Task.Factory.StartNew(async () =>
_ = Task.Factory.StartNew(async (state) =>
{
if (state is not Dictionary<RulesLog, Diagram> diagrams)
{
return;
}
while (!cancellationToken.IsCancellationRequested)
{
foreach (var item in Diagrams?.Values?.SelectMany(a => a.Nodes) ?? new List<NodeModel>())
foreach (var item in diagrams?.Values?.SelectMany(a => a.Nodes) ?? new List<NodeModel>())
{
if (item is IExexcuteExpressionsBase)
{
@@ -218,7 +222,7 @@ internal sealed class RulesEngineHostedService : BackgroundService, IRulesEngine
}
await Task.Delay(60000, cancellationToken).ConfigureAwait(false);
}
}, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
}, Diagrams, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
}

View File

@@ -27,7 +27,9 @@
<PropertyGroup>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Admin\BlazorSetParametersAsyncGenerator\BlazorSetParametersAsyncGenerator.csproj" PrivateAssets="all" OutputItemType="Analyzer" />
</ItemGroup>
</Project>

View File

@@ -45,6 +45,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Admin\BlazorSetParametersAsyncGenerator\BlazorSetParametersAsyncGenerator.csproj" PrivateAssets="all" OutputItemType="Analyzer" />
</ItemGroup>

View File

@@ -12,7 +12,7 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.376.213" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.376.232" />
</ItemGroup>
<ItemGroup>

View File

@@ -180,7 +180,7 @@ public partial class QuestDBProducer : BusinessBaseWithCacheIntervalVariableMode
await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false);
}
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
await UpdateVarModelMemory(cancellationToken).ConfigureAwait(false);
await UpdateVarModelsMemory(cancellationToken).ConfigureAwait(false);

View File

@@ -172,8 +172,15 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel<
.Map(dest => dest.Value, src => src.Value == null ? string.Empty : src.Value.ToString() ?? string.Empty)
.Map(dest => dest.CreateTime, (src) => DateTime.Now);
if (_businessPropertyWithCacheInterval.BusinessUpdateEnum == BusinessUpdateEnum.Interval && _driverPropertys.IsReadDB)
{
GlobalData.VariableValueChangeEvent += VariableValueChange;
}
await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
}
public override Task AfterVariablesChangedAsync(CancellationToken cancellationToken)
{
@@ -220,7 +227,7 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel<
await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false);
}
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
if (_driverPropertys.IsReadDB)
{

View File

@@ -91,7 +91,7 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheVariableModel<Histor
return base.ProtectedStartAsync(cancellationToken);
}
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
await Update(cancellationToken).ConfigureAwait(false);
}

View File

@@ -199,7 +199,7 @@ public partial class TDengineDBProducer : BusinessBaseWithCacheIntervalVariableM
await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false);
}
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
await UpdateVarModelMemory(cancellationToken).ConfigureAwait(false);
await UpdateVarModelsMemory(cancellationToken).ConfigureAwait(false);

View File

@@ -23,7 +23,7 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableBasic
/// <inheritdoc/>
public override bool IsConnected() => success;
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
await Update(cancellationToken).ConfigureAwait(false);
}

View File

@@ -87,7 +87,7 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl
base.Dispose(disposing);
}
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
await Update(cancellationToken).ConfigureAwait(false);

View File

@@ -150,7 +150,7 @@ public class ModbusSlave : BusinessBase
}
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
//获取设备连接状态
if (IsConnected())

View File

@@ -176,7 +176,7 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBa
}
}
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
var clientResult = await TryMqttClientAsync(cancellationToken).ConfigureAwait(false);
if (!clientResult.IsSuccess)

View File

@@ -260,7 +260,7 @@ public partial class MqttCollect : CollectBase
private volatile bool success;
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
var clientResult = await TryMqttClientAsync(cancellationToken).ConfigureAwait(false);
if (!clientResult.IsSuccess)

View File

@@ -102,7 +102,7 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBa
}
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
await Update(cancellationToken).ConfigureAwait(false);

View File

@@ -90,7 +90,7 @@ public class OpcDaMaster : CollectBase
}
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
if (_driverProperties.ActiveSubscribe)
{

View File

@@ -104,7 +104,7 @@ public class OpcUaMaster : CollectBase
}
private TimeTick checkTimeTick = new("60000");
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
if (_plc.Session == null)
{

View File

@@ -153,7 +153,7 @@ public partial class OpcUaServer : BusinessBase
await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false);
}
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
try
{

View File

@@ -22,27 +22,27 @@
</ProjectReference>
<ProjectReference Include="..\ThingsGateway.Foundation.OpcUa\ThingsGateway.Foundation.OpcUa.csproj" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Server" Version="1.5.376.213" GeneratePathProperty="true">
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Server" Version="1.5.376.232" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.376.213" GeneratePathProperty="true">
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.376.232" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="1.5.376.213" GeneratePathProperty="true">
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="1.5.376.232" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Core" Version="1.5.376.213" GeneratePathProperty="true">
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Core" Version="1.5.376.232" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Configuration" Version="1.5.376.213" GeneratePathProperty="true">
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Configuration" Version="1.5.376.232" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Security.Certificates" Version="1.5.376.213" GeneratePathProperty="true">
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Security.Certificates" Version="1.5.376.232" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>

View File

@@ -62,7 +62,7 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<Vari
base.Dispose(disposing);
}
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
if (_channel == null)
{

View File

@@ -59,7 +59,7 @@ public partial class Synchronization : BusinessBase, IRpcDriver
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
protected override async Task ProtectedExecuteAsync(CancellationToken cancellationToken)
{
try
{

View File

@@ -70,7 +70,7 @@
// /// 通讯失败时,更新设备状态,调用<see cref="DeviceRuntime.SetDeviceStatus(DateTime?, int?, string)"/>
// /// <br></br>
// /// </summary>
// protected override ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
// protected override Task ProtectedExecuteAsync(CancellationToken cancellationToken)
// {
// return base.ProtectedExecuteAsync(cancellationToken);
// }

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>10.8.0</Version>
<Version>10.8.2</Version>
</PropertyGroup>
<ItemGroup>