| #include "modbus/modbus_commands.hpp" |
| #include "modbus/modbus_exception.hpp" |
| |
| #include <cstring> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| namespace RTUIntf = phosphor::modbus::rtu; |
| |
| class ReadHoldingRegistersResponseTest : |
| public RTUIntf::ReadHoldingRegistersResponse |
| { |
| public: |
| ReadHoldingRegistersResponseTest(uint8_t address, |
| std::vector<uint16_t>& registers) : |
| RTUIntf::ReadHoldingRegistersResponse(address, registers) |
| {} |
| |
| template <std::size_t Length> |
| ReadHoldingRegistersResponseTest& operator=( |
| const std::array<uint8_t, Length>& expectedMessage) |
| { |
| len = Length; |
| std::copy(expectedMessage.begin(), expectedMessage.end(), raw.begin()); |
| return *this; |
| } |
| }; |
| |
| class ModbusCommandTest : public ::testing::Test |
| { |
| public: |
| ModbusCommandTest() = default; |
| |
| static constexpr auto failureMessageLength = 11; |
| |
| template <typename ExceptionType, std::size_t Length> |
| void TestReadHoldingRegisterResponseFailure( |
| const std::array<uint8_t, Length>& expectedMessage) |
| { |
| constexpr uint8_t expectedAddress = 0xa; |
| constexpr size_t expectedRegisterSize = 3; |
| std::vector<uint16_t> registers(expectedRegisterSize); |
| ReadHoldingRegistersResponseTest response(expectedAddress, registers); |
| response = expectedMessage; |
| |
| EXPECT_THROW(response.decode(), ExceptionType); |
| } |
| }; |
| |
| TEST_F(ModbusCommandTest, TestReadHoldingRegistersRequestSucess) |
| { |
| constexpr size_t expectedLength = 8; |
| constexpr uint8_t expectedAddress = 0x1; |
| constexpr std::array<uint8_t, expectedLength> expectedMessage = { |
| expectedAddress, // addr(1) = 0x01 |
| 0x03, // func(1) = 0x03 |
| 0x12, // reg_off(2) = 0x1234 |
| 0x34, |
| 0x00, // reg_cnt(2) = 0x0020, |
| 0x20, |
| 0x00, // crc(2) (Pre-computed) = 0xa4 |
| 0xa4}; |
| |
| RTUIntf::ReadHoldingRegistersRequest request(expectedAddress, 0x1234, 0x20); |
| request.encode(); |
| |
| std::array<uint8_t, expectedLength> actualMessage; |
| std::memcpy(actualMessage.data(), request.raw.data(), expectedLength); |
| |
| EXPECT_EQ(actualMessage, expectedMessage); |
| EXPECT_EQ(request.address, expectedAddress); |
| } |
| |
| TEST_F(ModbusCommandTest, TestReadHoldingRegistersResponseSucess) |
| { |
| constexpr size_t expectedLength = 11; |
| constexpr uint8_t expectedAddress = 0xa; |
| constexpr size_t expectedRegisterSize = 3; |
| constexpr std::array<uint8_t, expectedLength> expectedMessage = { |
| expectedAddress, // addr(1) = 0x0a |
| 0x03, // func(1) = 0x03 |
| 0x06, // bytes(1) = 0x06 |
| 0x11, // regs(3*2) = 0x1122, 0x3344, 0x5566 |
| 0x22, |
| 0x33, |
| 0x44, |
| 0x55, |
| 0x66, |
| 0x59, // crc(2) = 0x5928 (pre-computed) |
| 0x28}; |
| const std::vector<uint16_t> expectedRegisters{0x1122, 0x3344, 0x5566}; |
| std::vector<uint16_t> registers(expectedRegisterSize); |
| |
| RTUIntf::ReadHoldingRegistersResponse response(expectedAddress, registers); |
| EXPECT_EQ(response.len, expectedLength); |
| |
| std::copy(expectedMessage.begin(), expectedMessage.end(), |
| response.raw.begin()); |
| |
| EXPECT_EQ(response.len, expectedLength); |
| response.decode(); |
| EXPECT_EQ(registers, expectedRegisters); |
| } |
| |
| TEST_F(ModbusCommandTest, TestReadHoldingRegistersResponseError) |
| { |
| constexpr std::array<uint8_t, 5> expectedMessage = { |
| 0xa, // addr(1) = 0x0a |
| 0x83, // func(1) = 0x83 |
| 0x03, // exception code(1) = 0x03 |
| 0x70, // crc(2) = 0x70f3 (pre-computed) |
| 0xf3}; |
| |
| TestReadHoldingRegisterResponseFailure<RTUIntf::ModbusException>( |
| expectedMessage); |
| } |
| |
| TEST_F(ModbusCommandTest, TestReadHoldingRegistersResponseBadAddress) |
| { |
| constexpr std::array<uint8_t, failureMessageLength> expectedMessage = { |
| 0x1, // Bad address(1), should be 0x0a |
| 0x03, // func(1) = 0x03 |
| 0x06, // bytes(1) = 0x06 |
| 0x11, // regs(3*2) = 0x1122, 0x3344, 0x5566 |
| 0x22, 0x33, 0x44, 0x55, 0x66, |
| 0x2a, // crc(2) = 0x2a18 (pre-computed) |
| 0x18}; |
| |
| TestReadHoldingRegisterResponseFailure<RTUIntf::ModbusBadResponseException>( |
| expectedMessage); |
| } |
| |
| TEST_F(ModbusCommandTest, TestReadHoldingRegistersResponseBadCRC) |
| { |
| constexpr std::array<uint8_t, failureMessageLength> expectedMessage = { |
| 0xa, // addr(1) = 0x0a |
| 0x03, // func(1) = 0x03 |
| 0x06, // bytes(1) = 0x06 |
| 0x11, // regs(3*2) = 0x1122, 0x3344, 0x5566 |
| 0x22, 0x33, 0x44, 0x55, 0x66, |
| 0x59, // Bad crc(2), should be 0x5928 |
| 0x29}; |
| |
| TestReadHoldingRegisterResponseFailure<RTUIntf::ModbusCRCException>( |
| expectedMessage); |
| } |
| |
| TEST_F(ModbusCommandTest, TestReadHoldingRegistersResponseBadFunctionCode) |
| { |
| constexpr std::array<uint8_t, failureMessageLength> expectedMessage = { |
| 0xa, // addr(1) = 0x0a |
| 0x04, // Bad function code(1), should be 0x03 |
| 0x06, // bytes(1) = 0x06 |
| 0x11, // regs(3*2) = 0x1122, 0x3344, 0x5566 |
| 0x22, 0x33, 0x44, 0x55, 0x66, |
| 0x18, // crc(2) = 0x18ce (pre-computed) |
| 0xce}; |
| |
| TestReadHoldingRegisterResponseFailure<RTUIntf::ModbusBadResponseException>( |
| expectedMessage); |
| } |
| |
| TEST_F(ModbusCommandTest, TestReadHoldingRegistersResponseBadByteCount) |
| { |
| constexpr std::array<uint8_t, failureMessageLength> expectedMessage = { |
| 0xa, // addr(1) = 0x0a |
| 0x03, // func(1) = 0x03 |
| 0x04, // Bad bytes(1), should be 0x06 |
| 0x11, // regs(3*2) = 0x1122, 0x3344, 0x5566 |
| 0x22, 0x33, 0x44, 0x55, 0x66, |
| 0x7a, // crc(2) = 0x7ae8 (pre-computed) |
| 0xe8}; |
| |
| TestReadHoldingRegisterResponseFailure<RTUIntf::ModbusBadResponseException>( |
| expectedMessage); |
| } |