Commit cadcf24a5c8ba23c58d10e26ffdf4689eba96331

Authored by Peter M. Groen
1 parent 32017af3

Setting up working version

CMakeLists.txt
... ... @@ -7,7 +7,7 @@ aux_source_directory(3rdparty/libmodbus DIRSRCS)
7 7 include_directories(3rdparty/libmodbus include)
8 8  
9 9 set(SOURCE_FILES
10   - tests/main.cpp
  10 + src/main.cpp
11 11 src/ConnectionConfig.h
12 12 src/IModbusAdapter.h
13 13 src/ModbusAdapter.h
... ...
src/ConnectionConfig.h
1 1 #pragma once
2 2  
3 3 #include <string>
  4 +#include <unordered_map>
4 5  
5   -enum class ConnectionPort
  6 +enum class ConnectionPort : unsigned int
6 7 {
7   - CP_EXTERNAL,
  8 + CP_EXTERNAL = 0,
8 9 CP_IOBUS,
9 10 CP_TCP
10 11 };
11 12  
12   -enum class Parity
  13 +enum class Parity : unsigned int
13 14 {
14   - P_ODD,
15   - P_EVEN,
16   - P_NONE
  15 + PAR_ODD,
  16 + PAR_EVEN,
  17 + PAR_NONE
  18 +};
  19 +
  20 +enum class ConnectionType : unsigned int
  21 +{
  22 + CT_SERIAL,
  23 + CT_TCP,
  24 + CT_UNKNOWN
17 25 };
18 26  
19 27 class ConnectionConfig
20 28 {
21 29 public:
22   - ConnectionConfig( ConnectionPort port, int baud = 115200, Parity parity = Parity::P_NONE, int dataBits = 8, int stopBits = 1 )
23   - {
24   -
25   - }
  30 + ConnectionConfig( ConnectionPort port, int baud = 115200, Parity parity = Parity::PAR_NONE, int dataBits = 8, int stopBits = 1, int timeOut = -1 )
  31 + : m_port( port )
  32 + , m_baudRate( baud )
  33 + , m_parity( parity )
  34 + , m_dataBits( dataBits )
  35 + , m_stopBits( stopBits )
  36 + , m_ipaddress()
  37 + , m_portnumber( -1 )
  38 + , m_timeOut( timeOut )
  39 + {}
26 40  
27 41 ConnectionConfig( ConnectionPort port, const std::string &ip, int portnum, int timeOut = -1 )
28   - {
  42 + : m_port( port )
  43 + , m_baudRate( -1 )
  44 + , m_parity( Parity::PAR_NONE )
  45 + , m_dataBits( -1 )
  46 + , m_stopBits( -1 )
  47 + , m_ipaddress( ip )
  48 + , m_portnumber( portnum )
  49 + , m_timeOut( timeOut )
  50 + {}
29 51  
30   - }
  52 + // Getters and Setters. Implemented to avoid outside meddling on the member variables.
  53 + ConnectionType getType() const { return m_conType; }
  54 + std::string getPort() const { return m_portMap.at(m_port); }
  55 + int getBaudRate() const { return m_baudRate; }
  56 + char getParity() const { return m_parityMap.at(m_parity); }
  57 + int getDataBits() const { return m_dataBits; }
  58 + int getStopBits() const { return m_stopBits; }
  59 +
  60 + std::string getIpAddress() const { return m_ipaddress; }
  61 + int getTcpPort() const { return m_portnumber; }
  62 + int getTimeOut() const { return m_timeOut; }
31 63  
32 64 private:
33 65  
  66 + ConnectionType m_conType;
  67 +
34 68 /// Serial connections
35   - ConnectionPort m_serPort;
  69 + ConnectionPort m_port;
36 70 int m_baudRate;
37 71 Parity m_parity;
38 72 int m_dataBits;
39 73 int m_stopBits;
40 74  
41 75 /// TCP connections
  76 + std::string m_ipaddress;
  77 + int m_portnumber;
42 78  
  79 + /// Generic
  80 + int m_timeOut;
  81 + std::unordered_map<ConnectionPort, std::string> m_portMap =
  82 + {
  83 + { ConnectionPort::CP_EXTERNAL, "/dev/ttyUSB0" },
  84 + { ConnectionPort::CP_IOBUS, "/dev/ttyUSB1" }
  85 + };
  86 +
  87 + std::unordered_map<Parity, char> m_parityMap =
  88 + {
  89 + { Parity::PAR_EVEN, 'E' },
  90 + { Parity::PAR_ODD, 'O' },
  91 + { Parity::PAR_NONE, 'N' }
  92 + };
43 93 };
... ...
src/IModbusAdapter.h
... ... @@ -4,7 +4,69 @@
4 4  
5 5 #pragma once
6 6  
  7 +// std
  8 +#include <string>
  9 +#include <variant>
  10 +#include <vector>
  11 +
  12 +using modbusData = std::vector<std::variant<uint8_t, uint16_t>>;
  13 +
  14 +// Forward Declaration
  15 +class ConnectionConfig;
  16 +
  17 +/*!
  18 + * \brief The IModbusAdapter class
  19 + */
7 20 class IModbusAdapter
8 21 {
  22 +
  23 +public:
9 24 virtual ~IModbusAdapter() {}
  25 +
  26 + virtual void ModbusConnect( const ConnectionConfig &conncfg ) = 0;
  27 +
  28 + /*!
  29 + * \brief ModbusDisconnect
  30 + */
  31 + virtual void ModbusDisconnect() = 0;
  32 +
  33 + /*!
  34 + * \brief ModbusReadData
  35 + * \param slaveId
  36 + * \param startAddress
  37 + * \param noOfItems
  38 + */
  39 + virtual modbusData ModbusReadData( int slaveId, int startAddress, int noOfItems ) = 0;
  40 +
  41 + /*!
  42 + * \brief ModbusReadHoldReg
  43 + * \param slaveId
  44 + * \param startAddress
  45 + * \param noOfItems
  46 + * \return
  47 + */
  48 + virtual modbusData ModbusReadHoldReg( int slaveId, int startAddress, int noOfItems ) = 0;
  49 +
  50 + /*!
  51 + * \brief ModBusWriteData
  52 + * \param slaveId
  53 + * \param funtionCode
  54 + * \param startAddress
  55 + * \param noOfItems
  56 + * \param values
  57 + */
  58 + virtual void ModBusWriteData( int slaveId, int funtionCode, int startAddress, int noOfItems, std::vector<int>values ) = 0;
  59 +
  60 + /*!
  61 + * \brief isConnected
  62 + * \return
  63 + */
  64 + virtual bool isConnected() const = 0;
  65 +
  66 + /*!
  67 + * \brief ErrorString
  68 + * \param errnum
  69 + * \return
  70 + */
  71 + virtual std::string ErrorString( int errnum ) const = 0;
10 72 };
... ...
src/ModbusAdapter.cpp
  1 +
  2 +#include "ConnectionConfig.h"
  3 +#include "ModbusAdapter.h"
  4 +
  5 +#include <cstring> /// Added for memset functionality
  6 +
  7 +ModbusAdapter::ModbusAdapter()
  8 + : m_modbus( nullptr )
  9 +{
  10 + this->InitBuffers();
  11 +}
  12 +
  13 +ModbusAdapter::~ModbusAdapter()
  14 +{
  15 +
  16 +}
  17 +
  18 +void ModbusAdapter::ModbusConnect( const ConnectionConfig &conncfg )
  19 +{
  20 + if( m_connected )
  21 + {
  22 + this->ModbusDisconnect();
  23 + }
  24 +
  25 + this->m_connType = conncfg.getType();
  26 +
  27 + switch( m_connType )
  28 + {
  29 + case ConnectionType::CT_SERIAL:
  30 + this->ModbusConnectRTU( conncfg.getPort(), conncfg.getBaudRate(), conncfg.getParity(), conncfg.getDataBits(), conncfg.getStopBits(), conncfg.getTimeOut(), MODBUS_RTU_RTS_NONE );
  31 + break;
  32 +
  33 + case ConnectionType::CT_TCP:
  34 + break;
  35 +
  36 + default:
  37 + // throw a sensible message or return an errorcode.
  38 + break;
  39 + }
  40 +}
  41 +
  42 +void ModbusAdapter::ModbusDisconnect()
  43 +{
  44 + if( m_modbus != nullptr )
  45 + {
  46 + if( m_connected )
  47 + {
  48 + modbus_close( m_modbus );
  49 + modbus_free( m_modbus );
  50 + }
  51 + // Clean up after ourselves.
  52 + m_modbus = nullptr;
  53 + }
  54 + m_connected = false;
  55 +}
  56 +
  57 +modbusData ModbusAdapter::ModbusReadData( int slaveId, int functionCode, int startAddress, int noOfItems )
  58 +{
  59 + if( m_modbus == nullptr )
  60 + {
  61 + // No context
  62 + return modbusData();
  63 + }
  64 +
  65 +
  66 +
  67 + int resultValue = -1;
  68 + bool is16Bit = false;
  69 +
  70 + modbus_set_slave( m_modbus, slaveId );
  71 +
  72 + // Request data from modbus.
  73 + switch( functionCode )
  74 + {
  75 + case MODBUS_FC_READ_COILS:
  76 + resultValue = modbus_read_bits( m_modbus, startAddress, noOfItems, m_dest );
  77 + break;
  78 + case MODBUS_FC_READ_DISCRETE_INPUTS:
  79 + resultValue = modbus_read_input_bits( m_modbus, startAddress, noOfItems, m_dest );
  80 + break;
  81 + case MODBUS_FC_READ_HOLDING_REGISTERS:
  82 + resultValue = modbus_read_registers( m_modbus, startAddress, noOfItems, m_dest16 );
  83 + break;
  84 + case MODBUS_FC_READ_INPUT_REGISTERS:
  85 + resultValue = modbus_read_input_registers( m_modbus, startAddress, noOfItems, m_dest16 );
  86 + break;
  87 +
  88 + default:
  89 + break;
  90 +
  91 + }
  92 +
  93 + // Read the databuffers
  94 + if( resultValue != noOfItems )
  95 + {
  96 + return modbusData();
  97 + }
  98 +
  99 + modbusData resultData;
  100 + for( int index = 0; index < noOfItems; ++index )
  101 + {
  102 + resultData.push_back( (is16Bit ? static_cast<uint16_t>(m_dest16[index]) : static_cast<uint16_t>(m_dest[index])) );
  103 + }
  104 +
  105 + modbus_flush( m_modbus ); // flush data
  106 + this->ClearBuffers();
  107 +
  108 + return resultData;
  109 +}
  110 +
  111 +modbusData ModbusAdapter::ModbusReadHoldReg( int slaveId, int startAddress, int noOfItems )
  112 +{
  113 + if( m_modbus == nullptr )
  114 + {
  115 + return modbusData();
  116 + }
  117 +
  118 + int resultValue = -1; /// Return value from read functions
  119 +
  120 + // -- Start Critical Section --- ?? //
  121 +
  122 + modbus_set_slave( m_modbus, slaveId );
  123 + /// Request data from modbus
  124 + resultValue = modbus_read_registers( m_modbus, startAddress, noOfItems, m_dest16 );
  125 +
  126 + /// Read the databuffers
  127 + if( resultValue != noOfItems )
  128 + {
  129 + return modbusData();
  130 + }
  131 +
  132 + modbusData resultData;
  133 + for( int index = 0; index < noOfItems; ++index )
  134 + {
  135 + resultData.push_back( static_cast<uint16_t>(m_dest16[index]) );
  136 + }
  137 +
  138 + modbus_flush( m_modbus );
  139 + this->ClearBuffers();
  140 +
  141 + // -- End Critical Section --- ?? //
  142 +
  143 + return resultData;
  144 +}
  145 +
  146 +void ModbusAdapter::ModBusWriteData( int slaveId, int functionCode, int startAddress, int noOfItems, std::vector<int>values )
  147 +{
  148 + if( m_modbus == nullptr )
  149 + {
  150 + return;
  151 + }
  152 +
  153 + // ------------------------------------------------
  154 + // Create 8 bits databuffer
  155 + auto * data8 = new uint8_t[noOfItems];
  156 + for( int index = 0; index < noOfItems; ++index )
  157 + {
  158 + data8[index] = values[index];
  159 + }
  160 +
  161 + // Create same buffer for 16 bits data
  162 + auto * data16 = new uint8_t[noOfItems];
  163 + for( int index = 0; index < noOfItems; ++index )
  164 + {
  165 + data16[index] = values[index];
  166 + }
  167 + // ------------------------------------------------
  168 +
  169 + int resultValue = -1;
  170 +
  171 + modbus_set_slave( m_modbus, slaveId );
  172 +
  173 + // Request data from modbus
  174 + switch( functionCode )
  175 + {
  176 + case MODBUS_FC_WRITE_SINGLE_COIL:
  177 + resultValue = modbus_write_bit( m_modbus, startAddress, values[0] );
  178 + noOfItems = 1;
  179 + break;
  180 + case MODBUS_FC_WRITE_SINGLE_REGISTER:
  181 + resultValue = modbus_write_register( m_modbus, startAddress, values[0] );
  182 + noOfItems = 1;
  183 + break;
  184 + case MODBUS_FC_WRITE_MULTIPLE_COILS:
  185 +
  186 + resultValue = modbus_write_bits( m_modbus, startAddress, noOfItems, data8 );
  187 + break;
  188 + case MODBUS_FC_WRITE_MULTIPLE_REGISTERS:
  189 + resultValue = modbus_write_bits( m_modbus, startAddress, noOfItems, data16 );
  190 + break;
  191 + }
  192 +
  193 + delete[] data8;
  194 + delete[] data16;
  195 + modbus_flush(m_modbus); // Flush data.
  196 +
  197 + if( resultValue != noOfItems )
  198 + {
  199 + // Create a log line that writing the register failed. Maybe increment ErrorCounter(s)
  200 + }
  201 +}
  202 +
  203 +bool ModbusAdapter::isConnected() const
  204 +{
  205 + return m_connected;
  206 +}
  207 +
  208 +std::string ModbusAdapter::ErrorString( int errnum )
  209 +{
  210 + switch(errnum)
  211 + {
  212 + case EINVAL:
  213 + return "Protocol context is NULL";
  214 + break;
  215 + case ETIMEDOUT:
  216 + return "Timeout";
  217 + break;
  218 + case ECONNRESET:
  219 + return "Connection reset";
  220 + break;
  221 + case ECONNREFUSED:
  222 + return "Connection refused";
  223 + break;
  224 + case EPIPE:
  225 + return "Socket error";
  226 + break;
  227 + default:
  228 + return modbus_strerror(errno);
  229 + }
  230 +}
  231 +
  232 +/* ============= PRIVATE METHODS ============= */
  233 +void ModbusAdapter::ModbusConnectRTU( const std::string &serialport, int baud, char parity, int dataBits, int stopBits, int RTS, int timeOut )
  234 +{
  235 + m_modbus = modbus_new_rtu( serialport.c_str(), baud, parity, dataBits, stopBits, RTS );
  236 +
  237 +#ifdef LIB_MODBUS_DEBUG_OUTPUT
  238 + // Do sensible logging through PRIVA_LOG
  239 + m_modbus_set_debug( m_modbus, 1 );
  240 +#endif
  241 + if( m_modbus == nullptr )
  242 + {
  243 + // We can stop here. Nothing to be done as we don't have a valid context
  244 + // Log to PRIVA_LOG
  245 + return;
  246 + }
  247 + else if( m_modbus && modbus_connect( m_modbus ) == -1 )
  248 + {
  249 + // We could not connect to the selected serial port.
  250 + // We can stop here. Nothing to be done as we don't have a valid connection
  251 + modbus_free( m_modbus );
  252 + m_connected = false;
  253 + return;
  254 + }
  255 +
  256 + // Set recovery mode
  257 + modbus_set_error_recovery( m_modbus, MODBUS_ERROR_RECOVERY_PROTOCOL );
  258 +
  259 + // Set the response timeout
  260 + modbus_set_response_timeout( m_modbus, timeOut, 0 );
  261 +
  262 + m_connected = true;
  263 +}
  264 +
  265 +void ModbusAdapter::ModbusConnectTCP( const std::string &ip, int port, int timeOut )
  266 +{
  267 + if( ip.empty() )
  268 + {
  269 + // Nothing to be done. Set an Logline to PRIVA_LOG and exit.
  270 + return;
  271 + }
  272 +
  273 + m_modbus = modbus_new_tcp( ip.c_str(), port );
  274 +
  275 +#ifdef LIB_MODBUS_DEBUG_OUTPUT
  276 + // Do sensible logging through PRIVA_LOG
  277 + m_modbus_set_debug( m_modbus, 1 );
  278 +#endif
  279 +
  280 + if( m_modbus == nullptr )
  281 + {
  282 + // We can stop here. Nothing to be done as we don't have a valid context
  283 + // Log to PRIVA_LOG
  284 + return;
  285 + }
  286 + else if( m_modbus && modbus_connect( m_modbus ) == -1 )
  287 + {
  288 + // We could not connect to the selected serial port.
  289 + // We can stop here. Nothing to be done as we don't have a valid connection
  290 + modbus_free( m_modbus );
  291 + m_connected = false;
  292 + return;
  293 + }
  294 +
  295 + // Set recovery mode
  296 + modbus_set_error_recovery( m_modbus, MODBUS_ERROR_RECOVERY_PROTOCOL );
  297 +
  298 + // Set the response timeout
  299 + modbus_set_response_timeout( m_modbus, timeOut, 0 );
  300 +
  301 + m_connected = true;
  302 +}
  303 +
  304 +void ModbusAdapter::InitBuffers()
  305 +{
  306 + // Setup memory for Data.
  307 + m_dest = (uint8_t *)malloc(2000 + sizeof(uint8_t));
  308 + m_dest16 = (uint16_t *)malloc(125 * sizeof(uint16_t));
  309 +
  310 + this->ClearBuffers();
  311 +}
  312 +
  313 +void ModbusAdapter::ClearBuffers()
  314 +{
  315 + memset(m_dest, 0, 2000 * sizeof(uint8_t));
  316 + memset(m_dest16, 0, 125 * sizeof(uint16_t));
  317 +}
... ...
src/ModbusAdapter.h
... ... @@ -6,17 +6,94 @@
6 6  
7 7 #include "ConnectionConfig.h"
8 8 #include "IModbusAdapter.h"
  9 +#include "modbus.h"
9 10  
10   -/// \brief This class represents a single modbus context. Each context will
11   -/// result in an instance of this class. It is not intended to be
12   -/// created directly but through a factory. The factory will create
13   -/// the object and return the pointer to its interface.
  11 +// std
  12 +#include <memory>
  13 +#include <variant>
  14 +#include <vector>
14 15  
  16 +/// @brief The ModbusAdapter class represents a single modbus context. Each context will
  17 +/// result in an instance of this class. It is not intended to be
  18 +/// created directly but through a factory. The factory will create
  19 +/// the object and return the pointer to its interface.
15 20 class ModbusAdapter : public IModbusAdapter
16 21 {
17 22 public:
  23 + /*!
  24 + * \brief ModbusAdapter
  25 + */
18 26 ModbusAdapter();
  27 +
  28 + /*!
  29 + * \brief ~ModbusAdapter
  30 + */
19 31 virtual ~ModbusAdapter();
20 32  
21   - void ModbusConnectRTU(const ConnectionConfig &conncfg);
  33 + /*!
  34 + * \brief ModbusConnect
  35 + * \param conncfg
  36 + */
  37 + void ModbusConnect( const ConnectionConfig &conncfg );
  38 +
  39 + /*!
  40 + * \brief ModbusDisconnect
  41 + */
  42 + void ModbusDisconnect();
  43 +
  44 + /*!
  45 + * \brief ModbusReadData
  46 + * \param slaveId
  47 + * \param startAddress
  48 + * \param noOfItems
  49 + */
  50 + modbusData ModbusReadData( int slaveId, int functionCode, int startAddress, int noOfItems );
  51 +
  52 + /*!
  53 + * \brief ModbusReadHoldReg
  54 + * \param slaveId
  55 + * \param startAddress
  56 + * \param noOfItems
  57 + * \return
  58 + */
  59 + modbusData ModbusReadHoldReg( int slaveId, int startAddress, int noOfItems );
  60 +
  61 + /*!
  62 + * \brief ModBusWriteData
  63 + * \param slaveId
  64 + * \param funtionCode
  65 + * \param startAddress
  66 + * \param noOfItems
  67 + * \param values
  68 + */
  69 + void ModBusWriteData( int slaveId, int functionCode, int startAddress, int noOfItems, std::vector<int>values );
  70 +
  71 + /*!
  72 + * \brief isConnected
  73 + * \return
  74 + */
  75 + bool isConnected() const;
  76 +
  77 + /*!
  78 + * \brief ErrorString
  79 + * \param errnum
  80 + * \return
  81 + */
  82 + std::string ErrorString( int errnum );
  83 +
  84 +private: // Methods
  85 + void ModbusConnectRTU( const std::string &serialport, int baud, char parity, int dataBits, int stopBits, int RTS, int timeOut );
  86 + void ModbusConnectTCP( const std::string &ip, int port, int timeOut = -1 );
  87 +
  88 + void InitBuffers();
  89 + void ClearBuffers();
  90 +
  91 +private: // Members
  92 + ConnectionType m_connType { ConnectionType::CT_UNKNOWN }; ///> The type of connection this instance provides. Needed for administration
  93 + bool m_connected { false }; ///> Shows if the connection is still active.
  94 + modbus_t *m_modbus; ///> The actual low-level modbus instance as a raw-pointer. ( unique_pointer gives an error on this struct )
  95 +
  96 + // Return value Buffers ( Room for improvement )
  97 + uint8_t *m_dest;
  98 + uint16_t *m_dest16;
22 99 };
... ...
tests/main.cpp renamed to src/main.cpp