#include "domain/active_register_repository.h" #include "domain/register_monitor_model.h" #include "domain/register_repository.h" #include "services/register_monitor_service.h" #include #include #include namespace { void require(bool condition, const std::string &message) { if (!condition) { throw std::runtime_error(message); } } void testRangeManagement() { VirtualRegisterRepository repository; RegisterMonitorService service(repository); int change_count = 0; service.setAddressesChangedCallback([&change_count] { ++change_count; }); const RegisterMonitorResult added = service.addRange(" d10 ", 3); require(added.succeeded && added.affectedCount == 3 && service.addresses().size() == 3U, "monitor service must expand a normalized continuous address range"); require(service.addresses().front() == RegisterAddress{RegisterArea::D, 10} && service.addresses().back() == RegisterAddress{RegisterArea::D, 12}, "monitor service must preserve expanded address order"); const RegisterMonitorResult duplicate = service.addRange("D11", 2); require(!duplicate.succeeded && duplicate.error == RegisterMonitorError::NoChange, "monitor service must reject a fully duplicated range"); require(!service.addRange("M4000", 2).succeeded, "monitor service must reject a range crossing address 4000"); require(change_count == 1, "failed additions must not report a monitor address change"); require(service.remove({RegisterAddress{RegisterArea::D, 11}}).succeeded && service.addresses().size() == 2U, "monitor service must remove selected addresses"); require(service.clear().succeeded && service.addresses().empty(), "monitor service must clear the current monitor table"); require(change_count == 3, "successful add, remove and clear operations must notify address changes"); } void testSharedActiveRepositoryValues() { VirtualRegisterRepository virtual_repository; VirtualRegisterRepository plc_repository; ActiveRegisterRepository active_repository(virtual_repository); RegisterMonitorService service(active_repository); require(service.addRange("M0", 1).succeeded && service.addRange("D0", 1).succeeded, "monitor service must accept one M and one D address"); virtual_repository.writeBit({RegisterArea::M, 0}, true); virtual_repository.writeWord({RegisterArea::D, 0}, -123); std::vector values = service.values(false); require(values.size() == 2U && values[0].bitValue && values[1].wordValue == -123, "monitor values must reflect the active virtual repository"); plc_repository.writeBit({RegisterArea::M, 0}, false); plc_repository.writeWord({RegisterArea::D, 0}, 456); active_repository.use(plc_repository); values = service.values(false); require(!values[0].bitValue && values[1].wordValue == 456, "monitor values must follow the active repository switch"); values = service.values(true); require(values[0].state == MonitorValueState::CommunicationFault && values[1].state == MonitorValueState::CommunicationFault, "valid cached values must be marked stale during a communication fault"); } void testRegisterWrites() { VirtualRegisterRepository repository; RegisterMonitorService service(repository); const RegisterAddress m0{RegisterArea::M, 0}; const RegisterAddress d0{RegisterArea::D, 0}; const RegisterMonitorWriteResult bit_write = service.writeBit(m0, true); require(bit_write.succeeded && repository.readBit(m0).value, "monitor service must write M values through the active repository"); const RegisterMonitorWriteResult word_write = service.writeWord(d0, -123); require(word_write.succeeded && repository.readWord(d0).value == -123, "monitor service must write signed D values through the active repository"); const RegisterMonitorWriteResult wrong_bit = service.writeBit(d0, true); require(!wrong_bit.succeeded && wrong_bit.error == RegisterError::AreaMismatch, "monitor bit writes must reject D addresses"); const RegisterMonitorWriteResult wrong_word = service.writeWord(m0, 123); require(!wrong_word.succeeded && wrong_word.error == RegisterError::AreaMismatch, "monitor word writes must reject M addresses"); require(service.writeWord(d0, static_cast(-32768)).succeeded && repository.readWord(d0).value == -32768 && service.writeWord(d0, static_cast(32767)).succeeded && repository.readWord(d0).value == 32767, "monitor word writes must preserve signed 16-bit boundary values"); } } // namespace int main() { try { testRangeManagement(); testSharedActiveRepositoryValues(); testRegisterWrites(); } catch (const std::exception &error) { std::cerr << "register monitor service tests failed: " << error.what() << '\n'; return 1; } std::cout << "register monitor service tests passed\n"; return 0; }