| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- using Serilog.Events;
- using SqlSugar;
- using Xunit;
- using YZWater.Core.Models;
- using YZWater.Core.Services;
- namespace YZWater.Core.Tests.Services;
- /// <summary>
- /// 上次运行遗留的"未恢复"报警收尾。
- /// conditionId→DbId 的映射只在内存里,进程被杀/正常重启都来不及回写 RecoverTime:
- /// 现场库里那条 07:36:19 的 PLC_Connection 记录一直是 NULL,历史页把它显示成"进行中/无限时长",
- /// 操作员以为设备还在报警。诊断流水(ConditionId 为空)是瞬时事件,本就没有恢复一说,不该被盖上时间。
- /// </summary>
- [Collection("Serilog minimum level")]
- public class AlarmOrphanRepairTests : IDisposable
- {
- private readonly LogEventLevel _originalLevel = LogService.MinimumLevel;
- private readonly AlarmService _service;
- public AlarmOrphanRepairTests()
- {
- LogService.ApplyMinimumLevel("Error");
- DatabaseService.Initialize();
- _service = new AlarmService(PlcPollingService.Instance);
- }
- public void Dispose()
- {
- _service.Dispose();
- LogService.ApplyMinimumLevel(_originalLevel.ToString());
- }
- private int Insert(AlarmRecord record) =>
- DatabaseService.Db.Insertable(record).ExecuteReturnIdentity();
- private DateTime? ReadRecoverTime(int id) =>
- DatabaseService.Db.Queryable<AlarmRecord>().Where(r => r.Id == id).Select(r => r.RecoverTime).First();
- [Fact]
- public async Task Startup_ClosesOrphanedRealAlarm()
- {
- var marker = Guid.NewGuid().ToString("N");
- var orphanId = Insert(new AlarmRecord
- {
- AlarmType = "PLC",
- AlarmMessage = $"遗留活跃报警 {marker}",
- AlarmTime = DateTime.Now.AddHours(-2),
- ConditionId = $"PLC_{marker}"
- });
- Assert.Null(ReadRecoverTime(orphanId));
- await _service.CloseOrphanedAlarmsAsync();
- var closed = ReadRecoverTime(orphanId);
- Assert.NotNull(closed);
- // 收尾时间取本次启动时刻,不是报警时间:中间这段时间设备确实处于无人监视状态
- Assert.True(closed!.Value > DateTime.Now.AddMinutes(-5));
- }
- [Fact]
- public async Task Startup_LeavesJournalEventsAndRecoveredRowsAlone()
- {
- var marker = Guid.NewGuid().ToString("N");
- // 诊断流水:没有 conditionId,也没有恢复概念
- var journalId = Insert(new AlarmRecord
- {
- AlarmType = "Quality",
- AlarmMessage = $"测点质量流水 {marker}",
- AlarmTime = DateTime.Now.AddHours(-2),
- IsConfirmed = true,
- ConfirmedBy = "系统"
- });
- // 正常已恢复的报警,恢复时间不能被覆盖成启动时刻
- var recoveredAt = DateTime.Now.AddHours(-3);
- var recoveredId = Insert(new AlarmRecord
- {
- AlarmType = "DI",
- AlarmMessage = $"已恢复报警 {marker}",
- AlarmTime = recoveredAt.AddHours(-1),
- ConditionId = $"DI_{marker}",
- RecoverTime = recoveredAt
- });
- await _service.CloseOrphanedAlarmsAsync();
- Assert.Null(ReadRecoverTime(journalId));
- // SQLite 只存到毫秒,比较放宽到 1 秒,避免亚毫秒精度造成假红
- var kept = ReadRecoverTime(recoveredId);
- Assert.NotNull(kept);
- Assert.True(Math.Abs((kept!.Value - recoveredAt).TotalSeconds) < 1);
- }
- }
|