DCS/ruiyiweiUX/Assets/报警记录开始结束时间追踪说明.md

379 lines
13 KiB
Markdown
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.

# 报警记录开始/结束时间追踪说明
## 功能概述
报警记录系统现在支持完整的报警生命周期追踪,记录每个报警的"触发"和"解除"两个关键时间点。
## 核心改进
### 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; // 重复记录
}
}
```
## 实际使用场景
### 场景 1BFI异常报警完整周期
```
时间线:
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. **用户友好**"(已解除)" 标注清晰直观
这是一个重要的改进,使报警记录系统更加专业和实用!✅