Files
WCS/Plugins/Driver/Cowain.Driver/AlarmHostedService.cs
2026-03-02 09:08:20 +08:00

429 lines
19 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.
using CommunityToolkit.Mvvm.Messaging;
using Cowain.Base.Helpers;
using Cowain.Base.ViewModels;
using Microsoft.Extensions.Logging;
using Plugin.Cowain.Base.Models;
using Plugin.Cowain.Driver.Abstractions;
using Plugin.Cowain.Driver.IServices;
using Plugin.Cowain.Driver.ViewModels;
using System.Text.Json;
using System.Threading.Tasks.Dataflow;
namespace Plugin.Cowain.Driver;
public class AlarmHostedService : BackgroundHostedService
{
private readonly IMessenger _messenger; // MVVM工具包的消息器
private readonly IAlarmService _alarmService;
private readonly ILogger<AlarmHostedService> _logger;
private IDeviceMonitor _deviceMonitor;
private readonly IAlarmGroupService _alarmGroupService;
private readonly IAlarmLevelService _alarmLevelService;
// 缓存系统报警组ID和错误报警级别ID避免重复查询数据库
private int _systemAlarmGroupId = 1;
private int _errorAlarmLevelId = 1;
private List<AlarmGroupViewModel>? alarmGroups;
private List<AlarmLevelViewModel>? alarmLevels;
private readonly SemaphoreSlim _alarmsSemaphore = new(1, 1);
private readonly SemaphoreSlim _cacheSemaphore = new(1, 1); // 保护缓存初始化的信号量
public AlarmHostedService(IDeviceMonitor deviceMonitor, IAlarmService alarmService, IAlarmGroupService alarmGroupService, IMessenger messenger,
IAlarmLevelService alarmLevelService, ILogger<AlarmHostedService> logger)
{
_deviceMonitor = deviceMonitor;
_alarmService = alarmService;
_alarmGroupService = alarmGroupService;
_alarmLevelService = alarmLevelService;
_messenger = messenger;
_logger = logger;
}
/// <summary>
/// 初始化报警配置缓存(查询"系统"组ID和"错误"级别ID
/// </summary>
private async Task InitAlarmConfigCacheAsync(CancellationToken cancellationToken)
{
// 新增:处理初始化时的取消异常
if (cancellationToken.IsCancellationRequested) return;
await _cacheSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
// 查询名称为"系统"的报警组ID
alarmGroups = await _alarmGroupService.GetAllAsync().ConfigureAwait(false);
var systemGroup = alarmGroups.FirstOrDefault(g => string.Equals(g.Name, "系统", StringComparison.Ordinal));
if (systemGroup != null)
{
_systemAlarmGroupId = systemGroup.Id;
_logger.LogInformation($"成功获取「系统」报警组ID{_systemAlarmGroupId}");
}
else
{
_logger.LogWarning("未查询到名称为「系统」的报警组将使用默认ID1");
}
// 查询名称为"错误"的报警级别ID
alarmLevels = await _alarmLevelService.GetAllAsync().ConfigureAwait(false);
var errorLevel = alarmLevels.FirstOrDefault(l => string.Equals(l.Name, "错误", StringComparison.Ordinal));
if (errorLevel != null)
{
_errorAlarmLevelId = errorLevel.Id;
_logger.LogInformation($"成功获取「错误」报警级别ID{_errorAlarmLevelId}");
}
else
{
_logger.LogWarning("未查询到名称为「错误」的报警级别将使用默认ID1");
}
}
catch (OperationCanceledException)
{
// 正常取消,仅日志记录,不抛异常
_logger.LogInformation("报警配置缓存初始化被取消");
}
catch (Exception ex)
{
_logger.LogError(ex, "初始化报警配置缓存失败将使用默认ID1");
}
finally
{
// 新增:确保信号量释放(即使取消也释放)
if (_cacheSemaphore.CurrentCount == 0)
{
_cacheSemaphore.Release();
}
}
}
[LogAndSwallow]
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
var t = Task.Factory.StartNew(async () =>
{
try
{
// 初始化报警配置缓存(仅执行一次)
await InitAlarmConfigCacheAsync(stoppingToken).ConfigureAwait(false);
// 延时5秒等待PLC连接完成
await Task.Delay(5000, stoppingToken).ConfigureAwait(false);
while (!stoppingToken.IsCancellationRequested)
{
try //内层try捕获单次循环的取消异常
{
// 并行遍历设备集合
// 修改1内层用ct而非stoppingToken避免令牌混用
await Parallel.ForEachAsync(_deviceMonitor.Devices, stoppingToken, async (dev, ct) =>
{
// 检查内层令牌是否取消
if (ct.IsCancellationRequested) return;
if (!dev.IsConnected)
{
// 通信故障发生 - 使用查询到的Group和LevelID替代硬编码的1
var newAlarm = new AlarmViewModel
{
TagId = 100000 + dev.Id,
StartTime = DateTime.Now,
Status = true,
Group = _systemAlarmGroupId, // 从缓存获取"系统"组ID
Level = _errorAlarmLevelId, // 从缓存获取"错误"级别ID
GroupName = alarmGroups?.FirstOrDefault(g => g.Id == _systemAlarmGroupId)?.Name ?? "系统",
LevelName = alarmLevels?.FirstOrDefault(l => l.Id == _errorAlarmLevelId)?.Name ?? "错误",
Color = alarmLevels?.FirstOrDefault(l => l.Id == _errorAlarmLevelId)?.Color ?? "Red",
Desc = $"{dev.DeviceName}:设备通信故障",
};
// 修改2传递内层ct而非stoppingToken
await AddAlarmAsync(newAlarm, ct).ConfigureAwait(false);
return;
}
else
{
// 通信恢复,清除通信报警
var clearAlarm = new AlarmViewModel
{
TagId = 100000 + dev.Id,
};
// 修改2传递内层ct而非stoppingToken
await RemoveAlarmAsync(clearAlarm, ct).ConfigureAwait(false);
}
// 快照变量列表并筛选启用报警的变量,避免其他线程修改集合
var alarmVariables = (dev.Variables ?? Enumerable.Empty<VariableViewModel>()).Where(v => v.AlarmEnable).ToList();
// 并行处理每个变量的报警逻辑
// 修改3内层Parallel用ctInner传递ctInner到异步方法
await Parallel.ForEachAsync(alarmVariables, ct, async (item, ctInner) =>
{
if (ctInner.IsCancellationRequested) return;
if (!item.IsSuccess || string.IsNullOrEmpty(item.Value))
{
var clearAlarm = new AlarmViewModel
{
TagId = item.Id,
};
await RemoveAlarmAsync(clearAlarm, ctInner).ConfigureAwait(false);
}
else
{
if (item.Value == item.AlarmValue)
{
var newAlarm = new AlarmViewModel
{
TagId = item.Id,
StartTime = DateTime.Now,
Status = true,
Group = item.AlarmGroup,
Level = item.AlarmLevel,
GroupName = alarmGroups?.FirstOrDefault(g => g.Id == item.AlarmGroup)?.Name ?? string.Empty,
LevelName = alarmLevels?.FirstOrDefault(l => l.Id == item.AlarmLevel)?.Name ?? string.Empty,
Color = alarmLevels?.FirstOrDefault(l => l.Id == item.AlarmLevel)?.Color ?? "Red",
Desc = $"{item.Address}-{item.AlarmMsg}",
};
await AddAlarmAsync(newAlarm, ctInner).ConfigureAwait(false);
}
else
{
var clearAlarm = new AlarmViewModel
{
TagId = item.Id,
};
await RemoveAlarmAsync(clearAlarm, ctInner).ConfigureAwait(false);
}
}
}).ConfigureAwait(false);
}).ConfigureAwait(false);
// 检查取消状态,避免无效操作
if (stoppingToken.IsCancellationRequested) break;
var alarms = await GetAlarmsAsync(stoppingToken).ConfigureAwait(false);
_messenger.Send(new AlarmChangedMessage(alarms));
// 修改4Task.Delay添加try-catch处理取消异常
try
{
await Task.Delay(500, stoppingToken).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
// Delay被取消跳出循环
break;
}
}
catch (OperationCanceledException)
{
// 单次循环被取消,跳出外层循环
_logger.LogInformation("报警服务循环执行被取消");
break;
}
catch (Exception ex)
{
// 非取消异常,记录日志并继续循环
_logger.LogError(ex, "报警服务单次循环执行异常,将继续下一次循环");
await Task.Delay(500, stoppingToken).ConfigureAwait(false);
}
}
}
catch (OperationCanceledException)
{
// 正常关闭时的取消异常,仅记录日志,不抛异常
_logger.LogInformation("AlarmHostedService 执行线程已正常取消");
}
catch (Exception ex)
{
_logger.LogError(ex, "AlarmHostedService 执行线程发生未预期异常");
}
}, stoppingToken, TaskCreationOptions.LongRunning, TaskScheduler.Default).Unwrap();
return t;
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("AlarmHostedService StopAsync 开始执行");
try
{
if (ExecuteTask != null && !ExecuteTask.IsCompleted)
{
// 修改5优化取消令牌逻辑避免重复取消
using var stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
try
{
// 正确使用异步操作并传递取消令牌
var stopTasks = _deviceMonitor.DeviceThreads
.Select(hostTask => hostTask.StopReadAsync(stoppingCts.Token))
.ToList();
// 新增:超时保护,避免等待过久
await Task.WhenAll(stopTasks).WaitAsync(TimeSpan.FromSeconds(5), stoppingCts.Token).ConfigureAwait(false);
_logger.LogInformation("AlarmHostedService 所有设备线程已停止");
}
catch (TaskCanceledException)
{
_logger.LogInformation("设备线程停止操作被取消");
}
catch (TimeoutException)
{
_logger.LogWarning("设备线程停止操作超时");
}
try
{
// 修改6优雅等待执行任务结束SuppressThrowing避免抛出取消异常
await ExecuteTask.WaitAsync(stoppingCts.Token).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
}
catch
{
// 忽略等待时的所有异常
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "AlarmHostedService StopAsync 执行异常");
}
finally
{
_logger.LogInformation("AlarmHostedService StopAsync 执行完成");
// 确保调用基类StopAsync释放HostedService资源
await base.StopAsync(cancellationToken).ConfigureAwait(false);
}
}
/// <summary>
/// 添加故障
/// </summary>
public async Task AddAlarmAsync(AlarmViewModel alarm, CancellationToken cancellationToken)
{
// 新增:检查取消状态,避免无效操作
if (cancellationToken.IsCancellationRequested) return;
await _alarmsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
var exist = _deviceMonitor.Alarms.FirstOrDefault(a => a.TagId == alarm.TagId);
if (exist == null)
{
_deviceMonitor.Alarms.Add(alarm);
if (_alarmService != null)
{
var inAlarm = await _alarmService.GetInAlarmAsync(alarm.TagId).ConfigureAwait(false);
if (inAlarm == null)
{
var addResult = await _alarmService.AddAsync(alarm).ConfigureAwait(false);
if (!addResult.IsSuccess)
{
_logger.LogError($"添加历史报警异常:{JsonSerializer.Serialize(alarm)}");
}
}
}
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("添加报警操作被取消");
}
catch (Exception ex)
{
_logger.LogError(ex, $"添加报警异常:{alarm.TagId}");
}
finally
{
// 新增:确保信号量释放(即使取消/异常)
if (_alarmsSemaphore.CurrentCount == 0)
{
_alarmsSemaphore.Release();
}
}
}
/// <summary>
/// 移除故障
/// </summary>
public async Task RemoveAlarmAsync(AlarmViewModel alarm, CancellationToken cancellationToken)
{
// 新增:检查取消状态,避免无效操作
if (cancellationToken.IsCancellationRequested) return;
await _alarmsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
var exist = _deviceMonitor.Alarms.FirstOrDefault(a => a.TagId == alarm.TagId);
if (exist != null)
{
//实时报警列表中有此报警
_deviceMonitor.Alarms.Remove(exist);
if (_alarmService != null)
{
var inAlarm = await _alarmService.GetInAlarmAsync(alarm.TagId).ConfigureAwait(false);
if (inAlarm != null)
{
//数据中有此报警,添加解除记录
var removeResult = await _alarmService.CancelAsync(inAlarm).ConfigureAwait(false);
if (!removeResult.IsSuccess)
{
_logger.LogError($"取消历史报警异常:{JsonSerializer.Serialize(alarm)}");
}
}
}
}
else
{
//实时报警列表中无此报警,还需要查询历史报警记录
var inAlarm = await _alarmService.GetInAlarmAsync(alarm.TagId).ConfigureAwait(false);
if (inAlarm != null)
{
//数据中有此报警,添加解除记录
var removeResult = await _alarmService.CancelAsync(alarm).ConfigureAwait(false);
if (!removeResult.IsSuccess)
{
_logger.LogError($"取消历史报警异常:{JsonSerializer.Serialize(alarm)}");
}
}
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("移除报警操作被取消");
}
catch (Exception ex)
{
_logger.LogError(ex, $"移除报警异常:{alarm.TagId}");
}
finally
{
// 新增:确保信号量释放(即使取消/异常)
if (_alarmsSemaphore.CurrentCount == 0)
{
_alarmsSemaphore.Release();
}
}
}
public async Task<List<AlarmViewModel>> GetAlarmsAsync(CancellationToken cancellationToken)
{
// 新增:检查取消状态,避免无效操作
if (cancellationToken.IsCancellationRequested) return new List<AlarmViewModel>();
await _alarmsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
return [.. _deviceMonitor.Alarms];
}
catch (OperationCanceledException)
{
_logger.LogInformation("获取报警列表操作被取消");
return new List<AlarmViewModel>();
}
finally
{
// 新增:确保信号量释放(即使取消/异常)
if (_alarmsSemaphore.CurrentCount == 0)
{
_alarmsSemaphore.Release();
}
}
}
}