DCS/ruiyiweiUX/Assets/Scripts/Managers/DCSAlarmManager.cs

765 lines
26 KiB
C#
Raw Permalink 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 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;
}
/// <summary>
/// DCS协议报警管理器 - 统一管理所有报警功能,包括串口通信、界面显示和声音播放
/// </summary>
public class DCSAlarmManager : MonoBehaviour
{
public static DCSAlarmManager Instance { get; private set; }
// 报警事件供UI组件订阅
public static event Action<AlarmPriority, string> OnAlarmRaised;
public static event Action OnAlarmCleared; // 所有报警消除时触发
public static event Action<bool> OnMuteStateChanged;
private ISerialCommunicationService _serialService;
private AlarmRecordPersistenceService _recordPersistence;
// 静音功能
// private bool _isMuted = false;
// private DateTime _muteEndTime = DateTime.MinValue;
// 报警记录管理
private readonly List<AlarmEvent> _alarmRecords = new List<AlarmEvent>();
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<byte, (AlarmPriority priority, string message)> _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<byte> _activeAlarms = new();
// 当前活跃报警的最高优先级
private AlarmPriority _currentHighestPriority = AlarmPriority.None;
// 报警发送频率限制
private readonly Dictionary<byte, DateTime> _lastAlarmSendTime = new();
private readonly Dictionary<byte, bool> _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<ISerialCommunicationService>();
_recordPersistence = new AlarmRecordPersistenceService();
InitializeCurrentSettings();
// 从持久化存储加载历史记录
LoadPersistedRecords();
}
private void InitializeCurrentSettings()
{
// 从配置服务加载当前阈值设置
var settingsService = ServiceLocator.Get<ISystemSettingsService>();
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
);
}
/// <summary>
/// 发送报警控制命令到下位机
/// 统一协调串口命令发送、界面显示和声音播放
/// </summary>
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}");
}
}
/// <summary>
/// 统一触发系统报警(界面显示 + 声音播放)
/// 根据优先级过滤:高优先级报警时不显示中低优先级,中优先级报警时不显示低优先级
/// </summary>
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}");
}
}
/// <summary>
/// 根据设备状态自动检查并发送报警命令
/// 统一协调报警命令发送、界面显示和声音播放
/// </summary>
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秒才触发报警
/// <summary>
/// 更新BFI阈值设置
/// </summary>
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}");
}
}
}
/// <summary>
/// 检查BFI数值是否异常(带防抖动)
/// 统一处理BFI报警的发送、界面显示和声音
/// 防抖动:只有持续异常5秒才触发报警,避免阈值边界频繁抖动
/// </summary>
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;
}
/// <summary>
/// 更新当前活跃报警的最高优先级
/// </summary>
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报警管理器] 所有报警已清除,触发正常状态");
}
}
/// <summary>
/// 判断是否应该显示该优先级的报警
/// 规则:
/// - 有高优先级报警时,只显示高优先级
/// - 有中优先级报警时,只显示高、中优先级
/// - 只有低优先级报警时,显示所有报警
/// </summary>
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;
}
}
/// <summary>
/// 获取当前活跃的报警列表
/// </summary>
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;
}
/// <summary>
/// 获取当前最高优先级
/// </summary>
public AlarmPriority GetCurrentHighestPriority()
{
return _currentHighestPriority;
}
#region
/// <summary>
/// 添加报警记录
/// </summary>
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}");
}
/// <summary>
/// 获取报警记录列表
/// </summary>
public IReadOnlyList<AlarmEvent> 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();
}
/// <summary>
/// 清除所有报警记录
/// </summary>
public void ClearAlarmRecords()
{
// 清除持久化记录
if (_recordPersistence != null)
{
_recordPersistence.ClearRecords();
}
// 清除内存记录
int clearedCount = _alarmRecords.Count;
_alarmRecords.Clear();
_nextAlarmId = 1; // 重置ID计数器
Debug.Log($"[DCS报警记录] 已清除 {clearedCount} 条记录(含持久化)");
}
/// <summary>
/// 获取报警记录总数
/// </summary>
public int GetAlarmRecordCount()
{
if (_recordPersistence != null)
{
return _recordPersistence.GetRecordCount();
}
return _alarmRecords.Count;
}
/// <summary>
/// 从持久化存储加载历史记录到内存
/// </summary>
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
}