综合平台编程器项目的远程存储
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 

211 satır
7.2 KiB

  1. #include "domain/active_register_repository.h"
  2. #include "domain/project_storage.h"
  3. #include "infrastructure/plc_register_repository.h"
  4. #include "services/offline_simulation_service.h"
  5. #include "services/plc_communication_gateway.h"
  6. #include "services/project_service.h"
  7. #include "services/runtime_mode_service.h"
  8. #include <functional>
  9. #include <iostream>
  10. #include <stdexcept>
  11. #include <utility>
  12. namespace {
  13. class TestProjectStorage final : public ProjectStorage
  14. {
  15. public:
  16. ProjectSaveResult save(const Project &, const std::string &) override
  17. {
  18. return {true, ProjectStorageError::None, {}};
  19. }
  20. ProjectLoadResult load(const std::string &) override
  21. {
  22. return {false, {}, ProjectStorageError::FileReadFailed, {}};
  23. }
  24. };
  25. class FakePlcGateway final : public PlcCommunicationGateway
  26. {
  27. public:
  28. PlcCommunicationResult connectDevice(
  29. const PlcSerialConfiguration &configuration) override
  30. {
  31. last_configuration = configuration;
  32. connection_state = PlcConnectionState::Connected;
  33. if (state_changed)
  34. {
  35. state_changed();
  36. }
  37. return {true, {}};
  38. }
  39. void disconnectDevice() override
  40. {
  41. connection_state = PlcConnectionState::Disconnected;
  42. initial_read = false;
  43. if (initial_read_changed)
  44. {
  45. initial_read_changed(false);
  46. }
  47. if (state_changed)
  48. {
  49. state_changed();
  50. }
  51. }
  52. void setPollAddresses(const std::vector<RegisterAddress> &addresses) override
  53. {
  54. poll_addresses = addresses;
  55. }
  56. PlcConnectionState state() const override { return connection_state; }
  57. bool initialReadCompleted() const override { return initial_read; }
  58. const std::string &lastError() const override { return last_error; }
  59. void setCallbacks(
  60. std::function<void()> state_callback,
  61. std::function<void(bool)> initial_callback,
  62. std::function<void()> cache_callback,
  63. std::function<void(const std::string &)> error_callback) override
  64. {
  65. state_changed = std::move(state_callback);
  66. initial_read_changed = std::move(initial_callback);
  67. cache_updated = std::move(cache_callback);
  68. error_reported = std::move(error_callback);
  69. }
  70. void completeInitialRead()
  71. {
  72. initial_read = true;
  73. if (initial_read_changed)
  74. {
  75. initial_read_changed(true);
  76. }
  77. }
  78. PlcConnectionState connection_state = PlcConnectionState::Disconnected;
  79. bool initial_read = false;
  80. std::string last_error;
  81. PlcSerialConfiguration last_configuration;
  82. std::vector<RegisterAddress> poll_addresses;
  83. std::function<void()> state_changed;
  84. std::function<void(bool)> initial_read_changed;
  85. std::function<void()> cache_updated;
  86. std::function<void(const std::string &)> error_reported;
  87. };
  88. void require(bool condition, const std::string &message)
  89. {
  90. if (!condition)
  91. {
  92. throw std::runtime_error(message);
  93. }
  94. }
  95. void testPlcCacheAndWriteForwarding()
  96. {
  97. PlcRegisterRepository repository;
  98. const RegisterAddress m0{RegisterArea::M, 0};
  99. const RegisterAddress d0{RegisterArea::D, 0};
  100. require(repository.readBit(m0).error == RegisterError::Unavailable,
  101. "uninitialized PLC bit cache must be unavailable");
  102. require(repository.readWord(d0).error == RegisterError::Unavailable,
  103. "uninitialized PLC word cache must be unavailable");
  104. repository.updateBit(0, true);
  105. repository.updateWord(0, -123);
  106. require(repository.readBit(m0).succeeded && repository.readBit(m0).value,
  107. "valid PLC bit cache must be readable");
  108. require(repository.readWord(d0).succeeded
  109. && repository.readWord(d0).value == -123,
  110. "valid PLC word cache must preserve signed values");
  111. RegisterAddress written_address{RegisterArea::M, 1};
  112. bool written_bit = false;
  113. std::int16_t written_word = 0;
  114. repository.setWriteHandlers(
  115. [&](const RegisterAddress &address, bool value)
  116. {
  117. written_address = address;
  118. written_bit = value;
  119. return RegisterWriteResult{true, RegisterError::None};
  120. },
  121. [&](const RegisterAddress &address, std::int16_t value)
  122. {
  123. written_address = address;
  124. written_word = value;
  125. return RegisterWriteResult{true, RegisterError::None};
  126. });
  127. require(repository.writeBit(m0, false).succeeded
  128. && written_address == m0 && !written_bit,
  129. "PLC bit writes must be forwarded without changing the cache");
  130. require(repository.writeWord(d0, 456).succeeded
  131. && written_address == d0 && written_word == 456,
  132. "PLC word writes must be forwarded without changing the cache");
  133. repository.invalidate();
  134. require(!repository.hasAnyValidValue(),
  135. "disconnecting must invalidate all PLC cache validity flags");
  136. }
  137. void testRuntimeRepositorySwitchingAndDisconnect()
  138. {
  139. TestProjectStorage storage;
  140. ProjectService project_service(storage);
  141. project_service.editProject().dataPoints.push_back(
  142. {{RegisterArea::M, 5}, "RunState", "运行状态"});
  143. VirtualRegisterRepository virtual_repository;
  144. PlcRegisterRepository plc_repository;
  145. ActiveRegisterRepository active_repository(virtual_repository);
  146. OfflineSimulationService simulation_service(virtual_repository);
  147. RuntimeModeService service(project_service, simulation_service);
  148. FakePlcGateway gateway;
  149. service.configurePlc(
  150. gateway, active_repository, virtual_repository, plc_repository);
  151. const PlcCommunicationResult connected = service.connectPlc(
  152. {"COM9", 2, 19200, 8, 2, 1, 1000, 2, 200});
  153. require(connected.succeeded && gateway.poll_addresses.size() == 1U,
  154. "PLC connection must receive the project address poll set");
  155. require(service.enterOnlineRunning().error
  156. == ModeTransitionError::InitialPlcReadRequired,
  157. "online mode must wait for the first valid PLC read");
  158. plc_repository.updateBit(5, true);
  159. gateway.completeInitialRead();
  160. require(service.enterOnlineRunning().succeeded,
  161. "online mode must start after the first valid PLC read");
  162. require(active_repository.readBit({RegisterArea::M, 5}).succeeded
  163. && active_repository.readBit({RegisterArea::M, 5}).value,
  164. "online mode must expose the PLC cache through the active repository");
  165. gateway.disconnectDevice();
  166. require(service.mode() == ApplicationMode::Editing,
  167. "an online disconnect must return the application to editing mode");
  168. require(!service.initialPlcReadCompleted(),
  169. "an online disconnect must clear the initial read flag");
  170. require(active_repository.readBit({RegisterArea::M, 5}).succeeded
  171. && !active_repository.readBit({RegisterArea::M, 5}).value,
  172. "editing after disconnect must switch back to the virtual repository");
  173. }
  174. } // namespace
  175. int main()
  176. {
  177. try
  178. {
  179. testPlcCacheAndWriteForwarding();
  180. testRuntimeRepositorySwitchingAndDisconnect();
  181. }
  182. catch (const std::exception &error)
  183. {
  184. std::cerr << "PLC runtime tests failed: " << error.what() << '\n';
  185. return 1;
  186. }
  187. std::cout << "PLC runtime tests passed\n";
  188. return 0;
  189. }