Files
ThingsGateway/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/Cache/BusinessBaseWithCacheInterval.cs
2025-07-07 15:10:06 +08:00

342 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.Extension.Generic;
using TouchSocket.Core;
namespace ThingsGateway.Gateway.Application;
/// <summary>
/// 业务插件,额外实现变量、设备、变量间隔上传
/// </summary>
public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
{
/// <summary>
/// 获取具体业务属性的缓存设置。
/// </summary>
protected sealed override BusinessPropertyWithCache _businessPropertyWithCache => _businessPropertyWithCacheInterval;
/// <summary>
/// 获取具体业务属性的缓存间隔设置。
/// </summary>
protected abstract BusinessPropertyWithCacheInterval _businessPropertyWithCacheInterval { get; }
protected internal override async Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
{
if (AlarmModelEnable)
{
GlobalData.AlarmChangedEvent -= AlarmValueChange;
GlobalData.ReadOnlyRealAlarmIdVariables?.ForEach(a =>
{
AlarmValueChange(a.Value);
});
GlobalData.AlarmChangedEvent += AlarmValueChange;
// 解绑全局数据的事件
}
if (DevModelEnable)
{
// 如果不是间隔上传,则订阅全局变量值改变事件和设备状态改变事件,并触发一次事件处理
if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval)
{
GlobalData.DeviceStatusChangeEvent += DeviceStatusChange;
}
}
if (VarModelEnable)
{
// 注册变量值变化事件处理程序
if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval)
{
GlobalData.VariableValueChangeEvent += VariableValueChange;
}
}
await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
}
public override async Task AfterVariablesChangedAsync(CancellationToken cancellationToken)
{
if (AlarmModelEnable || DevModelEnable || VarModelEnable)
{
// 如果业务属性指定了全部变量,则设置当前设备的变量运行时列表和采集设备列表
if (_businessPropertyWithCacheInterval.IsAllVariable)
{
LogMessage?.LogInformation("Refresh variable");
IdVariableRuntimes.Clear();
IdVariableRuntimes.AddRange(GlobalData.GetEnableVariables().ToDictionary(a => a.Id));
CollectDevices = GlobalData.GetEnableDevices().Where(a => a.IsCollect == true).ToDictionary(a => a.Id);
VariableRuntimeGroups = IdVariableRuntimes.GroupBy(a => a.Value.BusinessGroup ?? string.Empty).ToDictionary(a => a.Key, a => a.Select(a => a.Value).ToList());
}
else
{
await base.AfterVariablesChangedAsync(cancellationToken).ConfigureAwait(false);
}
}
if (DevModelEnable)
{
CollectDevices?.ForEach(a =>
{
if (a.Value.DeviceStatus == DeviceStatusEnum.OnLine && _businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval)
DeviceStatusChange(a.Value, a.Value.AdaptDeviceBasicData());
});
}
if (VarModelEnable)
{
// 触发一次变量值变化事件
IdVariableRuntimes.ForEach(a =>
{
if (a.Value.IsOnline && _businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval)
VariableValueChange(a.Value, a.Value.AdaptVariableBasicData());
});
}
}
/// <summary>
/// 当报警状态变化时触发此方法。如果不需要进行报警上传,则可以忽略此方法。通常情况下,需要在此方法中执行 <see cref="BusinessBaseWithCache.AddQueueAlarmModel"/> 方法。
/// </summary>
/// <param name="alarmVariable">报警变量</param>
protected virtual void AlarmChange(AlarmVariable alarmVariable)
{
// 在报警状态变化时执行的自定义逻辑
}
/// <summary>
/// 当设备状态变化时触发此方法。如果不需要进行设备上传,则可以忽略此方法。通常情况下,需要在此方法中执行 <see cref="BusinessBaseWithCache.AddQueueDevModel"/> 方法。
/// </summary>
/// <param name="deviceRuntime">设备运行时信息</param>
/// <param name="deviceData">设备数据</param>
protected virtual void DeviceChange(DeviceRuntime deviceRuntime, DeviceBasicData deviceData)
{
// 在设备状态变化时执行的自定义逻辑
}
/// <summary>
/// 当设备状态定时变化时触发此方法。如果不需要进行设备上传,则可以忽略此方法。通常情况下,需要在此方法中执行 <see cref="BusinessBaseWithCache.AddQueueDevModel"/> 方法。
/// </summary>
/// <param name="deviceRuntime">设备运行时信息</param>
/// <param name="deviceData">设备数据</param>
protected virtual void DeviceTimeInterval(DeviceRuntime deviceRuntime, DeviceBasicData deviceData)
{
// 在设备状态变化时执行的自定义逻辑
}
/// <summary>
/// 释放资源方法
/// </summary>
protected override void Dispose(bool disposing)
{
// 解绑事件
GlobalData.AlarmChangedEvent -= AlarmValueChange;
GlobalData.VariableValueChangeEvent -= VariableValueChange;
GlobalData.DeviceStatusChangeEvent -= DeviceStatusChange;
// 清空内存队列
_memoryAlarmModelQueue.Clear();
_memoryDevModelQueue.Clear();
_memoryVarModelQueue.Clear();
_memoryVarModelsQueue.Clear();
base.Dispose(disposing);
}
/// <summary>
/// 间隔上传数据的方法
/// </summary>
protected void IntervalInsert(object? state, CancellationToken cancellationToken)
{
if (CurrentDevice?.Pause != false)
{
return;
}
// 如果业务属性的缓存为间隔上传,则根据定时器间隔执行相应操作
if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Change)
{
if (VarModelEnable)
{
try
{
if (LogMessage?.LogLevel <= LogLevel.Debug)
LogMessage?.LogDebug($"Interval {typeof(VariableBasicData).Name} data, count {IdVariableRuntimes.Count}");
// 间隔推送全部变量
var variableRuntimes = IdVariableRuntimes.Select(a => a.Value);
VariableTimeInterval(variableRuntimes, variableRuntimes.AdaptIEnumerableVariableBasicData());
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, AppResource.IntervalInsertVariableFail);
}
}
if (DevModelEnable)
{
try
{
if (CollectDevices != null)
{
if (LogMessage?.LogLevel <= LogLevel.Debug)
LogMessage?.LogDebug($"Interval {typeof(DeviceBasicData).Name} data, count {CollectDevices.Count}");
// 间隔推送全部设备
foreach (var deviceRuntime in CollectDevices.Select(a => a.Value))
{
DeviceTimeInterval(deviceRuntime, deviceRuntime.AdaptDeviceBasicData());
}
}
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, AppResource.IntervalInsertDeviceFail);
}
}
}
}
protected override List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken)
{
var list = base.ProtectedGetTasks(cancellationToken);
list.Add(ScheduledTaskHelper.GetTask(_businessPropertyWithCacheInterval.BusinessInterval, IntervalInsert, null, LogMessage, cancellationToken));
return list;
}
/// <summary>
/// 当变量状态变化时触发此方法。如果不需要进行变量上传,则可以忽略此方法。通常情况下,需要在此方法中执行 <see cref="BusinessBaseWithCache.AddQueueVarModel(CacheDBItem{VariableBasicData})"/> 方法。
/// </summary>
/// <param name="variableRuntime">变量运行时信息</param>
/// <param name="variable">变量数据</param>
protected virtual void VariableChange(VariableRuntime variableRuntime, VariableBasicData variable)
{
// 在变量状态变化时执行的自定义逻辑
}
/// <summary>
/// 当变量定时变化时触发此方法。如果不需要进行变量上传,则可以忽略此方法。通常情况下,需要在此方法中执行 <see cref="BusinessBaseWithCache.AddQueueVarModel(CacheDBItem{VariableBasicData})"/> 方法。
/// </summary>
/// <param name="variableRuntimes">变量运行时信息</param>
/// <param name="variables">变量数据</param>
protected virtual void VariableTimeInterval(IEnumerable<VariableRuntime> variableRuntimes, IEnumerable<VariableBasicData> variables)
{
// 在变量状态变化时执行的自定义逻辑
}
/// <summary>
/// 当报警值发生变化时触发此事件处理方法。该方法内部会检查是否需要进行报警上传,如果需要,则调用 <see cref="AlarmChange(AlarmVariable)"/> 方法。
/// </summary>
/// <param name="alarmVariable">报警变量</param>
protected void AlarmValueChange(AlarmVariable alarmVariable)
{
if (CurrentDevice?.Pause != false)
return;
if (TaskSchedulerLoop.Stoped) return;
if (!AlarmModelEnable) return;
// 如果业务属性的缓存为间隔上传,则不执行后续操作
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
{
// 检查当前设备的变量是否包含此报警变量,如果包含,则触发报警变量的变化处理方法
if (IdVariableRuntimes.ContainsKey(alarmVariable.Id))
AlarmChange(alarmVariable);
}
}
public override void PauseThread(bool pause)
{
lock (this)
{
var oldV = CurrentDevice.Pause;
base.PauseThread(pause);
if (!pause && oldV != pause)
{
if (AlarmModelEnable)
{
GlobalData.ReadOnlyRealAlarmIdVariables?.ForEach(a =>
{
AlarmChange(a.Value);
});
}
if (DevModelEnable)
{
CollectDevices?.ForEach(a =>
{
if (a.Value.DeviceStatus == DeviceStatusEnum.OnLine && _businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval)
DeviceStatusChange(a.Value, a.Value.AdaptDeviceBasicData());
});
}
if (VarModelEnable)
{
IdVariableRuntimes.ForEach(a =>
{
if (a.Value.IsOnline && _businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval)
VariableValueChange(a.Value, a.Value.AdaptVariableBasicData());
});
}
}
}
}
/// <summary>
/// 当设备状态发生变化时触发此事件处理方法。该方法内部会检查是否需要进行设备上传,如果需要,则调用 <see cref="DeviceChange(DeviceRuntime, DeviceBasicData)"/> 方法。
/// </summary>
/// <param name="deviceRuntime">设备运行时信息</param>
/// <param name="deviceData">设备数据</param>
protected void DeviceStatusChange(DeviceRuntime deviceRuntime, DeviceBasicData deviceData)
{
if (CurrentDevice?.Pause != false)
return;
if (TaskSchedulerLoop.Stoped) return;
if (!DevModelEnable) return;
// 如果业务属性的缓存为间隔上传,则不执行后续操作
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
{
// 检查当前设备的设备列表是否包含此设备,如果包含,则触发设备的状态变化处理方法
if (CollectDevices?.ContainsKey(deviceData.Id) == true)
DeviceChange(deviceRuntime, deviceData);
}
}
/// <summary>
/// 当变量值发生变化时触发此事件处理方法。该方法内部会检查是否需要进行变量上传,如果需要,则调用 <see cref="VariableChange(VariableRuntime, VariableBasicData)"/> 方法。
/// </summary>
/// <param name="variableRuntime">变量运行时信息</param>
/// <param name="variable">变量数据</param>
protected void VariableValueChange(VariableRuntime variableRuntime, VariableBasicData variable)
{
if (CurrentDevice?.Pause != false)
return;
if (!VarModelEnable) return;
if (TaskSchedulerLoop.Stoped) return;
// 如果业务属性的缓存为间隔上传,则不执行后续操作
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
{
// 检查当前设备的变量是否包含此变量,如果包含,则触发变量的变化处理方法
if (IdVariableRuntimes.ContainsKey(variable.Id))
VariableChange(variableRuntime, variable);
}
}
}