@ -28,32 +28,6 @@ namespace Haoliang.Core.Services
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 ;
@ -72,14 +46,11 @@ namespace Haoliang.Core.Services
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 ;
@ -93,7 +64,6 @@ namespace Haoliang.Core.Services
throw new KeyNotFoundException ( $"Alarm with ID {alarmId} not found" ) ;
}
// 更新字段
alarm . AlarmId = alarmId ;
alarm . UpdateTime = DateTime . Now ;
@ -185,464 +155,4 @@ namespace Haoliang.Core.Services
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 ( ) ;
}
}