综合平台编程器项目的远程存储
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

141 lignes
4.9 KiB

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