|
- #include "domain/active_register_repository.h"
- #include "domain/project_storage.h"
- #include "infrastructure/plc_register_repository.h"
- #include "services/offline_simulation_service.h"
- #include "services/plc_communication_gateway.h"
- #include "services/project_service.h"
- #include "services/runtime_mode_service.h"
-
- #include <functional>
- #include <iostream>
- #include <stdexcept>
- #include <utility>
-
- namespace {
-
- class TestProjectStorage final : public ProjectStorage
- {
- public:
- ProjectSaveResult save(const Project &, const std::string &) override
- {
- return {true, ProjectStorageError::None, {}};
- }
-
- ProjectLoadResult load(const std::string &) override
- {
- return {false, {}, ProjectStorageError::FileReadFailed, {}};
- }
- };
-
- class FakePlcGateway final : public PlcCommunicationGateway
- {
- public:
- PlcCommunicationResult connectDevice(
- const PlcSerialConfiguration &configuration) override
- {
- last_configuration = configuration;
- connection_state = PlcConnectionState::Connected;
- if (state_changed)
- {
- state_changed();
- }
- return {true, {}};
- }
-
- void disconnectDevice() override
- {
- connection_state = PlcConnectionState::Disconnected;
- initial_read = false;
- if (initial_read_changed)
- {
- initial_read_changed(false);
- }
- if (state_changed)
- {
- state_changed();
- }
- }
-
- void setPollAddresses(const std::vector<RegisterAddress> &addresses) override
- {
- poll_addresses = addresses;
- }
-
- PlcConnectionState state() const override { return connection_state; }
- bool initialReadCompleted() const override { return initial_read; }
- const std::string &lastError() const override { return last_error; }
-
- void setCallbacks(
- std::function<void()> state_callback,
- std::function<void(bool)> initial_callback,
- std::function<void()> cache_callback,
- std::function<void(const std::string &)> error_callback) override
- {
- state_changed = std::move(state_callback);
- initial_read_changed = std::move(initial_callback);
- cache_updated = std::move(cache_callback);
- error_reported = std::move(error_callback);
- }
-
- void completeInitialRead()
- {
- initial_read = true;
- if (initial_read_changed)
- {
- initial_read_changed(true);
- }
- }
-
- PlcConnectionState connection_state = PlcConnectionState::Disconnected;
- bool initial_read = false;
- std::string last_error;
- PlcSerialConfiguration last_configuration;
- std::vector<RegisterAddress> poll_addresses;
- std::function<void()> state_changed;
- std::function<void(bool)> initial_read_changed;
- std::function<void()> cache_updated;
- std::function<void(const std::string &)> error_reported;
- };
-
- void require(bool condition, const std::string &message)
- {
- if (!condition)
- {
- throw std::runtime_error(message);
- }
- }
-
- void testPlcCacheAndWriteForwarding()
- {
- PlcRegisterRepository repository;
- const RegisterAddress m0{RegisterArea::M, 0};
- const RegisterAddress d0{RegisterArea::D, 0};
- require(repository.readBit(m0).error == RegisterError::Unavailable,
- "uninitialized PLC bit cache must be unavailable");
- require(repository.readWord(d0).error == RegisterError::Unavailable,
- "uninitialized PLC word cache must be unavailable");
-
- repository.updateBit(0, true);
- repository.updateWord(0, -123);
- require(repository.readBit(m0).succeeded && repository.readBit(m0).value,
- "valid PLC bit cache must be readable");
- require(repository.readWord(d0).succeeded
- && repository.readWord(d0).value == -123,
- "valid PLC word cache must preserve signed values");
-
- RegisterAddress written_address{RegisterArea::M, 1};
- bool written_bit = false;
- std::int16_t written_word = 0;
- repository.setWriteHandlers(
- [&](const RegisterAddress &address, bool value)
- {
- written_address = address;
- written_bit = value;
- return RegisterWriteResult{true, RegisterError::None};
- },
- [&](const RegisterAddress &address, std::int16_t value)
- {
- written_address = address;
- written_word = value;
- return RegisterWriteResult{true, RegisterError::None};
- });
- require(repository.writeBit(m0, false).succeeded
- && written_address == m0 && !written_bit,
- "PLC bit writes must be forwarded without changing the cache");
- require(repository.writeWord(d0, 456).succeeded
- && written_address == d0 && written_word == 456,
- "PLC word writes must be forwarded without changing the cache");
- repository.invalidate();
- require(!repository.hasAnyValidValue(),
- "disconnecting must invalidate all PLC cache validity flags");
- }
-
- void testRuntimeRepositorySwitchingAndDisconnect()
- {
- TestProjectStorage storage;
- ProjectService project_service(storage);
- project_service.editProject().dataPoints.push_back(
- {{RegisterArea::M, 5}, "RunState", "运行状态"});
- VirtualRegisterRepository virtual_repository;
- PlcRegisterRepository plc_repository;
- ActiveRegisterRepository active_repository(virtual_repository);
- OfflineSimulationService simulation_service(virtual_repository);
- RuntimeModeService service(project_service, simulation_service);
- FakePlcGateway gateway;
- service.configurePlc(
- gateway, active_repository, virtual_repository, plc_repository);
-
- const PlcCommunicationResult connected = service.connectPlc(
- {"COM9", 2, 19200, 8, 2, 1, 1000, 2, 200});
- require(connected.succeeded && gateway.poll_addresses.size() == 1U,
- "PLC connection must receive the project address poll set");
- require(service.enterOnlineRunning().error
- == ModeTransitionError::InitialPlcReadRequired,
- "online mode must wait for the first valid PLC read");
-
- plc_repository.updateBit(5, true);
- gateway.completeInitialRead();
- require(service.enterOnlineRunning().succeeded,
- "online mode must start after the first valid PLC read");
- require(active_repository.readBit({RegisterArea::M, 5}).succeeded
- && active_repository.readBit({RegisterArea::M, 5}).value,
- "online mode must expose the PLC cache through the active repository");
-
- gateway.disconnectDevice();
- require(service.mode() == ApplicationMode::Editing,
- "an online disconnect must return the application to editing mode");
- require(!service.initialPlcReadCompleted(),
- "an online disconnect must clear the initial read flag");
- require(active_repository.readBit({RegisterArea::M, 5}).succeeded
- && !active_repository.readBit({RegisterArea::M, 5}).value,
- "editing after disconnect must switch back to the virtual repository");
- }
-
- } // namespace
-
- int main()
- {
- try
- {
- testPlcCacheAndWriteForwarding();
- testRuntimeRepositorySwitchingAndDisconnect();
- }
- catch (const std::exception &error)
- {
- std::cerr << "PLC runtime tests failed: " << error.what() << '\n';
- return 1;
- }
- std::cout << "PLC runtime tests passed\n";
- return 0;
- }
|