From 86fed624d1b71db343893ef83b94726266c74253 Mon Sep 17 00:00:00 2001 From: Peter M. Groen Date: Mon, 12 Dec 2022 00:40:35 +0100 Subject: [PATCH] Add TCP connection --- src/CMakeLists.txt | 4 ++-- src/modbus.cpp | 385 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/modbus.h | 221 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/modbusbase.cpp | 370 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/modbusbase.h | 221 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/modbustcp.h | 28 ++++++++++++++++++++-------- 6 files changed, 613 insertions(+), 616 deletions(-) delete mode 100644 src/modbus.cpp delete mode 100644 src/modbus.h create mode 100644 src/modbusbase.cpp create mode 100644 src/modbusbase.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dc15081..ebfcf34 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,8 +12,8 @@ include_directories( set(SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/imodbusport.h - ${CMAKE_CURRENT_SOURCE_DIR}/modbus.h - ${CMAKE_CURRENT_SOURCE_DIR}/modbus.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/modbusbase.h + ${CMAKE_CURRENT_SOURCE_DIR}/modbusbase.cpp ${CMAKE_CURRENT_SOURCE_DIR}/modbustcp.h ${CMAKE_CURRENT_SOURCE_DIR}/modbustcp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/modbusrtu.h diff --git a/src/modbus.cpp b/src/modbus.cpp deleted file mode 100644 index 2565a3e..0000000 --- a/src/modbus.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright (c) 2022 Peter M. Groen - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "modbus.h" - -using namespace osdev::components::modbus; - -int ModbusBase::readCoils(uint16_t address, uint16_t amount, bool *buffer) -{ - if (m_connected) - { - if (amount > 2040) - { - setBadInput(); - return EX_BAD_DATA; - } - - modbusRead(address, amount, READ_COILS); - uint8_t *to_rec = new uint8_t[m_max_message_length]; - ssize_t result = modbusReceive(to_rec); - if (result == -1) - { - setBadConnection(); - return BAD_CON; - } - - modbusErrorHandle(to_rec, READ_COILS); - if (m_error) - { - return m_error_number; - } - - for (auto i = 0; i < amount; i++) - { - buffer[i] = static_cast((to_rec[9u + i / 8u] >> (i % 8u)) & 1u); - } - return 0; - } - else - { - setBadConnection(); - return BAD_CON; - } -} - -int ModbusBase::readInputBits(uint16_t address, uint16_t amount, bool *buffer) -{ - if (m_connected) - { - if (amount > 2040) - { - setBadInput(); - return EX_BAD_DATA; - } - - modbusRead(address, amount, READ_INPUT_BITS); - uint8_t *to_rec = new uint8_t[m_max_message_length]; - ssize_t result = modbusReceive(to_rec); - if (result == -1) - { - setBadConnection(); - return BAD_CON; - } - - if (m_error) - { - return m_error_number; - } - - for (auto i = 0; i < amount; i++) - { - buffer[i] = static_cast((to_rec[9u + i / 8u] >> (i % 8u)) & 1u); - } - modbusErrorHandle(to_rec, READ_INPUT_BITS); - return 0; - } - else - { - return BAD_CON; - } -} - -int ModbusBase::readHoldingRegisters(uint16_t address, uint16_t amount, uint16_t *buffer) -{ - if (m_connected) - { - modbusRead(address, amount, READ_REGS); - uint8_t *to_rec = new uint8_t[m_max_message_length]; - ssize_t result = modbusReceive(to_rec); - if (result == -1) - { - setBadConnection(); - return BAD_CON; - } - modbusErrorHandle(to_rec, READ_REGS); - if (m_error) - { - return m_error_number; - } - for (auto i = 0; i < amount; i++) - { - buffer[i] = (static_cast(to_rec[9u + 2u * i])) << 8u; - buffer[i] += static_cast(to_rec[10u + 2u * i]); - } - return 0; - } - else - { - setBadConnection(); - return BAD_CON; - } -} - -int ModbusBase::readInputRegisters(uint16_t address, uint16_t amount, uint16_t *buffer) -{ - if (m_connected) - { - modbusRead(address, amount, READ_INPUT_REGS); - uint8_t *to_rec = new uint8_t[m_max_message_length]; - ssize_t result = modbusReceive(to_rec); - if (result == -1) - { - setBadConnection(); - return BAD_CON; - } - - modbusErrorHandle(to_rec, READ_INPUT_REGS); - if (m_error) - { - return m_error_number; - } - - for (auto i = 0; i < amount; i++) - { - buffer[i] = (static_cast(to_rec[9u + 2u * i])) << 8u; - buffer[i] = static_cast(to_rec[10u + 2u * i]); - } - return 0; - } - else - { - setBadConnection(); - return BAD_CON; - } -} - -int ModbusBase::writeCoil(uint16_t address, const bool &to_write) -{ - if (m_connected) - { - int value = to_write * 0xFF00; - modbusWrite(address, 1, WRITE_COIL, reinterpret_cast(&value)); - uint8_t *to_rec = new uint8_t[m_max_message_length]; - ssize_t result = modbusReceive(to_rec); - if (result == -1) - { - setBadConnection(); - return BAD_CON; - } - - modbusErrorHandle(to_rec, WRITE_COIL); - if (m_error) - { - return m_error_number; - } - return 0; - } - else - { - setBadConnection(); - return BAD_CON; - } -} - -int ModbusBase::writeRegister(uint16_t address, const uint16_t &value) -{ - if (m_connected) - { - modbusWrite(address, 1, WRITE_REG, &value); - uint8_t *to_rec = new uint8_t[m_max_message_length]; - ssize_t result = modbusReceive(to_rec); - if (result == -1) - { - setBadConnection(); - return BAD_CON; - } - - modbusErrorHandle(to_rec, WRITE_COIL); - if (m_error) - { - return m_error_number; - } - return 0; - } - else - { - setBadConnection(); - return BAD_CON; - } -} - -int ModbusBase::writeCoils(uint16_t address, uint16_t amount, const bool *value) -{ - if (m_connected) - { - uint16_t *temp = new uint16_t[amount]; - for (int i = 0; i < amount; i++) - { - temp[i] = static_cast(value[i]); - } - - modbusWrite(address, amount, WRITE_COILS, temp); - delete[] temp; - - uint8_t *to_rec = new uint8_t[m_max_message_length]; - ssize_t result = modbusReceive(to_rec); - if (result == -1) - { - setBadConnection(); - return BAD_CON; - } - - modbusErrorHandle(to_rec, WRITE_COILS); - if (m_error) - { - return m_error_number; - } - return 0; - } - else - { - setBadConnection(); - return BAD_CON; - } -} - -int ModbusBase::writeRegisters(uint16_t address, uint16_t amount, const uint16_t *value) -{ - if (m_connected) - { - modbusWrite(address ,amount, WRITE_REGS, value); - uint8_t *to_rec = new uint8_t[m_max_message_length]; - ssize_t result = modbusReceive(to_rec); - if (result == -1) - { - setBadConnection(); - return BAD_CON; - } - - modbusErrorHandle(to_rec, WRITE_COILS); - if (m_error) - { - return m_error_number; - } - return 0; - } - else - { - setBadConnection(); - return BAD_CON; - } -} - -void ModbusBase::buildRequest(uint8_t *to_send, uint16_t address, int function_code) const -{ - to_send[0] = static_cast(m_msg_id >> 8u); - to_send[1] = static_cast(m_msg_id & 0x00FFu); - to_send[2] = 0; - to_send[3] = 0; - to_send[4] = 0; - to_send[6] = static_cast(m_slaveId); - to_send[7] = static_cast(function_code); - to_send[8] = static_cast(address >> 8u); - to_send[9] = static_cast(address & 0x00FFu); -} - -int ModbusBase::modbusRead(uint16_t address, uint16_t amount, int function_code) -{ - // TODO: Building the frames should be dependant on the chosen transport layer.. ( 256 for RTU, 260 for TCP ) - // Declare as pure virtual and implement in the transport-specific class? - // For now we focus on TCP as it is easier to implement. - uint8_t to_send[12]; - buildRequest(to_send, address, function_code); - to_send[5] = 6; - to_send[10] = static_cast(amount >> 8u); - to_send[11] = static_cast(amount & 0x00FFu); - return modbusSend(to_send, 12); -} - -int ModbusBase::modbusWrite(uint16_t address, uint16_t amount, int function_code, const uint16_t *value) -{ - // TODO: Building the frames should be dependant on the chosen transport layer.. ( 256 for RTU, 260 for TCP ) - // Declare as pure virtual and implement in the transport-specific class? - // For now we focus on TCP as it is easier to implement. - int status = 0; - uint8_t *to_send; - - switch (function_code) - { - // Intentionally fall-through - case WRITE_COIL: - case WRITE_REG: - { - to_send = new uint8_t[12]; - buildRequest(to_send, address, function_code); - to_send[5] = 6; - to_send[10] = static_cast(value[0] >> 8u); - to_send[11] = static_cast(value[0] & 0x00FFu); - status = modbusSend(to_send, 12); - break; - } - case WRITE_REGS: - { - to_send = new uint8_t[13 + 2 * amount]; - buildRequest(to_send, address, function_code); - to_send[5] = static_cast(7 + 2 * amount); - to_send[10] = static_cast(amount >> 8u); - to_send[11] = static_cast(amount & 0x00FFu); - to_send[12] = static_cast(2 * amount); - for (int i = 0; i < amount; i++) - { - to_send[13 + 2 * i] = static_cast(value[i] >> 8u); - to_send[14 + 2 * i] = static_cast(value[i] & 0x00FFu); - } - status = modbusSend(to_send, 13 + 2 * amount); - break; - } - case WRITE_COILS: - { - to_send = new uint8_t[14 + ( amount - 1 ) / 8]; - buildRequest(to_send, address, function_code); - to_send[5] = static_cast(7 + ( amount + 7 ) / 8); - to_send[10] = static_cast(amount >> 8u); - to_send[11] = static_cast(amount & 0x00FFu); - to_send[12] = static_cast((amount + 7) / 8); - for (int i = 0; i < (amount + 7) / 8; i++) - { - to_send[13 + i] = 0; // Init needed before summing. - } - for (int i = 0; i < amount; i++) - { - to_send[13 + i / 8] += static_cast(value[i] << (i % 8u)); - } - status = modbusSend(to_send, 13 + (amount - 1) / 8); - } - } - delete[] to_send; - return status; -} - -ssize_t ModbusBase::modbusSend(uint8_t *to_send, size_t length) -{ - (void)to_send; - (void)length; - - return 0; -} - -ssize_t ModbusBase::modbusReceive(uint8_t *buffer) const -{ - (void)buffer; - - return 0; -} - -void ModbusBase::modbusErrorHandle(const uint8_t *msg, int function_code) -{ - (void)msg; - (void)function_code; -} - -void ModbusBase::setBadConnection() -{ - -} - -void ModbusBase::setBadInput() -{ - -} - diff --git a/src/modbus.h b/src/modbus.h deleted file mode 100644 index 9514a98..0000000 --- a/src/modbus.h +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (c) 2022 Peter M. Groen - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ -#pragma once - -#include -#include -#include - -// Create a simple logger for console output during debugging. -// TODO: Replace with a custom logger by using std::function -// to keep it consistent with TrueMQTT. -#ifdef ENABLE_MODBUS_LOGGING -#include -#define LOG(fmt, ...) printf("[ modbus-cpp ]" fmt, ##__VA_ARGS__) -#else -#define LOG(...) (void)0 -#endif - -// Function Codes -#define READ_COILS 0x01 -#define READ_INPUT_BITS 0x02 -#define READ_REGS 0x03 -#define READ_INPUT_REGS 0x04 -#define WRITE_COIL 0x05 -#define WRITE_REG 0x06 -#define WRITE_COILS 0x0F -#define WRITE_REGS 0x10 - -// Exception codes -#define EX_ILLEGAL_FUNCTION 0x01 -#define EX_ILLEGAL_ADDRESS 0x02 -#define EX_ILLEGAL_VALUE 0x03 -#define EX_SERVER_FAILURE 0x04 -#define EX_ACKNOWLEDGE 0x05 -#define EX_SERVER_BUSY 0x06 -#define EX_NEGATIVE_ACK 0x07 -#define EX_MEM_PARITY_PROB 0x08 -#define EX_GATEWAY_PROBLEMP 0x0A -#define EX_GATEWAY_PROBLEMF 0x0B -#define EX_BAD_DATA 0xFF - -#define BAD_CON -1 - -namespace osdev { -namespace components { -namespace modbus { - -// Modbus base class. Providing all Modbus PDUs without transport implementation -class Modbus -{ -public: - Modbus(); - virtual ~Modbus(); - - // Pure virtuals. Override when inherited. - virtual bool Connect() = 0; - virtual void Close() = 0; - - // Modbus implementation(s) - /*! - * Read Coils - * MODBUS Function 0x01 - * \param address - Reference Address - * \param amount - Amount of Coils to Read - * \param buffer - Buffer to Store Data Read from Coils - */ - int readCoils(uint16_t address, uint16_t amount, bool *buffer); // Replace buffer with something sensible? - - /*! - * Read Input Bits - * MODBUS Function 0x02 - * \param address - Refernce Address - * \param amount - Amount of BITS to Read - * \param buffer - Buffer to store Data Read from Input Bits - */ - int readInputBits(uint16_t address, uint16_t amount, bool *buffer); // Replace buffer with something sensible? - - /*! - * Read Holding Registers - * MODBUS Function 0x03 - * \param address - Reference Address - * \param amount - Amount of Registers to Read - * \param buffer - Buffer to Store Data Read from Registers - */ - int readHoldingRegisters(uint16_t address, uint16_t amount, uint16_t *buffer); - - /*! - * Read Input Registers - * MODBUS Function 0x04 - * \param address - Reference Address - * \param amount - Amount of registers to read - * \param buffer - Buffer to store Data Read from Registers - */ - int readInputRegisters(uint16_t address, uint16_t amount, uint16_t *buffer); - - /*! - * Write Single Coils - * MODBUS Function 0x05 - * \param address - Reference Address - * \param to_write - Value to be written to Coil - */ - int writeCoil(uint16_t address, const bool &to_write); - - /*! - * Write Single Register - * MODBUS Function 0x06 - * \param address - Reference Address - * \param value - Value to Be Written to Register - */ - int writeRegister(uint16_t address, const uint16_t &value); - - /*! - * Write Multiple Coils - * MODBUS Function 0x0F - * \param address - Reference Address - * \param amount - Amount of coils to write - * \param value - Values to Be Written to Coils - */ - int writeCoils(uint16_t address, uint16_t amount, const bool *value); - - /*! - * Write Multiple Registers - * MODBUS Function 0x10 - * \param address - Reference Address - * \param amount - Amount of Value to Write - * \param value - Values to Be Written to the Registers - */ - int writeRegisters(uint16_t address, uint16_t amount, const uint16_t *value); - - // Getters and Setters. - void setConnected(bool connected = false){ m_connected = connected;} - bool getConnected() const { return m_connected; } - void setMessageId( uint32_t message_id ) { m_msg_id = message_id; } - uint32_t getMessageId() const { return m_msg_id; } - void setSlaveId(int slave_id){ m_slaveId = slave_id; } - int getSlaveId() const { return m_slaveId; } - void setError(bool error, int error_number = 0, const std::string &error_message = std::string()) - { - m_error = error; - m_error_number = error_number; - m_error_message = error_message; - } - bool getError() const { return m_error; } - int getErrorNumber() const { return m_error_number; } - std::string getErrorMessage() const { return m_error_message;} - void setMaxMessageLength(unsigned int max_message_length) { m_max_message_length = max_message_length; } - unsigned int getMaxMessageLength() const { return m_max_message_length; } - -private: // Methods - /*! - * Modbus Request Builder - * \param to_send - Message buffer to be send - * \param address - Reference Address - * \param function_code - Modbus Functional Code - */ - void buildRequest(uint8_t *to_send, uint16_t address, int function_code) const; - - int modbusRead(uint16_t address, uint16_t amount, int function_code); - - /*! - * Write Request Builder and Sender - * \param address - Reference address - * \param amount - Amount of data to be written - * \param function_code - Modbus Functional Code - * \param value - Data to be written - * - * \return int - - */ - int modbusWrite(uint16_t address, uint16_t amount, int function_code, const uint16_t *value); - - /*! - * \brief modbusSend - * \param to_send - * \param length - * \return - */ - virtual ssize_t modbusSend(uint8_t *to_send, size_t length) = 0; - - /*! - * \brief modbusReceive - * \param buffer - * \return - */ - virtual ssize_t modbusReceive(uint8_t *buffer) const = 0; - - /*! - * \brief modbusErrorHandle - * \param msg - * \param function_code - */ - void modbusErrorHandle(const uint8_t *msg, int function_code); - - /*! - * \brief setBadConnection - */ - void setBadConnection(); - - /*! - * \brief setBadInput - */ - void setBadInput(); - -private: // Members (Giggity!) - bool m_connected{}; - uint32_t m_msg_id{}; - int m_slaveId{}; - bool m_error{}; - int m_error_number{}; - std::string m_error_message; - unsigned int m_max_message_length; - -}; - -} // End namespace modbus -} // End namespace components -} // End namespace osdev - diff --git a/src/modbusbase.cpp b/src/modbusbase.cpp new file mode 100644 index 0000000..b88bba3 --- /dev/null +++ b/src/modbusbase.cpp @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2022 Peter M. Groen + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "modbusbase.h" + +using namespace osdev::components::modbus; + +int ModbusBase::readCoils(uint16_t address, uint16_t amount, bool *buffer) +{ + if (m_connected) + { + if (amount > 2040) + { + setBadInput(); + return EX_BAD_DATA; + } + + modbusRead(address, amount, READ_COILS); + uint8_t *to_rec = new uint8_t[m_max_message_length]; + ssize_t result = modbusReceive(to_rec); + if (result == -1) + { + setBadConnection(); + return BAD_CON; + } + + modbusErrorHandle(to_rec, READ_COILS); + if (m_error) + { + return m_error_number; + } + + for (auto i = 0; i < amount; i++) + { + buffer[i] = static_cast((to_rec[9u + i / 8u] >> (i % 8u)) & 1u); + } + return 0; + } + else + { + setBadConnection(); + return BAD_CON; + } +} + +int ModbusBase::readInputBits(uint16_t address, uint16_t amount, bool *buffer) +{ + if (m_connected) + { + if (amount > 2040) + { + setBadInput(); + return EX_BAD_DATA; + } + + modbusRead(address, amount, READ_INPUT_BITS); + uint8_t *to_rec = new uint8_t[m_max_message_length]; + ssize_t result = modbusReceive(to_rec); + if (result == -1) + { + setBadConnection(); + return BAD_CON; + } + + if (m_error) + { + return m_error_number; + } + + for (auto i = 0; i < amount; i++) + { + buffer[i] = static_cast((to_rec[9u + i / 8u] >> (i % 8u)) & 1u); + } + modbusErrorHandle(to_rec, READ_INPUT_BITS); + return 0; + } + else + { + return BAD_CON; + } +} + +int ModbusBase::readHoldingRegisters(uint16_t address, uint16_t amount, uint16_t *buffer) +{ + if (m_connected) + { + modbusRead(address, amount, READ_REGS); + uint8_t *to_rec = new uint8_t[m_max_message_length]; + ssize_t result = modbusReceive(to_rec); + if (result == -1) + { + setBadConnection(); + return BAD_CON; + } + modbusErrorHandle(to_rec, READ_REGS); + if (m_error) + { + return m_error_number; + } + for (auto i = 0; i < amount; i++) + { + buffer[i] = (static_cast(to_rec[9u + 2u * i])) << 8u; + buffer[i] += static_cast(to_rec[10u + 2u * i]); + } + return 0; + } + else + { + setBadConnection(); + return BAD_CON; + } +} + +int ModbusBase::readInputRegisters(uint16_t address, uint16_t amount, uint16_t *buffer) +{ + if (m_connected) + { + modbusRead(address, amount, READ_INPUT_REGS); + uint8_t *to_rec = new uint8_t[m_max_message_length]; + ssize_t result = modbusReceive(to_rec); + if (result == -1) + { + setBadConnection(); + return BAD_CON; + } + + modbusErrorHandle(to_rec, READ_INPUT_REGS); + if (m_error) + { + return m_error_number; + } + + for (auto i = 0; i < amount; i++) + { + buffer[i] = (static_cast(to_rec[9u + 2u * i])) << 8u; + buffer[i] = static_cast(to_rec[10u + 2u * i]); + } + return 0; + } + else + { + setBadConnection(); + return BAD_CON; + } +} + +int ModbusBase::writeCoil(uint16_t address, const bool &to_write) +{ + if (m_connected) + { + int value = to_write * 0xFF00; + modbusWrite(address, 1, WRITE_COIL, reinterpret_cast(&value)); + uint8_t *to_rec = new uint8_t[m_max_message_length]; + ssize_t result = modbusReceive(to_rec); + if (result == -1) + { + setBadConnection(); + return BAD_CON; + } + + modbusErrorHandle(to_rec, WRITE_COIL); + if (m_error) + { + return m_error_number; + } + return 0; + } + else + { + setBadConnection(); + return BAD_CON; + } +} + +int ModbusBase::writeRegister(uint16_t address, const uint16_t &value) +{ + if (m_connected) + { + modbusWrite(address, 1, WRITE_REG, &value); + uint8_t *to_rec = new uint8_t[m_max_message_length]; + ssize_t result = modbusReceive(to_rec); + if (result == -1) + { + setBadConnection(); + return BAD_CON; + } + + modbusErrorHandle(to_rec, WRITE_COIL); + if (m_error) + { + return m_error_number; + } + return 0; + } + else + { + setBadConnection(); + return BAD_CON; + } +} + +int ModbusBase::writeCoils(uint16_t address, uint16_t amount, const bool *value) +{ + if (m_connected) + { + uint16_t *temp = new uint16_t[amount]; + for (int i = 0; i < amount; i++) + { + temp[i] = static_cast(value[i]); + } + + modbusWrite(address, amount, WRITE_COILS, temp); + delete[] temp; + + uint8_t *to_rec = new uint8_t[m_max_message_length]; + ssize_t result = modbusReceive(to_rec); + if (result == -1) + { + setBadConnection(); + return BAD_CON; + } + + modbusErrorHandle(to_rec, WRITE_COILS); + if (m_error) + { + return m_error_number; + } + return 0; + } + else + { + setBadConnection(); + return BAD_CON; + } +} + +int ModbusBase::writeRegisters(uint16_t address, uint16_t amount, const uint16_t *value) +{ + if (m_connected) + { + modbusWrite(address ,amount, WRITE_REGS, value); + uint8_t *to_rec = new uint8_t[m_max_message_length]; + ssize_t result = modbusReceive(to_rec); + if (result == -1) + { + setBadConnection(); + return BAD_CON; + } + + modbusErrorHandle(to_rec, WRITE_COILS); + if (m_error) + { + return m_error_number; + } + return 0; + } + else + { + setBadConnection(); + return BAD_CON; + } +} + +void ModbusBase::buildRequest(uint8_t *to_send, uint16_t address, int function_code) const +{ + to_send[0] = static_cast(m_msg_id >> 8u); + to_send[1] = static_cast(m_msg_id & 0x00FFu); + to_send[2] = 0; + to_send[3] = 0; + to_send[4] = 0; + to_send[6] = static_cast(m_slaveId); + to_send[7] = static_cast(function_code); + to_send[8] = static_cast(address >> 8u); + to_send[9] = static_cast(address & 0x00FFu); +} + +int ModbusBase::modbusRead(uint16_t address, uint16_t amount, int function_code) +{ + // TODO: Building the frames should be dependant on the chosen transport layer.. ( 256 for RTU, 260 for TCP ) + // Declare as pure virtual and implement in the transport-specific class? + // For now we focus on TCP as it is easier to implement. + uint8_t to_send[12]; + buildRequest(to_send, address, function_code); + to_send[5] = 6; + to_send[10] = static_cast(amount >> 8u); + to_send[11] = static_cast(amount & 0x00FFu); + return modbusSend(to_send, 12); +} + +int ModbusBase::modbusWrite(uint16_t address, uint16_t amount, int function_code, const uint16_t *value) +{ + // TODO: Building the frames should be dependant on the chosen transport layer.. ( 256 for RTU, 260 for TCP ) + // Declare as pure virtual and implement in the transport-specific class? + // For now we focus on TCP as it is easier to implement. + int status = 0; + uint8_t *to_send; + + switch (function_code) + { + // Intentionally fall-through + case WRITE_COIL: + case WRITE_REG: + { + to_send = new uint8_t[12]; + buildRequest(to_send, address, function_code); + to_send[5] = 6; + to_send[10] = static_cast(value[0] >> 8u); + to_send[11] = static_cast(value[0] & 0x00FFu); + status = modbusSend(to_send, 12); + break; + } + case WRITE_REGS: + { + to_send = new uint8_t[13 + 2 * amount]; + buildRequest(to_send, address, function_code); + to_send[5] = static_cast(7 + 2 * amount); + to_send[10] = static_cast(amount >> 8u); + to_send[11] = static_cast(amount & 0x00FFu); + to_send[12] = static_cast(2 * amount); + for (int i = 0; i < amount; i++) + { + to_send[13 + 2 * i] = static_cast(value[i] >> 8u); + to_send[14 + 2 * i] = static_cast(value[i] & 0x00FFu); + } + status = modbusSend(to_send, 13 + 2 * amount); + break; + } + case WRITE_COILS: + { + to_send = new uint8_t[14 + ( amount - 1 ) / 8]; + buildRequest(to_send, address, function_code); + to_send[5] = static_cast(7 + ( amount + 7 ) / 8); + to_send[10] = static_cast(amount >> 8u); + to_send[11] = static_cast(amount & 0x00FFu); + to_send[12] = static_cast((amount + 7) / 8); + for (int i = 0; i < (amount + 7) / 8; i++) + { + to_send[13 + i] = 0; // Init needed before summing. + } + for (int i = 0; i < amount; i++) + { + to_send[13 + i / 8] += static_cast(value[i] << (i % 8u)); + } + status = modbusSend(to_send, 13 + (amount - 1) / 8); + } + } + delete[] to_send; + return status; +} + +void ModbusBase::modbusErrorHandle(const uint8_t *msg, int function_code) +{ + (void)msg; + (void)function_code; +} + +void ModbusBase::setBadConnection() +{ + +} + +void ModbusBase::setBadInput() +{ + +} + diff --git a/src/modbusbase.h b/src/modbusbase.h new file mode 100644 index 0000000..f13e087 --- /dev/null +++ b/src/modbusbase.h @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2022 Peter M. Groen + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +#pragma once + +#include +#include +#include + +// Create a simple logger for console output during debugging. +// TODO: Replace with a custom logger by using std::function +// to keep it consistent with TrueMQTT. +#ifdef ENABLE_MODBUS_LOGGING +#include +#define LOG(fmt, ...) printf("[ modbus-cpp ]" fmt, ##__VA_ARGS__) +#else +#define LOG(...) (void)0 +#endif + +// Function Codes +#define READ_COILS 0x01 +#define READ_INPUT_BITS 0x02 +#define READ_REGS 0x03 +#define READ_INPUT_REGS 0x04 +#define WRITE_COIL 0x05 +#define WRITE_REG 0x06 +#define WRITE_COILS 0x0F +#define WRITE_REGS 0x10 + +// Exception codes +#define EX_ILLEGAL_FUNCTION 0x01 +#define EX_ILLEGAL_ADDRESS 0x02 +#define EX_ILLEGAL_VALUE 0x03 +#define EX_SERVER_FAILURE 0x04 +#define EX_ACKNOWLEDGE 0x05 +#define EX_SERVER_BUSY 0x06 +#define EX_NEGATIVE_ACK 0x07 +#define EX_MEM_PARITY_PROB 0x08 +#define EX_GATEWAY_PROBLEMP 0x0A +#define EX_GATEWAY_PROBLEMF 0x0B +#define EX_BAD_DATA 0xFF + +#define BAD_CON -1 + +namespace osdev { +namespace components { +namespace modbus { + +// Modbus base class. Providing all Modbus PDUs without transport implementation +class ModbusBase +{ +public: + ModbusBase(); + virtual ~ModbusBase(); + + // Pure virtuals. Override when inherited. + virtual bool Connect() = 0; + virtual bool Close() = 0; + + /*! + * \brief modbusSend + * \param to_send + * \param length + * \return + */ + virtual ssize_t modbusSend(uint8_t *to_send, size_t length) = 0; + + /*! + * \brief modbusReceive + * \param buffer + * \return + */ + virtual ssize_t modbusReceive(uint8_t *buffer) = 0; + + // Modbus implementation(s) + /*! + * Read Coils + * MODBUS Function 0x01 + * \param address - Reference Address + * \param amount - Amount of Coils to Read + * \param buffer - Buffer to Store Data Read from Coils + */ + int readCoils(uint16_t address, uint16_t amount, bool *buffer); // Replace buffer with something sensible? + + /*! + * Read Input Bits + * MODBUS Function 0x02 + * \param address - Refernce Address + * \param amount - Amount of BITS to Read + * \param buffer - Buffer to store Data Read from Input Bits + */ + int readInputBits(uint16_t address, uint16_t amount, bool *buffer); // Replace buffer with something sensible? + + /*! + * Read Holding Registers + * MODBUS Function 0x03 + * \param address - Reference Address + * \param amount - Amount of Registers to Read + * \param buffer - Buffer to Store Data Read from Registers + */ + int readHoldingRegisters(uint16_t address, uint16_t amount, uint16_t *buffer); + + /*! + * Read Input Registers + * MODBUS Function 0x04 + * \param address - Reference Address + * \param amount - Amount of registers to read + * \param buffer - Buffer to store Data Read from Registers + */ + int readInputRegisters(uint16_t address, uint16_t amount, uint16_t *buffer); + + /*! + * Write Single Coils + * MODBUS Function 0x05 + * \param address - Reference Address + * \param to_write - Value to be written to Coil + */ + int writeCoil(uint16_t address, const bool &to_write); + + /*! + * Write Single Register + * MODBUS Function 0x06 + * \param address - Reference Address + * \param value - Value to Be Written to Register + */ + int writeRegister(uint16_t address, const uint16_t &value); + + /*! + * Write Multiple Coils + * MODBUS Function 0x0F + * \param address - Reference Address + * \param amount - Amount of coils to write + * \param value - Values to Be Written to Coils + */ + int writeCoils(uint16_t address, uint16_t amount, const bool *value); + + /*! + * Write Multiple Registers + * MODBUS Function 0x10 + * \param address - Reference Address + * \param amount - Amount of Value to Write + * \param value - Values to Be Written to the Registers + */ + int writeRegisters(uint16_t address, uint16_t amount, const uint16_t *value); + + // Getters and Setters. + void setConnected(bool connected = false){ m_connected = connected;} + bool getConnected() const { return m_connected; } + void setMessageId( uint32_t message_id ) { m_msg_id = message_id; } + uint32_t getMessageId() const { return m_msg_id; } + void setSlaveId(int slave_id){ m_slaveId = slave_id; } + int getSlaveId() const { return m_slaveId; } + void setError(bool error, int error_number = 0, const std::string &error_message = std::string()) + { + m_error = error; + m_error_number = error_number; + m_error_message = error_message; + } + bool getError() const { return m_error; } + int getErrorNumber() const { return m_error_number; } + std::string getErrorMessage() const { return m_error_message;} + void setMaxMessageLength(unsigned int max_message_length) { m_max_message_length = max_message_length; } + unsigned int getMaxMessageLength() const { return m_max_message_length; } + +private: // Methods + /*! + * Modbus Request Builder + * \param to_send - Message buffer to be send + * \param address - Reference Address + * \param function_code - Modbus Functional Code + */ + void buildRequest(uint8_t *to_send, uint16_t address, int function_code) const; + + int modbusRead(uint16_t address, uint16_t amount, int function_code); + + /*! + * Write Request Builder and Sender + * \param address - Reference address + * \param amount - Amount of data to be written + * \param function_code - Modbus Functional Code + * \param value - Data to be written + * + * \return int - + */ + int modbusWrite(uint16_t address, uint16_t amount, int function_code, const uint16_t *value); + + /*! + * \brief modbusErrorHandle + * \param msg + * \param function_code + */ + void modbusErrorHandle(const uint8_t *msg, int function_code); + + /*! + * \brief setBadConnection + */ + void setBadConnection(); + + /*! + * \brief setBadInput + */ + void setBadInput(); + +private: // Members (Giggity!) + bool m_connected{}; + uint32_t m_msg_id{}; + int m_slaveId{}; + bool m_error{}; + int m_error_number{}; + std::string m_error_message; + unsigned int m_max_message_length; + +}; + +} // End namespace modbus +} // End namespace components +} // End namespace osdev + diff --git a/src/modbustcp.h b/src/modbustcp.h index f3b49bb..3807e31 100644 --- a/src/modbustcp.h +++ b/src/modbustcp.h @@ -6,7 +6,8 @@ */ #pragma once -#include "imodbusport.h" +#include "modbusbase.h" +#include "connectionconfig.h" #include #include @@ -21,18 +22,29 @@ using X_SOCKET = int; using SOCKADDR = struct sockaddr; using SOCKADDR_IN = struct sockaddr_in; -class ModbusTcp : public IModbusPort +class ModbusTcp : public ModbusBase { public: explicit ModbusTcp(const ConnectionConfig &con_config); virtual ~ModbusTcp() {} - // The standard device interface. - // Implementation of IModbusPort - virtual bool Open() const override; - virtual bool Close() const override; - virtual int Read(uint8_t *buffer) const override; - virtual int Write(uint8_t *buffer, size_t length) const override; + virtual bool Connect() override; + virtual bool Close() override; + + /*! + * \brief modbusSend + * \param to_send + * \param length + * \return + */ + virtual ssize_t mmodbusSend(uint8_t *to_send, size_t length) override; + + /*! + * \brief modbusReceive + * \param buffer + * \return + */ + virtual ssize_t modbusReceive(uint8_t *buffer) const override; private: uint16_t m_port {502}; -- libgit2 0.21.4