QualityJournalTests.cs 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. using Serilog.Events;
  2. using Xunit;
  3. using YZWater.Core.Services;
  4. namespace YZWater.Core.Tests.Services;
  5. /// <summary>
  6. /// 测点数据质量事件的记录方式回归。
  7. /// 背景:现场报警历史被"测点 X 读取失败 / 已恢复"刷满 —— 一次抖动就是"点位数 × 2"行,
  8. /// 真报警被埋掉,而且这些行是系统自动确认的流水,操作员既确认不了也复位不了,
  9. /// 误点复位还会让程序往 PLC 的复位地址写一次脉冲。
  10. /// 现在一轮最多两条汇总(明细仍在日志文件里),且带独立类型 Quality。
  11. /// </summary>
  12. [Collection("Serilog minimum level")]
  13. public class QualityJournalTests : IDisposable
  14. {
  15. private readonly LogEventLevel _originalLevel = LogService.MinimumLevel;
  16. private readonly AlarmService _service;
  17. public QualityJournalTests()
  18. {
  19. LogService.ApplyMinimumLevel("Error");
  20. _service = new AlarmService(PlcPollingService.Instance);
  21. }
  22. public void Dispose()
  23. {
  24. _service.Dispose();
  25. LogService.ApplyMinimumLevel(_originalLevel.ToString());
  26. }
  27. [Fact]
  28. public void TwentyBadTags_SummarizeIntoOneMessage_NotTwenty()
  29. {
  30. var lost = Enumerable.Range(1, 20).Select(i => $"{i}#泵.Run").ToList();
  31. var (lostMessage, restoredMessage) =
  32. PlcPollingService.BuildQualityJournalMessages(lost, new List<string>());
  33. Assert.Null(restoredMessage);
  34. Assert.NotNull(lostMessage);
  35. var message = lostMessage!;
  36. // 一条消息讲清规模,而不是一个点一条流水
  37. Assert.Contains("20", message);
  38. Assert.Contains("共 20 个", message);
  39. // 清单截断到前 8 个,避免整张点位表被塞进一条记录
  40. Assert.DoesNotContain("20#泵.Run", message);
  41. Assert.Contains("8#泵.Run", message);
  42. // 键名拼错时 LanguageService 会原样返回键,这条断言保证模板真的解析了
  43. Assert.DoesNotContain("TagQualityLostSummary", message);
  44. }
  45. [Fact]
  46. public void LostAndRestored_ProduceTwoSeparateMessages()
  47. {
  48. var (lost, restored) = PlcPollingService.BuildQualityJournalMessages(
  49. new List<string> { "DI.高压报警" }, new List<string> { "1#泵.Fault", "2#泵.Fault" });
  50. Assert.NotNull(lost);
  51. Assert.Contains("DI.高压报警", lost);
  52. Assert.NotNull(restored);
  53. Assert.Contains("1#泵.Fault", restored);
  54. Assert.DoesNotContain("DI.高压报警", restored);
  55. }
  56. [Fact]
  57. public void NoTransitions_ProduceNoRows()
  58. {
  59. var (lost, restored) = PlcPollingService.BuildQualityJournalMessages(
  60. new List<string>(), new List<string>());
  61. Assert.Null(lost);
  62. Assert.Null(restored);
  63. }
  64. /// <summary>
  65. /// 诊断流水不得进入活跃报警集合 —— 否则它会占用报警位、被升级/泛溢逻辑统计,
  66. /// 还会让"复位"误以为有东西可复位而去写 PLC。
  67. /// </summary>
  68. [Fact]
  69. public void JournalAlarm_NeverBecomesAnActiveAlarm()
  70. {
  71. _service.RecordJournalAlarm("1 个测点读取失败:1#泵.Run", level: 1, alarmType: "Quality");
  72. Assert.Equal(0, _service.ActiveAlarmCount);
  73. Assert.False(_service.HasActiveAlarm);
  74. }
  75. }