# 报警记录开始/结束时间追踪说明 ## 功能概述 报警记录系统现在支持完整的报警生命周期追踪,记录每个报警的"触发"和"解除"两个关键时间点。 ## 核心改进 ### 1. 数据模型增强 **AlarmRecordData 新增字段**: ```csharp public bool IsTriggered; // true=报警触发, false=报警解除 ``` **完整数据结构**: ```csharp [Serializable] public class AlarmRecordData { public int Id; // 记录ID public int Priority; // 报警优先级(High/Medium/Low) public string Reason; // 报警原因 public string Time; // 记录时间(yyyy-MM-dd HH:mm:ss) public string AlarmCode; // 报警代码(0x11, 0x32等) public bool IsTriggered; // 报警状态(触发/解除) } ``` ### 2. 报警记录逻辑 #### 报警触发时 ```csharp // BFI异常触发 SendAlarmCommand(0x32, AlarmPriority.High, true); // 记录内容: { Id: 1, Priority: High, Reason: "BFI数值异常", Time: "2025-11-28 14:30:15", AlarmCode: "0x32", IsTriggered: true ✅ 标记为触发 } ``` #### 报警解除时 ```csharp // BFI恢复正常 SendAlarmCommand(0x32, AlarmPriority.High, false); // 记录内容: { Id: 2, Priority: High, Reason: "BFI数值异常 (已解除)", Time: "2025-11-28 14:35:20", AlarmCode: "0x32", IsTriggered: false ✅ 标记为解除 } ``` ### 3. 去重机制优化 **旧逻辑**(问题): - 同一报警在60秒内重复触发会被去重 - BFI异常解除后再次触发会被误认为重复 **新逻辑**(改进): - 同一报警代码 + 同一状态在60秒内重复才去重 - 触发和解除被视为不同事件,都会记录 **去重判断条件**: ```csharp private bool IsDuplicateRecord(byte alarmCode, string reason, DateTime time, bool isTriggered) { // 必须满足以下所有条件才认为是重复: // 1. 报警代码相同(AlarmCode) // 2. 报警原因相同(Reason) // 3. 报警状态相同(IsTriggered) ✅ 新增 // 4. 时间间隔小于60秒 if (record.AlarmCode == codeStr && record.Reason == reason && record.IsTriggered == isTriggered && // ✅ 关键判断 timeDiff < 60) { return true; // 重复记录 } } ``` ## 实际使用场景 ### 场景 1:BFI异常报警完整周期 ``` 时间线: 14:30:15 - BFI值下降到阈值以下 → 触发报警 14:35:20 - BFI值恢复正常范围 → 解除报警 14:40:30 - BFI值再次异常 → 再次触发报警(不被去重)✅ 14:42:10 - BFI值恢复 → 再次解除 报警记录: ┌────┬──────┬─────────────────┬─────────────────────┬──────┬────────┐ │ ID │ 优先级│ 原因 │ 时间 │ 代码 │ 状态 │ ├────┼──────┼─────────────────┼─────────────────────┼──────┼────────┤ │ 1 │ High │ BFI数值异常 │ 2025-11-28 14:30:15 │ 0x32 │ 触发 │ │ 2 │ High │ BFI数值异常(已解除)│ 2025-11-28 14:35:20 │ 0x32 │ 解除 │ │ 3 │ High │ BFI数值异常 │ 2025-11-28 14:40:30 │ 0x32 │ 触发 │ │ 4 │ High │ BFI数值异常(已解除)│ 2025-11-28 14:42:10 │ 0x32 │ 解除 │ └────┴──────┴─────────────────┴─────────────────────┴──────┴────────┘ ``` ### 场景 2:电池电量报警 ``` 时间线: 10:00:00 - 电量降至35% → 触发低优先级报警(0x11) 10:05:00 - 连接充电器,电量开始上升 → 解除报警 10:10:00 - 断开充电器,电量降至38% → 再次触发报警 10:15:00 - 重新连接充电器 → 再次解除 报警记录: ┌────┬──────┬──────────────────┬─────────────────────┬──────┬────────┐ │ ID │ 优先级│ 原因 │ 时间 │ 代码 │ 状态 │ ├────┼──────┼──────────────────┼─────────────────────┼──────┼────────┤ │ 5 │ Low │ 电池电量低(<40%) │ 2025-11-28 10:00:00 │ 0x11 │ 触发 │ │ 6 │ Low │ 电池电量低(已解除)│ 2025-11-28 10:05:00 │ 0x11 │ 解除 │ │ 7 │ Low │ 电池电量低(<40%) │ 2025-11-28 10:10:00 │ 0x11 │ 触发 │ │ 8 │ Low │ 电池电量低(已解除)│ 2025-11-28 10:15:00 │ 0x11 │ 解除 │ └────┴──────┴──────────────────┴─────────────────────┴──────┴────────┘ ``` ### 场景 3:去重保护(60秒窗口) ``` 时间线: 11:00:00 - BFI异常触发报警 11:00:15 - BFI仍然异常(15秒内重复触发)→ 被去重,不记录 ✅ 11:00:45 - BFI仍然异常(45秒内重复触发)→ 被去重,不记录 ✅ 11:01:30 - BFI恢复正常 → 解除报警(不同状态,会记录)✅ 报警记录: ┌────┬──────┬─────────────────┬─────────────────────┬──────┬────────┐ │ ID │ 优先级│ 原因 │ 时间 │ 代码 │ 状态 │ ├────┼──────┼─────────────────┼─────────────────────┼──────┼────────┤ │ 9 │ High │ BFI数值异常 │ 2025-11-28 11:00:00 │ 0x32 │ 触发 │ │ 10 │ High │ BFI数值异常(已解除)│ 2025-11-28 11:01:30 │ 0x32 │ 解除 │ └────┴──────┴─────────────────┴─────────────────────┴──────┴────────┘ 日志输出: [11:00:15] [报警记录持久化] 跳过重复记录: BFI数值异常 (代码: 0x32, 状态: 触发) [11:00:45] [报警记录持久化] 跳过重复记录: BFI数值异常 (代码: 0x32, 状态: 触发) ``` ## 代码实现细节 ### DCSAlarmManager.cs 改动 **SendAlarmCommand 方法**: ```csharp public void SendAlarmCommand(byte alarmCode, AlarmPriority priority, bool trigger) { // ... 发送串口命令 ... // 更新活跃报警集合 if (trigger) { _activeAlarms.Add(alarmCode); } else { _activeAlarms.Remove(alarmCode); } // 更新当前最高优先级 UpdateHighestPriority(); // ✅ 记录报警状态变化(触发和解除都记录) 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); // ✅ 传递状态 } } // 触发系统报警显示和声音(仅在触发报警时) if (trigger && _alarmCodeMap.TryGetValue(alarmCode, out var alarmInfo)) { TriggerSystemAlarm(alarmCode, alarmInfo.priority, alarmInfo.message); } } ``` ### AlarmRecordPersistenceService.cs 改动 **AddRecord 方法签名**: ```csharp // 旧签名(不支持状态) public void AddRecord(AlarmEvent alarmEvent, byte alarmCode) // 新签名(支持状态)✅ public void AddRecord(AlarmEvent alarmEvent, byte alarmCode, bool isTriggered) ``` **去重逻辑**: ```csharp private bool IsDuplicateRecord(byte alarmCode, string reason, DateTime time, bool isTriggered) { // 从最新记录开始检查 for (int i = _storage.Records.Count - 1; i >= 0; i--) { var record = _storage.Records[i]; // ✅ 检查报警代码、原因和状态是否相同 if (record.AlarmCode == codeStr && record.Reason == reason && record.IsTriggered == isTriggered) // 新增状态判断 { // 检查时间差 var timeDiff = (time - recordTime).TotalSeconds; // 如果在去重窗口内,认为是重复记录 if (timeDiff < 60) { return true; // 重复 } } } return false; // 不重复 } ``` ## 数据存储示例 **alarm_records.json 文件内容**: ```json { "Records": [ { "Id": 1, "Priority": 2, "Reason": "BFI数值异常", "Time": "2025-11-28 14:30:15", "AlarmCode": "0x32", "IsTriggered": true }, { "Id": 2, "Priority": 2, "Reason": "BFI数值异常 (已解除)", "Time": "2025-11-28 14:35:20", "AlarmCode": "0x32", "IsTriggered": false }, { "Id": 3, "Priority": 0, "Reason": "电池电量低(<40%)", "Time": "2025-11-28 15:00:00", "AlarmCode": "0x11", "IsTriggered": true } ], "NextId": 4 } ``` ## UI 显示效果 在报警记录面板(AlarmRecordPanel)中,用户可以看到: ``` 报警记录列表 ┌────────────────────────────────────────────────────┐ │ # │ 优先级 │ 原因 │ 时间 │ ├────┼───────┼────────────────────────┼────────────┤ │ 1 │ High │ BFI数值异常 │ 14:30:15 │ │ 2 │ High │ BFI数值异常 (已解除) │ 14:35:20 │ ✅ 明确标注 │ 3 │ High │ BFI数值异常 │ 14:40:30 │ │ 4 │ Low │ 电池电量低(<40%) │ 15:00:00 │ │ 5 │ Low │ 电池电量低 (已解除) │ 15:10:00 │ ✅ 明确标注 └────┴───────┴────────────────────────┴────────────┘ ``` ## 优点总结 ### 1. 完整性 - ✅ 记录报警的完整生命周期 - ✅ 可以计算报警持续时间 - ✅ 便于故障分析和统计 ### 2. 准确性 - ✅ 触发和解除分别记录,不会混淆 - ✅ 去重机制更智能,不会误判 - ✅ 同一报警可以多次记录 ### 3. 可追溯性 - ✅ 每次报警都有开始和结束时间 - ✅ 可以分析报警频率和持续时间 - ✅ 便于生成报警统计报告 ### 4. 用户友好 - ✅ "(已解除)" 标注清晰明了 - ✅ 列表按时间顺序排列 - ✅ 支持筛选触发/解除事件 ## 测试建议 ### 1. 基本功能测试 - [ ] 触发BFI报警 → 验证记录"BFI数值异常" - [ ] BFI恢复 → 验证记录"BFI数值异常 (已解除)" - [ ] 再次触发 → 验证新记录,未被去重 ### 2. 去重测试 - [ ] 15秒内重复触发 → 验证第二次被去重 - [ ] 触发后立即解除 → 验证解除事件不被去重 - [ ] 解除后15秒内再次解除 → 验证第二次被去重 ### 3. 多报警测试 - [ ] 同时触发多个报警 → 验证各自独立记录 - [ ] 不同报警代码 → 验证不会互相干扰 ### 4. 持久化测试 - [ ] 应用重启 → 验证历史记录仍然存在 - [ ] 超过1000条记录 → 验证自动删除最旧记录 ## 数据分析示例 基于完整的触发/解除记录,可以进行以下分析: ### 1. 报警持续时间统计 ```csharp // 计算BFI报警平均持续时间 var bfiAlarms = records.Where(r => r.AlarmCode == "0x32"); var triggered = bfiAlarms.Where(r => r.IsTriggered).OrderBy(r => r.Time); var cleared = bfiAlarms.Where(r => !r.IsTriggered).OrderBy(r => r.Time); // 配对触发和解除记录 for (int i = 0; i < Math.Min(triggered.Count(), cleared.Count()); i++) { var duration = (cleared.ElementAt(i).Time - triggered.ElementAt(i).Time).TotalMinutes; Console.WriteLine($"报警持续: {duration:F1} 分钟"); } ``` ### 2. 报警频率分析 ```csharp // 统计每天的报警次数 var dailyCount = records .Where(r => r.IsTriggered) .GroupBy(r => DateTime.Parse(r.Time).Date) .Select(g => new { Date = g.Key, Count = g.Count() }); ``` ## 总结 通过引入 `IsTriggered` 状态标记,报警记录系统现在能够: 1. **完整追踪**:记录每个报警的触发和解除时间 2. **智能去重**:区分触发和解除,避免误判 3. **数据丰富**:支持报警持续时间统计和频率分析 4. **用户友好**:"(已解除)" 标注清晰直观 这是一个重要的改进,使报警记录系统更加专业和实用!✅