综合平台编程器项目的远程存储
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

139 lines
5.0 KiB

  1. #include "domain/project_storage.h"
  2. #include "domain/register_repository.h"
  3. #include "domain/virtual_register_repository.h"
  4. #include "services/alarm_editor_service.h"
  5. #include "services/alarm_service.h"
  6. #include "services/project_service.h"
  7. #include "support/test_support.h"
  8. #include <algorithm>
  9. #include <exception>
  10. #include <iostream>
  11. #include <stdexcept>
  12. #include <string>
  13. namespace {
  14. using TestProjectStorage = TestSupport::InMemoryProjectStorage;
  15. using TestSupport::require;
  16. const AlarmRecord *findRecord(
  17. const AlarmService &service, const std::string &definition_id)
  18. {
  19. const auto &records = service.records();
  20. const auto record = std::find_if(
  21. records.cbegin(), records.cend(),
  22. [&definition_id](const AlarmRecord &candidate)
  23. {
  24. return candidate.definitionId == definition_id;
  25. });
  26. return record == records.cend() ? nullptr : &*record;
  27. }
  28. void testAlarmDefinitionsAndRuntimeLifecycle()
  29. {
  30. TestProjectStorage storage;
  31. ProjectService project_service(storage);
  32. AlarmEditorService editor_service(project_service);
  33. VirtualRegisterRepository repository;
  34. AlarmService alarm_service(project_service, repository);
  35. AlarmDefinition m_alarm;
  36. m_alarm.address = RegisterAddress{RegisterArea::M, 0};
  37. m_alarm.condition = AlarmCondition::MOn;
  38. m_alarm.message = "Emergency stop";
  39. const AlarmEditorResult m_result = editor_service.addDefinition(m_alarm);
  40. AlarmDefinition high_alarm;
  41. high_alarm.address = RegisterAddress{RegisterArea::D, 0};
  42. high_alarm.condition = AlarmCondition::DHigh;
  43. high_alarm.threshold = 80;
  44. high_alarm.message = "Temperature high";
  45. const AlarmEditorResult high_result = editor_service.addDefinition(high_alarm);
  46. AlarmDefinition low_alarm;
  47. low_alarm.address = RegisterAddress{RegisterArea::D, 1};
  48. low_alarm.condition = AlarmCondition::DLow;
  49. low_alarm.threshold = 10;
  50. low_alarm.message = "Pressure low";
  51. const AlarmEditorResult low_result = editor_service.addDefinition(low_alarm);
  52. require(m_result.succeeded && high_result.succeeded && low_result.succeeded,
  53. "M, D high and D low alarm definitions must be accepted");
  54. AlarmDefinition invalid = high_alarm;
  55. invalid.address = RegisterAddress{RegisterArea::M, 2};
  56. require(editor_service.addDefinition(invalid).error
  57. == AlarmEditorError::InvalidDefinition,
  58. "D alarm conditions must reject M addresses");
  59. repository.writeWord(RegisterAddress{RegisterArea::D, 1}, 20);
  60. alarm_service.refresh();
  61. require(alarm_service.records().empty(),
  62. "inactive alarm conditions must not create records");
  63. repository.writeBit(RegisterAddress{RegisterArea::M, 0}, true);
  64. repository.writeWord(RegisterAddress{RegisterArea::D, 0}, 80);
  65. repository.writeWord(RegisterAddress{RegisterArea::D, 1}, 10);
  66. alarm_service.refresh();
  67. require(findRecord(alarm_service, m_result.id) != nullptr
  68. && findRecord(alarm_service, high_result.id) != nullptr
  69. && findRecord(alarm_service, low_result.id) != nullptr,
  70. "all three alarm conditions must create active records");
  71. require(alarm_service.acknowledge(m_result.id)
  72. && findRecord(alarm_service, m_result.id)->acknowledged,
  73. "an active alarm must be locally acknowledged");
  74. repository.writeBit(RegisterAddress{RegisterArea::M, 0}, false);
  75. repository.writeWord(RegisterAddress{RegisterArea::D, 0}, 79);
  76. repository.writeWord(RegisterAddress{RegisterArea::D, 1}, 11);
  77. alarm_service.refresh();
  78. require(alarm_service.records().empty(),
  79. "cleared conditions must immediately remove active alarms");
  80. repository.writeBit(RegisterAddress{RegisterArea::M, 0}, true);
  81. alarm_service.refresh();
  82. require(findRecord(alarm_service, m_result.id) != nullptr
  83. && alarm_service.records().size() == 1
  84. && !alarm_service.records().front().acknowledged,
  85. "a repeated alarm must create a new unacknowledged occurrence");
  86. alarm_service.reset();
  87. require(alarm_service.records().empty(),
  88. "leaving runtime must clear session alarm records");
  89. }
  90. void testConfiguredAlarmLimit()
  91. {
  92. TestProjectStorage storage;
  93. ProjectLimitSettings limits;
  94. limits.maximumAlarmDefinitions = 0U;
  95. ProjectService project_service(storage, limits);
  96. AlarmEditorService editor_service(project_service);
  97. AlarmDefinition definition;
  98. definition.address = RegisterAddress{RegisterArea::M, 0};
  99. definition.condition = AlarmCondition::MOn;
  100. definition.message = "Disabled by configuration";
  101. require(!editor_service.addDefinition(definition).succeeded,
  102. "the alarm editor must use the configured definition limit");
  103. }
  104. } // namespace
  105. int main()
  106. {
  107. try
  108. {
  109. testAlarmDefinitionsAndRuntimeLifecycle();
  110. testConfiguredAlarmLimit();
  111. }
  112. catch (const std::exception &error)
  113. {
  114. std::cerr << "alarm service tests failed: " << error.what() << '\n';
  115. return 1;
  116. }
  117. std::cout << "alarm service tests passed\n";
  118. return 0;
  119. }