using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using System.Collections; // 确保有这个 using public enum AlarmPriority { None, Low, Medium, High } public class AlarmEvent { public int Id; public AlarmPriority Priority; public string Reason; public DateTime Time; } /// /// DCS协议报警管理器 - 统一管理所有报警功能,包括串口通信、界面显示和声音播放 /// public class DCSAlarmManager : MonoBehaviour { public static DCSAlarmManager Instance { get; private set; } // 报警事件,供UI组件订阅 public static event Action OnAlarmRaised; public static event Action OnAlarmCleared; // 所有报警消除时触发 public static event Action OnMuteStateChanged; private ISerialCommunicationService _serialService; private AlarmRecordPersistenceService _recordPersistence; // 静音功能 // private bool _isMuted = false; // private DateTime _muteEndTime = DateTime.MinValue; // 报警记录管理 private readonly List _alarmRecords = new List(); private const int MAX_ALARM_RECORDS = 1000; // 最大记录数 // 10秒内报警相关 private readonly int delayTime = 10; private bool _isPowerAlarmActive = false; private bool _isLowBatteryAlarmActive = false; private bool _isMediumBatteryAlarmActive = false; private int _nextAlarmId = 1; // public bool IsMuted // { // get // { // // 检查定时静音是否已过期 // if (_muteEndTime > DateTime.MinValue && DateTime.Now >= _muteEndTime) // { // _isMuted = false; // _muteEndTime = DateTime.MinValue; // OnMuteStateChanged?.Invoke(false); // } // return _isMuted; // } // } // 报警代码到消息的映射 private readonly Dictionary _alarmCodeMap = new() { { 0x11, (AlarmPriority.Low, "电池电量低(<40%)") }, { 0x21, (AlarmPriority.Medium, "电池电量低(<20%)") }, { 0x31, (AlarmPriority.High, "电池电量空(<5%)") }, { 0x32, (AlarmPriority.High, "BFI数值异常") }, { 0x22, (AlarmPriority.Medium, "激光器温度异常") }, { 0x33, (AlarmPriority.High, "激光器工作异常") }, { 0x34, (AlarmPriority.High, "通信异常") }, { 0x35, (AlarmPriority.Medium, "电池故障") }, { 0x12, (AlarmPriority.Low, "交流电未连接") } }; // 当前活跃的报警 private readonly HashSet _activeAlarms = new(); // 当前活跃报警的最高优先级 private AlarmPriority _currentHighestPriority = AlarmPriority.None; // 报警发送频率限制 private readonly Dictionary _lastAlarmSendTime = new(); private readonly Dictionary _lastAlarmState = new(); // 记录上次报警状态 private const int ALARM_SEND_INTERVAL_MS = 1000; // 同一报警状态1秒内不重复发送 // 启动阶段的通讯异常检测 private bool _communicationErrorDuringStartup = false; public bool HasCommunicationErrorDuringStartup => _communicationErrorDuringStartup; void Awake() { if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } void Start(){ _serialService = ServiceLocator.Get(); _recordPersistence = new AlarmRecordPersistenceService(); InitializeCurrentSettings(); // 从持久化存储加载历史记录 LoadPersistedRecords(); } private void InitializeCurrentSettings() { // 从配置服务加载当前阈值设置 var settingsService = ServiceLocator.Get(); BFIThresholdSettings _settings = new BFIThresholdSettings { MinThreshold = settingsService?.BFILowThreshold ?? _bfiLowThreshold, MaxThreshold = settingsService?.BFIHighThreshold ??_bfiHighThreshold, // EnableLowAlarm = settingsService?.EnableBFIAlarm ?? true, // EnableHighAlarm = settingsService?.EnableBFIAlarm ?? true, AlarmPriority = (AlarmPriority)(settingsService?.BFIAlarmPriority ?? 2) }; UpdateBFIThresholds( _settings.MinThreshold, _settings.MaxThreshold, (int)_settings.AlarmPriority, true ); } /// /// 发送报警控制命令到下位机 /// 统一协调串口命令发送、界面显示和声音播放 /// public void SendAlarmCommand(byte alarmCode, AlarmPriority priority, bool trigger) { if (_serialService == null) return; var now = DateTime.Now; // 检查报警状态是否改变 bool stateChanged = false; if (_lastAlarmState.TryGetValue(alarmCode, out var lastState)) { stateChanged = (lastState != trigger); } else { stateChanged = true; // 首次发送 } // 如果状态未改变,检查重发间隔 if (!stateChanged) { if (_lastAlarmSendTime.TryGetValue(alarmCode, out var lastSendTime)) { var timeSinceLastSend = (now - lastSendTime).TotalMilliseconds; if (timeSinceLastSend < ALARM_SEND_INTERVAL_MS) { return; } else { Debug.Log($"报警重发: 代码=0x{alarmCode:X2}, 状态={trigger}, 距离上次{timeSinceLastSend/1000:F0}秒"); } } } else { Debug.Log($"报警状态变化: 代码=0x{alarmCode:X2}, {lastState}→{trigger}"); } try { // 1. 发送串口命令 byte priorityByte = priority switch { AlarmPriority.Low => 0, AlarmPriority.Medium => 1, AlarmPriority.High => 2, _ => 1 }; byte stateByte = (byte)(trigger ? 1 : 0); _serialService.SendAlarmCommand(alarmCode, priorityByte, stateByte); _lastAlarmSendTime[alarmCode] = now; _lastAlarmState[alarmCode] = trigger; // 更新状态记录 // 更新活跃报警集合 if (trigger) { _activeAlarms.Add(alarmCode); } else { _activeAlarms.Remove(alarmCode); } // 更新当前最高优先级 UpdateHighestPriority(); Debug.Log($"[报警命令] 代码=0x{alarmCode:X2}, 优先级={priority}, 状态={trigger}"); // 2. 记录报警状态变化(触发和解除都记录) if (_alarmCodeMap.TryGetValue(alarmCode, out var alarmInfoForRecord)) { var alarmEvent = new AlarmEvent { Id = _nextAlarmId++, Priority = alarmInfoForRecord.priority, Reason = trigger ? alarmInfoForRecord.message : $"{alarmInfoForRecord.message} (已解除)", Time = DateTime.Now }; // 使用持久化服务保存记录(自动去重) if (_recordPersistence != null) { _recordPersistence.AddRecord(alarmEvent, alarmCode, trigger); } else { // 降级到内存存储 AddAlarmRecord(alarmEvent); } } // 3. 统一触发系统报警显示和声音(仅在触发报警时) if (trigger && _alarmCodeMap.TryGetValue(alarmCode, out var alarmInfo)) { // 检查启动阶段的通讯异常 if (alarmCode == 0x34) { _communicationErrorDuringStartup = true; Debug.LogError("[DCSAlarmManager] 通讯异常(0x34)在启动阶段被触发!"); } TriggerSystemAlarm(alarmCode, alarmInfo.priority, alarmInfo.message); } } catch (Exception ex) { Debug.LogError($"发送报警命令异常: {ex.Message}"); } } /// /// 统一触发系统报警(界面显示 + 声音播放) /// 根据优先级过滤:高优先级报警时不显示中低优先级,中优先级报警时不显示低优先级 /// private void TriggerSystemAlarm(byte alarmCode, AlarmPriority priority, string message) { try { // 检查是否应该显示此报警(优先级过滤) if (!ShouldDisplayAlarm(priority)) { Debug.Log($"[DCS报警管理器] 报警被优先级过滤: [{priority}] {message} (当前最高优先级: {_currentHighestPriority})"); return; } // 触发报警事件,UI组件和AudioService可以订阅此事件 OnAlarmRaised?.Invoke(priority, message); Debug.Log($"[DCS报警管理器] 触发系统报警: [{priority}] {message} (代码: 0x{alarmCode:X2})"); } catch (Exception ex) { Debug.LogError($"触发系统报警异常: {ex.Message}"); } } /// /// 根据设备状态自动检查并发送报警命令 /// 统一协调报警命令发送、界面显示和声音播放 /// public void CheckAndSendAlarms(DeviceStatusData status) { // 检查电源状态报警 CheckPowerAlarms(status.PowerType); // 检查激光器状态报警 CheckLaserAlarms(status.LaserStatus); // 检查电池电量报警(仅在使用电池供电时) CheckBatteryAlarms(status.BatteryLevel, status.PowerType); // 检查温度报警 CheckTemperatureAlarms(status.Temperature); } private IEnumerator SendLowBatteryClearAlarm(float delayInSeconds) { yield return new WaitForSeconds(delayInSeconds); // 等待指定秒数 SendAlarmCommand(0x11, AlarmPriority.Low, false); // 注意:不在这里重置 _isLowBatteryAlarmActive,保持低电状态标记, // 直到电量恢复 >40% 或切换到交流电供电时再重置,避免持续低电重复触发。 } private IEnumerator SendMediumBatteryClearAlarm(float delayInSeconds) { yield return new WaitForSeconds(delayInSeconds); // 等待指定秒数 SendAlarmCommand(0x21, AlarmPriority.Medium, false); // 同理:不在这里重置 _isMediumBatteryAlarmActive,保持中低电状态标记, // 直到电量恢复 >20% 或切换到交流电供电时再重置。 } private void CheckBatteryAlarms(byte batteryLevel, byte powerType) { // 使用交流电时忽略电池报警 if (powerType == 0) // 0=AC交流电供电 { // 清除所有电池相关报警 if (_activeAlarms.Contains(0x31)) { SendAlarmCommand(0x31, AlarmPriority.High, false); Debug.Log("交流电供电,清除电池电量空报警"); } if (_activeAlarms.Contains(0x21)) { SendAlarmCommand(0x21, AlarmPriority.Medium, false); Debug.Log("交流电供电,清除电池电量低报警"); } if (_activeAlarms.Contains(0x11)) { SendAlarmCommand(0x11, AlarmPriority.Low, false); Debug.Log("交流电供电,清除电池电量低报警"); } // 在交流电供电下,视为电池状态恢复,重置进入低电的状态标记,允许后续再触发。 _isLowBatteryAlarmActive = false; _isMediumBatteryAlarmActive = false; return; // 使用交流电时不检查电池报警 } // 仅在使用电池供电时检查电池报警 (powerType == 1) if (batteryLevel <= 5) { SendAlarmCommand(0x31, AlarmPriority.High, true); } else if (batteryLevel <= 20) { // 仅在从非中低电状态进入中低电状态时触发一次 if (!_isMediumBatteryAlarmActive) { _isMediumBatteryAlarmActive = true; SendAlarmCommand(0x21, AlarmPriority.Medium, true); StartCoroutine(SendMediumBatteryClearAlarm(delayTime)); } } else if (batteryLevel <= 40) { // 仅在从非低电状态进入低电状态时触发一次 if (!_isLowBatteryAlarmActive) { _isLowBatteryAlarmActive = true; SendAlarmCommand(0x11, AlarmPriority.Low, true); StartCoroutine(SendLowBatteryClearAlarm(delayTime)); } } // 清除已恢复的电池报警 if (batteryLevel > 5 && _activeAlarms.Contains(0x31)) { SendAlarmCommand(0x31, AlarmPriority.High, false); } // 当电量恢复到阈值之上,清除一次性触发标记,允许后续再次进入时重新触发 if (batteryLevel > 20 && _isMediumBatteryAlarmActive) { _isMediumBatteryAlarmActive = false; } if (batteryLevel > 40 && _isLowBatteryAlarmActive) { _isLowBatteryAlarmActive = false; } } private void CheckLaserAlarms(byte laserStatus) { if (laserStatus == 2) // 激光器异常 { SendAlarmCommand(0x33, AlarmPriority.High, true); } else if (laserStatus != 2 && _activeAlarms.Contains(0x33)) // 激光器恢复正常 { SendAlarmCommand(0x33, AlarmPriority.High, false); } } private void CheckTemperatureAlarms(short temperature) { // 假设温度单位为0.1°C,正常范围20-40°C float tempCelsius = temperature / 10.0f; bool tempAbnormal = tempCelsius > 40; if (tempAbnormal) { SendAlarmCommand(0x22, AlarmPriority.Medium, true); } else if (!tempAbnormal && _activeAlarms.Contains(0x22)) { SendAlarmCommand(0x22, AlarmPriority.Medium, false); } } private void CheckPowerAlarms(byte powerType) { if (powerType == 1) { // 仅在从交流电切换到电池供电的瞬间触发一次 if (!_isPowerAlarmActive) { _isPowerAlarmActive = true; SendAlarmCommand(0x12, AlarmPriority.Low, true); StartCoroutine(SendPowerClearAlarm(delayTime)); } } else if (powerType == 0) { // 交流电供电,视为恢复,允许下一次切换到电池时再触发 _isPowerAlarmActive = false; // 如果当前仍有活动报警,立即发送清除 if (_activeAlarms.Contains(0x12)) { SendAlarmCommand(0x12, AlarmPriority.Low, false); } } } private IEnumerator SendPowerClearAlarm(float delayInSeconds) { yield return new WaitForSeconds(delayInSeconds); // 等待指定秒数 SendAlarmCommand(0x12, AlarmPriority.Low, false); // 恢复交流电 // 不在这里重置 _isPowerAlarmActive,保持当前供电状态标记; // 仅当检测到实际切换回交流电(powerType==0)时再重置。 } // BFI阈值设置 private float _bfiLowThreshold = 0f; private float _bfiHighThreshold = 200f; private bool _enableBFIAlarm = true; // BFI防抖动相关 private float _bfiLastValue = 0f; private DateTime _bfiAbnormalStartTime = DateTime.MinValue; private const int BFI_DEBOUNCE_TIME_MS = 5000; // 5秒防抖:持续异常5秒才触发报警 /// /// 更新BFI阈值设置 /// public void UpdateBFIThresholds(float lowThreshold, float highThreshold, int priority, bool enabled) { _bfiLowThreshold = lowThreshold; _bfiHighThreshold = highThreshold; _enableBFIAlarm = enabled; } public void CheckBFIAlarm(float bfiValue) { if (!_enableBFIAlarm) return; // 使用可配置的阈值范围 bool bfiAbnormal = bfiValue < _bfiLowThreshold || bfiValue > _bfiHighThreshold; var alarmPriority = AlarmPriority.High; if (bfiAbnormal) { // BFI异常 SendAlarmCommand(0x32, alarmPriority, true); Debug.Log($"[BFI报警] 数值异常: {bfiValue:F2} (阈值范围: {_bfiLowThreshold:F1}-{_bfiHighThreshold:F1})"); } else { // BFI恢复正常 if (_activeAlarms.Contains(0x32)) { SendAlarmCommand(0x32, alarmPriority, false); Debug.Log($"[BFI报警] 数值恢复正常: {bfiValue:F2}"); } } } /// /// 检查BFI数值是否异常(带防抖动) /// 统一处理BFI报警的发送、界面显示和声音 /// 防抖动:只有持续异常5秒才触发报警,避免阈值边界频繁抖动 /// public void CheckBFIAlarmInterval(float bfiValue) { if (!_enableBFIAlarm) return; // 使用可配置的阈值范围 bool bfiAbnormal = bfiValue < _bfiLowThreshold || bfiValue > _bfiHighThreshold; var alarmPriority = AlarmPriority.High; if (bfiAbnormal) { // BFI异常 if (_bfiAbnormalStartTime == DateTime.MinValue) { // 第一次检测到异常,记录开始时间 _bfiAbnormalStartTime = DateTime.Now; #if UNITY_ANDROID && !UNITY_EDITOR AndroidFileLogger.Instance?.LogInfo("BFI", $"防抖:检测到异常,开始计时: BFI={bfiValue:F2} (阈值: {_bfiLowThreshold:F1}-{_bfiHighThreshold:F1})"); #endif Debug.Log($"[BFI防抖] 检测到异常,开始计时: BFI={bfiValue:F2}"); SendAlarmCommand(0x32, alarmPriority, true); return; // 不立即报警,等待持续异常 } // 检查持续异常时间 var abnormalDuration = (DateTime.Now - _bfiAbnormalStartTime).TotalMilliseconds; if (abnormalDuration >= BFI_DEBOUNCE_TIME_MS) { // 持续异常超过5秒,触发报警 #if UNITY_ANDROID && !UNITY_EDITOR AndroidFileLogger.Instance?.LogInfo("BFI", $"防抖:持续异常{abnormalDuration:F0}ms,触发报警: BFI={bfiValue:F2}"); #endif Debug.Log($"[BFI防抖] 持续异常{abnormalDuration:F0}ms,触发报警: BFI={bfiValue:F2}"); SendAlarmCommand(0x32, alarmPriority, true); Debug.Log($"[BFI报警] 数值异常: {bfiValue:F2} (阈值范围: {_bfiLowThreshold:F1}-{_bfiHighThreshold:F1})"); } else if (abnormalDuration < BFI_DEBOUNCE_TIME_MS) { // 仍在防抖时间内,继续等待 var remaining = BFI_DEBOUNCE_TIME_MS - abnormalDuration; Debug.Log($"[BFI防抖] 异常持续中({abnormalDuration:F0}ms),等待{remaining:F0}ms后触发报警"); } } else { // BFI恢复正常 if (_bfiAbnormalStartTime != DateTime.MinValue) { // 异常时间未达到5秒就恢复了 var abnormalDuration = (DateTime.Now - _bfiAbnormalStartTime).TotalMilliseconds; #if UNITY_ANDROID && !UNITY_EDITOR AndroidFileLogger.Instance?.LogInfo("BFI", $"防抖:异常已恢复(持续{abnormalDuration:F0}ms<{BFI_DEBOUNCE_TIME_MS}ms): BFI={bfiValue:F2}"); #endif Debug.Log($"[BFI防抖] 异常已恢复(持续{abnormalDuration:F0}ms): BFI={bfiValue:F2}"); _bfiAbnormalStartTime = DateTime.MinValue; } // 如果当前有激活的报警,取消报警 if (_activeAlarms.Contains(0x32)) { #if UNITY_ANDROID && !UNITY_EDITOR AndroidFileLogger.Instance?.LogInfo("BFI", $"防抖:取消报警: BFI={bfiValue:F2}"); #endif Debug.Log($"[BFI防抖] 取消报警: BFI={bfiValue:F2}"); SendAlarmCommand(0x32, alarmPriority, false); Debug.Log($"[BFI报警] 数值恢复正常: {bfiValue:F2}"); } } _bfiLastValue = bfiValue; } /// /// 更新当前活跃报警的最高优先级 /// private void UpdateHighestPriority() { var previousPriority = _currentHighestPriority; _currentHighestPriority = AlarmPriority.None; foreach (var alarmCode in _activeAlarms) { if (_alarmCodeMap.TryGetValue(alarmCode, out var alarmInfo)) { if (alarmInfo.priority > _currentHighestPriority) { _currentHighestPriority = alarmInfo.priority; } } } Debug.Log($"[DCS报警管理器] 更新最高优先级: {_currentHighestPriority}"); // 如果从有报警变为无报警,触发清除事件 if (previousPriority != AlarmPriority.None && _currentHighestPriority == AlarmPriority.None) { OnAlarmCleared?.Invoke(); Debug.Log("[DCS报警管理器] 所有报警已清除,触发正常状态"); } } /// /// 判断是否应该显示该优先级的报警 /// 规则: /// - 有高优先级报警时,只显示高优先级 /// - 有中优先级报警时,只显示高、中优先级 /// - 只有低优先级报警时,显示所有报警 /// private bool ShouldDisplayAlarm(AlarmPriority priority) { switch (_currentHighestPriority) { case AlarmPriority.High: // 有高优先级报警时,只显示高优先级 return priority == AlarmPriority.High; case AlarmPriority.Medium: // 有中优先级报警时,只显示高、中优先级 return priority >= AlarmPriority.Medium; case AlarmPriority.Low: case AlarmPriority.None: default: // 只有低优先级或无报警时,显示所有报警 return true; } } /// /// 获取当前活跃的报警列表 /// public List<(byte code, string message, AlarmPriority priority)> GetActiveAlarms() { var result = new List<(byte, string, AlarmPriority)>(); foreach (var alarmCode in _activeAlarms) { if (_alarmCodeMap.TryGetValue(alarmCode, out var alarmInfo)) { result.Add((alarmCode, alarmInfo.message, alarmInfo.priority)); } } return result; } /// /// 获取当前最高优先级 /// public AlarmPriority GetCurrentHighestPriority() { return _currentHighestPriority; } #region 报警记录管理 /// /// 添加报警记录 /// private void AddAlarmRecord(AlarmEvent alarmEvent) { _alarmRecords.Add(alarmEvent); // 保持记录数量在限制范围内 if (_alarmRecords.Count > MAX_ALARM_RECORDS) { _alarmRecords.RemoveAt(0); // 移除最旧的记录 } Debug.Log($"[DCS报警记录] 添加记录: ID={alarmEvent.Id}, {alarmEvent.Priority}, {alarmEvent.Reason}"); } /// /// 获取报警记录列表 /// public IReadOnlyList GetAlarmRecords(int maxCount = 1000) { // 优先从持久化服务读取 if (_recordPersistence != null) { return _recordPersistence.GetRecords(maxCount).AsReadOnly(); } // 降级到内存存储 var count = Math.Min(maxCount, _alarmRecords.Count); return _alarmRecords.Skip(_alarmRecords.Count - count).ToList().AsReadOnly(); } /// /// 清除所有报警记录 /// public void ClearAlarmRecords() { // 清除持久化记录 if (_recordPersistence != null) { _recordPersistence.ClearRecords(); } // 清除内存记录 int clearedCount = _alarmRecords.Count; _alarmRecords.Clear(); _nextAlarmId = 1; // 重置ID计数器 Debug.Log($"[DCS报警记录] 已清除 {clearedCount} 条记录(含持久化)"); } /// /// 获取报警记录总数 /// public int GetAlarmRecordCount() { if (_recordPersistence != null) { return _recordPersistence.GetRecordCount(); } return _alarmRecords.Count; } /// /// 从持久化存储加载历史记录到内存 /// private void LoadPersistedRecords() { if (_recordPersistence == null) return; var persistedRecords = _recordPersistence.GetAllRecords(); if (persistedRecords != null && persistedRecords.Count > 0) { _alarmRecords.Clear(); _alarmRecords.AddRange(persistedRecords); // 更新ID计数器 if (persistedRecords.Count > 0) { _nextAlarmId = persistedRecords.Max(r => r.Id) + 1; } Debug.Log($"[DCS报警管理器] 从持久化存储加载了 {persistedRecords.Count} 条历史记录"); } } #endregion }