modbus_rtu_lib: define read holding register
Add the modbus rtu library commands with initial support for read
holding register. Define a base Message classes which all subsequent and
specific request & response messages can inherit from. Also add the
relevant unit testing for the added command set.
Tested:
```
> meson test -C builddir
ninja: Entering directory `/host/repos/Modbus/phosphor-modbus/builddir'
ninja: no work to do.
1/1 test_modbus_commands OK 0.01s
Ok: 1
Fail: 0
```
Change-Id: I331b0dee66a0829e9352ae0eac8ac82a9150904c
Signed-off-by: Jagpal Singh Gill <paligill@gmail.com>
diff --git a/rtu/modbus/modbus_exception.hpp b/rtu/modbus/modbus_exception.hpp
new file mode 100644
index 0000000..9498a1c
--- /dev/null
+++ b/rtu/modbus/modbus_exception.hpp
@@ -0,0 +1,107 @@
+#pragma once
+
+#include <format>
+#include <stdexcept>
+#include <string>
+
+namespace phosphor::modbus::rtu
+{
+
+enum class ModbusExceptionCode
+{
+ // The Modbus function code in the request is not supported or is an invalid
+ // action for the server.
+ illegalFunctionCode = 1,
+
+ // The requested data address is not valid or authorized for the server.
+ illegalDataAddress = 2,
+
+ // The value provided in the request is not an allowable value for the
+ // server, or the data field is structured incorrectly.
+ illegalDataValue = 3,
+
+ // The server encountered an internal error and cannot perform the requested
+ // operation.
+ slaveDeviceFailure = 4,
+
+ // The server has accepted the request but needs more time to process it.
+ acknowledge = 5,
+
+ // The server is currently busy and cannot respond to the request.
+ slaveDeviceBusy = 6,
+
+ // The server cannot perform the program function received in the query.
+ // This code is returned for an unsuccessful programming request using
+ // function code 13 or 14 decimal. The client should request diagnostic or
+ // error information from the server.
+ negativeAcknowledge = 7,
+
+ // The server attempted to read extended memory, but detected a parity error
+ // in the memory. The client can retry the request, but service may be
+ // required on the server device.
+ memoryParityError = 8,
+ unknownError = 255,
+};
+
+class ModbusException : public std::runtime_error
+{
+ public:
+ const ModbusExceptionCode code;
+
+ explicit ModbusException(uint8_t code, const std::string& message = "") :
+ std::runtime_error(std::format(
+ "{} ({})", toString(static_cast<ModbusExceptionCode>(code)),
+ message)),
+ code(static_cast<ModbusExceptionCode>(code))
+ {}
+
+ static auto toString(ModbusExceptionCode code) -> std::string
+ {
+ switch (code)
+ {
+ case ModbusExceptionCode::illegalFunctionCode:
+ return "Illegal Function Code";
+ case ModbusExceptionCode::illegalDataAddress:
+ return "Illegal Data Address";
+ case ModbusExceptionCode::illegalDataValue:
+ return "Illegal Data Value";
+ case ModbusExceptionCode::slaveDeviceFailure:
+ return "Slave Device Failure";
+ case ModbusExceptionCode::acknowledge:
+ return "Acknowledge";
+ case ModbusExceptionCode::slaveDeviceBusy:
+ return "Slave Device Busy";
+ case ModbusExceptionCode::negativeAcknowledge:
+ return "Negative Acknowledge";
+ case ModbusExceptionCode::memoryParityError:
+ return "Memory Parity Error";
+ default:
+ return "Unknown Modbus Error";
+ }
+ }
+};
+
+class ModbusCRCException : public std::runtime_error
+{
+ public:
+ explicit ModbusCRCException(uint16_t expectedCRC, uint16_t crc) :
+ std::runtime_error(
+ "CRC mismatch, expected: " + std::to_string(expectedCRC) +
+ " received: " + std::to_string(crc))
+ {}
+};
+
+class ModbusBadResponseException : public std::runtime_error
+{
+ public:
+ explicit ModbusBadResponseException(const std::string& fieldName,
+ uint32_t expectedValue,
+ uint32_t currentValue) :
+ std::runtime_error(
+ "Value mismatch for " + fieldName +
+ ", expected value: " + std::to_string(expectedValue) +
+ ", current value: " + std::to_string(currentValue))
+ {}
+};
+
+} // namespace phosphor::modbus::rtu