modbusbase.h 6.95 KB
/*
 *  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 <cstring>
#include <string>
#include <stdint.h>

// 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 <cstdio>
#define LOG(fmt, ...) printf("[ modbus-cpp ]" fmt, ##__VA_ARGS__)
#else
#define LOG(...) (void)0
#endif

#define X_ISVALIDSOCKET(s) ((s) >= 0)
#define X_CLOSE_SOCKET(s) close(s)
#define X_ISCONNECTSUCCEED(s) ((s) >= 0)

// 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 int modbusSend(uint8_t *to_send, size_t length) = 0;

    /*!
     * \brief modbusReceive
     * \param buffer
     * \return
     */
    virtual int 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