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

379 lines
13 KiB
Markdown
Raw Normal View History

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