You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

648 lines
24 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Haoliang.Models.System;
using Haoliang.Models.Device;
using Haoliang.Models.DataCollection;
using Haoliang.Data.Repositories;
using Haoliang.Core.Services;
namespace Haoliang.Core.Services
{
public interface IAlarmService
{
Task<Alarm> CreateAlarmAsync(Alarm alarm);
Task<Alarm> UpdateAlarmAsync(int alarmId, Alarm alarm);
Task<bool> DeleteAlarmAsync(int alarmId);
Task<Alarm> GetAlarmByIdAsync(int alarmId);
Task<IEnumerable<Alarm>> GetAllAlarmsAsync();
Task<IEnumerable<Alarm>> GetAlarmsByDeviceAsync(int deviceId);
Task<IEnumerable<Alarm>> GetAlarmsByTypeAsync(AlarmType type);
Task<IEnumerable<Alarm>> GetActiveAlarmsAsync();
Task<IEnumerable<Alarm>> GetAlarmsByDateRangeAsync(DateTime startDate, DateTime endDate);
Task<bool> ResolveAlarmAsync(int alarmId, string resolutionNote);
Task<bool> AcknowledgeAlarmAsync(int alarmId, string acknowledgeNote);
Task<AlarmStatistics> GetAlarmStatisticsAsync(DateTime date);
Task<IEnumerable<Alarm>> GetCriticalAlarmsAsync();
Task<IEnumerable<Alarm>> GetDeviceAlarmsAsync(int deviceId, int days = 7);
}
public interface IAlarmRuleService
{
Task<AlarmRule> CreateAlarmRuleAsync(AlarmRule rule);
Task<AlarmRule> UpdateAlarmRuleAsync(int ruleId, AlarmRule rule);
Task<bool> DeleteAlarmRuleAsync(int ruleId);
Task<AlarmRule> GetAlarmRuleByIdAsync(int ruleId);
Task<IEnumerable<AlarmRule>> GetAllAlarmRulesAsync();
Task<IEnumerable<AlarmRule>> GetActiveAlarmRulesAsync();
Task<IEnumerable<AlarmRule>> GetRulesByDeviceAsync(int deviceId);
Task<bool> EvaluateAlarmRuleAsync(AlarmRule rule, DeviceCurrentStatus status);
Task<Alarm> GenerateAlarmFromRuleAsync(AlarmRule rule, DeviceCurrentStatus status);
Task TestAlarmRuleAsync(int ruleId);
}
public interface IAlarmNotificationService
{
Task SendAlarmNotificationAsync(Alarm alarm);
Task SendBulkAlarmNotificationsAsync(IEnumerable<Alarm> alarms);
Task<bool> SendSmsNotificationAsync(string phoneNumber, string message);
Task<bool> SendEmailNotificationAsync(string email, string subject, string message);
Task<bool> SendWechatNotificationAsync(string openId, string message);
Task<IEnumerable<AlarmNotification>> GetNotificationHistoryAsync(DateTime startDate, DateTime endDate);
Task<bool> ConfigureNotificationChannelAsync(NotificationChannel channel);
Task<IEnumerable<NotificationChannel>> GetAvailableChannelsAsync();
}
public class AlarmManager : IAlarmService
{
private readonly IAlarmRepository _alarmRepository;
private readonly IAlarmRuleService _alarmRuleService;
private readonly IAlarmNotificationService _notificationService;
public AlarmManager(
IAlarmRepository alarmRepository,
IAlarmRuleService alarmRuleService,
IAlarmNotificationService notificationService)
{
_alarmRepository = alarmRepository;
_alarmRuleService = alarmRuleService;
_notificationService = notificationService;
}
public async Task<Alarm> CreateAlarmAsync(Alarm alarm)
{
// 设置初始状态
alarm.AlarmStatus = AlarmStatus.Active;
alarm.CreateTime = DateTime.Now;
alarm.UpdateTime = DateTime.Now;
var createdAlarm = await _alarmRepository.AddAsync(alarm);
// 发送告警通知
await _notificationService.SendAlarmNotificationAsync(createdAlarm);
return createdAlarm;
}
public async Task<Alarm> UpdateAlarmAsync(int alarmId, Alarm alarm)
{
var existingAlarm = await _alarmRepository.GetByIdAsync(alarmId);
if (existingAlarm == null)
{
throw new KeyNotFoundException($"Alarm with ID {alarmId} not found");
}
// 更新字段
alarm.AlarmId = alarmId;
alarm.UpdateTime = DateTime.Now;
var updatedAlarm = await _alarmRepository.UpdateAsync(alarm);
return updatedAlarm;
}
public async Task<bool> DeleteAlarmAsync(int alarmId)
{
return await _alarmRepository.DeleteAsync(alarmId);
}
public async Task<Alarm> GetAlarmByIdAsync(int alarmId)
{
return await _alarmRepository.GetByIdAsync(alarmId);
}
public async Task<IEnumerable<Alarm>> GetAllAlarmsAsync()
{
return await _alarmRepository.GetAllAsync();
}
public async Task<IEnumerable<Alarm>> GetAlarmsByDeviceAsync(int deviceId)
{
return await _alarmRepository.GetByDeviceIdAsync(deviceId);
}
public async Task<IEnumerable<Alarm>> GetAlarmsByTypeAsync(AlarmType type)
{
return await _alarmRepository.GetByAlarmTypeAsync(type);
}
public async Task<IEnumerable<Alarm>> GetActiveAlarmsAsync()
{
return await _alarmRepository.GetByStatusAsync(AlarmStatus.Active);
}
public async Task<IEnumerable<Alarm>> GetAlarmsByDateRangeAsync(DateTime startDate, DateTime endDate)
{
return await _alarmRepository.GetByDateRangeAsync(startDate, endDate);
}
public async Task<bool> ResolveAlarmAsync(int alarmId, string resolutionNote)
{
var alarm = await _alarmRepository.GetByIdAsync(alarmId);
if (alarm == null)
{
return false;
}
alarm.AlarmStatus = AlarmStatus.Resolved;
alarm.ResolutionNote = resolutionNote;
alarm.ResolvedTime = DateTime.Now;
alarm.UpdateTime = DateTime.Now;
return await _alarmRepository.UpdateAsync(alarm) != null;
}
public async Task<bool> AcknowledgeAlarmAsync(int alarmId, string acknowledgeNote)
{
var alarm = await _alarmRepository.GetByIdAsync(alarmId);
if (alarm == null)
{
return false;
}
alarm.AlarmStatus = AlarmStatus.Acknowledged;
alarm.AcknowledgeNote = acknowledgeNote;
alarm.AcknowledgedTime = DateTime.Now;
alarm.UpdateTime = DateTime.Now;
return await _alarmRepository.UpdateAsync(alarm) != null;
}
public async Task<AlarmStatistics> GetAlarmStatisticsAsync(DateTime date)
{
return await _alarmRepository.GetAlarmStatisticsAsync(date);
}
public async Task<IEnumerable<Alarm>> GetCriticalAlarmsAsync()
{
return await _alarmRepository.GetBySeverityAsync(AlarmSeverity.Critical);
}
public async Task<IEnumerable<Alarm>> GetDeviceAlarmsAsync(int deviceId, int days = 7)
{
var startDate = DateTime.Now.AddDays(-days);
var endDate = DateTime.Now;
return await _alarmRepository.GetByDeviceAndDateRangeAsync(deviceId, startDate, endDate);
}
}
public class AlarmRuleService : IAlarmRuleService
{
private readonly IAlarmRuleRepository _ruleRepository;
private readonly IAlarmRepository _alarmRepository;
private readonly IDeviceRepository _deviceRepository;
private readonly ILoggerService _logger;
public AlarmRuleService(
IAlarmRuleRepository ruleRepository,
IAlarmRepository alarmRepository,
IDeviceRepository deviceRepository,
ILoggerService logger)
{
_ruleRepository = ruleRepository;
_alarmRepository = alarmRepository;
_deviceRepository = deviceRepository;
_logger = logger;
}
public async Task<AlarmRule> CreateAlarmRuleAsync(AlarmRule rule)
{
rule.IsActive = true;
rule.CreateTime = DateTime.Now;
rule.UpdateTime = DateTime.Now;
return await _ruleRepository.AddAsync(rule);
}
public async Task<AlarmRule> UpdateAlarmRuleAsync(int ruleId, AlarmRule rule)
{
var existingRule = await _ruleRepository.GetByIdAsync(ruleId);
if (existingRule == null)
{
throw new KeyNotFoundException($"Alarm rule with ID {ruleId} not found");
}
rule.RuleId = ruleId;
rule.UpdateTime = DateTime.Now;
return await _ruleRepository.UpdateAsync(rule);
}
public async Task<bool> DeleteAlarmRuleAsync(int ruleId)
{
return await _ruleRepository.DeleteAsync(ruleId);
}
public async Task<AlarmRule> GetAlarmRuleByIdAsync(int ruleId)
{
return await _ruleRepository.GetByIdAsync(ruleId);
}
public async Task<IEnumerable<AlarmRule>> GetAllAlarmRulesAsync()
{
return await _ruleRepository.GetAllAsync();
}
public async Task<IEnumerable<AlarmRule>> GetActiveAlarmRulesAsync()
{
return await _ruleRepository.GetByStatusAsync(true);
}
public async Task<IEnumerable<AlarmRule>> GetRulesByDeviceAsync(int deviceId)
{
return await _ruleRepository.GetByDeviceIdAsync(deviceId);
}
public async Task<bool> EvaluateAlarmRuleAsync(AlarmRule rule, DeviceCurrentStatus status)
{
if (!rule.IsActive || status == null)
return false;
try
{
switch (rule.RuleType)
{
case AlarmRuleType.DeviceOffline:
return EvaluateDeviceOfflineRule(rule, status);
case AlarmRuleType.TemperatureHigh:
return EvaluateTemperatureRule(rule, status);
case AlarmRuleType.PressureHigh:
return EvaluatePressureRule(rule, status);
case AlarmRuleType.ProductionStop:
return EvaluateProductionStopRule(rule, status);
case AlarmRuleType.NetworkError:
return EvaluateNetworkErrorRule(rule, status);
default:
return false;
}
}
catch (Exception ex)
{
await _logger.LogErrorAsync($"Failed to evaluate alarm rule {rule.RuleId}: {ex.Message}");
return false;
}
}
public async Task<Alarm> GenerateAlarmFromRuleAsync(AlarmRule rule, DeviceCurrentStatus status)
{
var alarm = new Alarm
{
DeviceId = status.DeviceId,
DeviceCode = "", // Will be populated from device
AlarmType = rule.AlarmType,
Severity = rule.Severity,
Title = rule.AlarmTitle,
Description = rule.AlarmDescription,
AlarmStatus = AlarmStatus.Active,
RuleId = rule.RuleId,
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now
};
// Get device code
var device = await _deviceRepository.GetByIdAsync(status.DeviceId);
if (device != null)
{
alarm.DeviceCode = device.DeviceCode;
}
return alarm;
}
public async Task TestAlarmRuleAsync(int ruleId)
{
var rule = await _ruleRepository.GetByIdAsync(ruleId);
if (rule == null)
throw new KeyNotFoundException($"Alarm rule with ID {ruleId} not found");
// Get a sample device status for testing
var devices = await _deviceRepository.GetAllAsync();
if (!devices.Any())
throw new Exception("No devices available for testing");
var sampleDevice = devices.First();
var deviceStatus = new DeviceCurrentStatus
{
DeviceId = sampleDevice.Id,
Status = "Running",
IsRunning = true,
NCProgram = "O1234",
CumulativeCount = 1000,
RecordTime = DateTime.Now
};
// Evaluate the rule
var shouldTrigger = await EvaluateAlarmRuleAsync(rule, deviceStatus);
if (shouldTrigger)
{
var alarm = await GenerateAlarmFromRuleAsync(rule, deviceStatus);
alarm.Title = $"Test Alarm: {rule.AlarmTitle}";
alarm.Description = $"This is a test alarm for rule '{rule.RuleName}'.";
await _alarmRepository.AddAsync(alarm);
await _alarmRepository.SaveAsync();
await _logger.LogInformationAsync($"Test alarm created for rule {rule.RuleId}");
}
else
{
await _logger.LogInformationAsync($"Rule {rule.RuleId} did not trigger alarm during test");
}
}
private bool EvaluateDeviceOfflineRule(AlarmRule rule, DeviceCurrentStatus status)
{
// Check if device status indicates offline
return status.Status?.ToLower() == "offline" ||
!status.IsRunning ||
string.IsNullOrEmpty(status.NCProgram);
}
private bool EvaluateTemperatureRule(AlarmRule rule, DeviceCurrentStatus status)
{
// Find temperature tag
var temperatureTag = status.Tags?.FirstOrDefault(t => t.Id?.Contains("temp") == true || t.Id?.Contains("温度") == true);
if (temperatureTag == null || temperatureTag.Value == null)
return false;
if (decimal.TryParse(temperatureTag.Value.ToString(), out decimal temperature))
{
return temperature > rule.ThresholdValue;
}
return false;
}
private bool EvaluatePressureRule(AlarmRule rule, DeviceCurrentStatus status)
{
// Find pressure tag
var pressureTag = status.Tags?.FirstOrDefault(t => t.Id?.Contains("pressure") == true || t.Id?.Contains("压力") == true);
if (pressureTag == null || pressureTag.Value == null)
return false;
if (decimal.TryParse(pressureTag.Value.ToString(), out decimal pressure))
{
return pressure > rule.ThresholdValue;
}
return false;
}
private bool EvaluateProductionStopRule(AlarmRule rule, DeviceCurrentStatus status)
{
// Check if device has been stopped for specified duration
var stopDuration = TimeSpan.FromMinutes(rule.ThresholdValue);
return !status.IsRunning &&
status.RecordTime < DateTime.Now.Subtract(stopDuration);
}
private bool EvaluateNetworkErrorRule(AlarmRule rule, DeviceCurrentStatus status)
{
// Check for network-related errors in status
return status.Tags?.Any(t => t.Id?.Contains("error") == true && t.Value?.ToString() == "1") == true;
}
}
public class AlarmNotificationService : IAlarmNotificationService
{
private readonly IAlarmNotificationRepository _notificationRepository;
private readonly IEmailService _emailService;
private readonly ISmsService _smsService;
private readonly IWechatService _wechatService;
private readonly ILoggerService _logger;
private readonly IDeviceRepository _deviceRepository;
public AlarmNotificationService(
IAlarmNotificationRepository notificationRepository,
IEmailService emailService,
ISmsService smsService,
IWechatService wechatService,
ILoggerService logger,
IDeviceRepository deviceRepository)
{
_notificationRepository = notificationRepository;
_emailService = emailService;
_smsService = smsService;
_wechatService = wechatService;
_logger = logger;
_deviceRepository = deviceRepository;
}
public async Task SendAlarmNotificationAsync(Alarm alarm)
{
try
{
var device = await _deviceRepository.GetByIdAsync(alarm.DeviceId);
var notificationChannels = await GetNotificationChannelsForDevice(alarm.DeviceId);
foreach (var channel in notificationChannels)
{
if (channel.IsEnabled)
{
await SendNotificationViaChannel(alarm, channel, device);
}
}
// Log the notification
await LogNotificationSent(alarm, notificationChannels.Count());
}
catch (Exception ex)
{
await _logger.LogErrorAsync($"Failed to send alarm notification for alarm {alarm.AlarmId}: {ex.Message}");
}
}
public async Task SendBulkAlarmNotificationsAsync(IEnumerable<Alarm> alarms)
{
foreach (var alarm in alarms)
{
await SendAlarmNotificationAsync(alarm);
}
}
public async Task<bool> SendSmsNotificationAsync(string phoneNumber, string message)
{
try
{
return await _smsService.SendSmsAsync(phoneNumber, message);
}
catch (Exception ex)
{
await _logger.LogErrorAsync($"Failed to send SMS to {phoneNumber}: {ex.Message}");
return false;
}
}
public async Task<bool> SendEmailNotificationAsync(string email, string subject, string message)
{
try
{
return await _emailService.SendEmailAsync(email, subject, message);
}
catch (Exception ex)
{
await _logger.LogErrorAsync($"Failed to send email to {email}: {ex.Message}");
return false;
}
}
public async Task<bool> SendWechatNotificationAsync(string openId, string message)
{
try
{
return await _wechatService.SendMessageAsync(openId, message);
}
catch (Exception ex)
{
await _logger.LogErrorAsync($"Failed to send WeChat message to {openId}: {ex.Message}");
return false;
}
}
public async Task<IEnumerable<AlarmNotification>> GetNotificationHistoryAsync(DateTime startDate, DateTime endDate)
{
return await _notificationRepository.GetByDateRangeAsync(startDate, endDate);
}
public async Task<bool> ConfigureNotificationChannelAsync(NotificationChannel channel)
{
// Validate channel configuration
if (!ValidateChannelConfiguration(channel))
return false;
// Save channel configuration
return await _notificationRepository.SaveChannelAsync(channel);
}
public async Task<IEnumerable<NotificationChannel>> GetAvailableChannelsAsync()
{
return await _notificationRepository.GetAllChannelsAsync();
}
private async Task<List<NotificationChannel>> GetNotificationChannelsForDevice(int deviceId)
{
// This would typically query the database for device-specific notification settings
// For now, return a default set of channels
return new List<NotificationChannel>
{
new NotificationChannel { ChannelType = NotificationChannelType.Email, IsEnabled = true },
new NotificationChannel { ChannelType = NotificationChannelType.Sms, IsEnabled = false },
new NotificationChannel { ChannelType = NotificationChannelType.WeChat, IsEnabled = false }
};
}
private async Task SendNotificationViaChannel(Alarm alarm, NotificationChannel channel, Device device)
{
var message = FormatAlarmMessage(alarm, device);
var subject = $"Alarm: {alarm.AlarmType} - {device?.DeviceCode}";
switch (channel.ChannelType)
{
case NotificationChannelType.Email:
if (!string.IsNullOrEmpty(channel.Recipient))
{
await SendEmailNotificationAsync(channel.Recipient, subject, message);
}
break;
case NotificationChannelType.Sms:
if (!string.IsNullOrEmpty(channel.Recipient))
{
await SendSmsNotificationAsync(channel.Recipient, message);
}
break;
case NotificationChannelType.WeChat:
if (!string.IsNullOrEmpty(channel.Recipient))
{
await SendWechatNotificationAsync(channel.Recipient, message);
}
break;
}
}
private string FormatAlarmMessage(Alarm alarm, Device device)
{
return $"🚨 Alarm Alert\n\n" +
$"Device: {device?.DeviceCode} ({device?.DeviceName})\n" +
$"Type: {alarm.AlarmType}\n" +
$"Severity: {alarm.Severity}\n" +
$"Title: {alarm.Title}\n" +
$"Description: {alarm.Description}\n" +
$"Time: {alarm.CreateTime:yyyy-MM-dd HH:mm:ss}\n" +
$"Device ID: {alarm.DeviceId}";
}
private bool ValidateChannelConfiguration(NotificationChannel channel)
{
switch (channel.ChannelType)
{
case NotificationChannelType.Email:
return !string.IsNullOrEmpty(channel.Recipient) && channel.Recipient.Contains("@");
case NotificationChannelType.Sms:
return !string.IsNullOrEmpty(channel.Recipient) && channel.Recipient.All(char.IsDigit);
case NotificationChannelType.WeChat:
return !string.IsNullOrEmpty(channel.Recipient);
default:
return false;
}
}
private async Task LogNotificationSent(Alarm alarm, int channelCount)
{
var notification = new AlarmNotification
{
AlarmId = alarm.AlarmId,
NotificationTime = DateTime.Now,
ChannelsUsed = channelCount,
Success = true
};
await _notificationRepository.AddNotificationAsync(notification);
}
}
// Additional supporting classes and interfaces
public interface IEmailService
{
Task<bool> SendEmailAsync(string email, string subject, string message);
}
public interface ISmsService
{
Task<bool> SendSmsAsync(string phoneNumber, string message);
}
public interface IWechatService
{
Task<bool> SendMessageAsync(string openId, string message);
}
public interface IAlarmRuleRepository
{
Task<AlarmRule> AddAsync(AlarmRule rule);
Task<AlarmRule> UpdateAsync(AlarmRule rule);
Task<bool> DeleteAsync(int ruleId);
Task<AlarmRule> GetByIdAsync(int ruleId);
Task<IEnumerable<AlarmRule>> GetAllAsync();
Task<IEnumerable<AlarmRule>> GetByStatusAsync(bool isActive);
Task<IEnumerable<AlarmRule>> GetByDeviceIdAsync(int deviceId);
}
public interface IAlarmNotificationRepository
{
Task<AlarmNotification> AddNotificationAsync(AlarmNotification notification);
Task<IEnumerable<AlarmNotification>> GetByDateRangeAsync(DateTime startDate, DateTime endDate);
Task<bool> SaveChannelAsync(NotificationChannel channel);
Task<IEnumerable<NotificationChannel>> GetAllChannelsAsync();
}
}