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

765 lines
26 KiB
C#
Raw Permalink Normal View History

2026-06-09 13:59:11 +08:00
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
}