13 KiB
13 KiB
报警记录开始/结束时间追踪说明
功能概述
报警记录系统现在支持完整的报警生命周期追踪,记录每个报警的"触发"和"解除"两个关键时间点。
核心改进
1. 数据模型增强
AlarmRecordData 新增字段:
public bool IsTriggered; // true=报警触发, false=报警解除
完整数据结构:
[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. 报警记录逻辑
报警触发时
// BFI异常触发
SendAlarmCommand(0x32, AlarmPriority.High, true);
// 记录内容:
{
Id: 1,
Priority: High,
Reason: "BFI数值异常",
Time: "2025-11-28 14:30:15",
AlarmCode: "0x32",
IsTriggered: true ✅ 标记为触发
}
报警解除时
// 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秒内重复才去重
- 触发和解除被视为不同事件,都会记录
去重判断条件:
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 方法:
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 方法签名:
// 旧签名(不支持状态)
public void AddRecord(AlarmEvent alarmEvent, byte alarmCode)
// 新签名(支持状态)✅
public void AddRecord(AlarmEvent alarmEvent, byte alarmCode, bool isTriggered)
去重逻辑:
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 文件内容:
{
"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. 报警持续时间统计
// 计算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. 报警频率分析
// 统计每天的报警次数
var dailyCount = records
.Where(r => r.IsTriggered)
.GroupBy(r => DateTime.Parse(r.Time).Date)
.Select(g => new { Date = g.Key, Count = g.Count() });
总结
通过引入 IsTriggered 状态标记,报警记录系统现在能够:
- 完整追踪:记录每个报警的触发和解除时间
- 智能去重:区分触发和解除,避免误判
- 数据丰富:支持报警持续时间统计和频率分析
- 用户友好:"(已解除)" 标注清晰直观
这是一个重要的改进,使报警记录系统更加专业和实用!✅