From 467852705fb7f1e99346076fa7820b0a990a6385 Mon Sep 17 00:00:00 2001 From: Peter M. Groen Date: Fri, 3 Jun 2022 18:27:32 +0200 Subject: [PATCH] Setting up working version --- 3rdparty/libmodbus/modbus-tcp.c | 23 +---------------------- 3rdparty/libmodbus/modbus.c | 21 +++++++++++---------- CMakeLists.txt | 2 ++ src/ConnectionConfig.h | 52 +++++++++++++++++++++++++++++++++++++++++----------- src/IModbusAdapter.h | 4 ++-- src/ModbusAdapter.cpp | 2 +- src/ModbusAdapter.h | 16 ++++++++-------- src/ModbusConnections.cpp | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ModbusConnections.h | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 351 insertions(+), 54 deletions(-) create mode 100644 src/ModbusConnections.cpp create mode 100644 src/ModbusConnections.h diff --git a/3rdparty/libmodbus/modbus-tcp.c b/3rdparty/libmodbus/modbus-tcp.c index 15a8f00..230e3a3 100644 --- a/3rdparty/libmodbus/modbus-tcp.c +++ b/3rdparty/libmodbus/modbus-tcp.c @@ -1,32 +1,11 @@ -/* - * Copyright © 2001-2013 Stéphane Raimbault - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - #include #include #include #include -#ifndef _MSC_VER #include -#endif #include #include -#if defined(_WIN32) -# define OS_WIN32 -/* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later. - * minwg32 headers check WINVER before allowing the use of these */ -# ifndef WINVER -# define WINVER 0x0501 -# endif -/* Already set in modbus-tcp.h but it seems order matters in VS2005 */ -# include -# include -# define SHUT_RDWR 2 -# define close closesocket -#else # include # include @@ -40,7 +19,7 @@ # include # include # include -#endif + #if !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 diff --git a/3rdparty/libmodbus/modbus.c b/3rdparty/libmodbus/modbus.c index 4017ada..e7cc29e 100644 --- a/3rdparty/libmodbus/modbus.c +++ b/3rdparty/libmodbus/modbus.c @@ -1129,19 +1129,19 @@ int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest) } /* Reads the data from a remove device and put that data into an array */ -static int read_registers(modbus_t *ctx, int function, int addr, int nb, - uint16_t *dest) +static int read_registers(modbus_t *ctx, int function, int addr, int nb, uint16_t *dest) { int rc; int req_length; uint8_t req[_MIN_REQ_LENGTH]; uint8_t rsp[MAX_MESSAGE_LENGTH]; - if (nb > MODBUS_MAX_READ_REGISTERS) { - if (ctx->debug) { + if (nb > MODBUS_MAX_READ_REGISTERS) + { + if (ctx->debug) + { fprintf(stderr, - "ERROR Too many registers requested (%d > %d)\n", - nb, MODBUS_MAX_READ_REGISTERS); + "ERROR Too many registers requested (%d > %d)\n", nb, MODBUS_MAX_READ_REGISTERS); } errno = EMBMDATA; return -1; @@ -1150,7 +1150,8 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb, req_length = ctx->backend->build_request_basis(ctx, function, addr, nb, req); rc = send_msg(ctx, req, req_length); - if (rc > 0) { + if (rc > 0) + { int offset; int i; @@ -1164,10 +1165,10 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb, offset = ctx->backend->header_length; - for (i = 0; i < rc; i++) { + for (i = 0; i < rc; i++) + { /* shift reg hi_byte to temp OR with lo_byte */ - dest[i] = (rsp[offset + 2 + (i << 1)] << 8) | - rsp[offset + 3 + (i << 1)]; + dest[i] = (rsp[offset + 2 + (i << 1)] << 8) | rsp[offset + 3 + (i << 1)]; } } diff --git a/CMakeLists.txt b/CMakeLists.txt index 71970cd..440621b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,8 @@ set(SOURCE_FILES src/IModbusAdapter.h src/ModbusAdapter.h src/ModbusAdapter.cpp + src/ModbusConnections.h + src/ModbusConnections.cpp ) add_executable(main diff --git a/src/ConnectionConfig.h b/src/ConnectionConfig.h index c9249e2..87d1413 100644 --- a/src/ConnectionConfig.h +++ b/src/ConnectionConfig.h @@ -3,6 +3,9 @@ #include #include +/*! + * \brief The ConnectionPort enum + */ enum class ConnectionPort : unsigned int { CP_EXTERNAL = 0, @@ -10,6 +13,9 @@ enum class ConnectionPort : unsigned int CP_TCP }; +/*! + * \brief The Parity enum + */ enum class Parity : unsigned int { PAR_ODD, @@ -17,6 +23,9 @@ enum class Parity : unsigned int PAR_NONE }; +/*! + * \brief The ConnectionType enum + */ enum class ConnectionType : unsigned int { CT_SERIAL, @@ -27,6 +36,15 @@ enum class ConnectionType : unsigned int class ConnectionConfig { public: + /*! + * \brief ConnectionConfig + * \param port + * \param baud + * \param parity + * \param dataBits + * \param stopBits + * \param timeOut + */ ConnectionConfig( ConnectionPort port, int baud = 115200, Parity parity = Parity::PAR_NONE, int dataBits = 8, int stopBits = 1, int timeOut = -1 ) : m_port( port ) , m_baudRate( baud ) @@ -36,8 +54,16 @@ public: , m_ipaddress() , m_portnumber( -1 ) , m_timeOut( timeOut ) + , m_conType( ConnectionType::CT_SERIAL ) {} + /*! + * \brief ConnectionConfig + * \param port + * \param ip + * \param portnum + * \param timeOut + */ ConnectionConfig( ConnectionPort port, const std::string &ip, int portnum, int timeOut = -1 ) : m_port( port ) , m_baudRate( -1 ) @@ -47,23 +73,24 @@ public: , m_ipaddress( ip ) , m_portnumber( portnum ) , m_timeOut( timeOut ) + , m_conType( ConnectionType::CT_TCP ) {} // Getters and Setters. Implemented to avoid outside meddling on the member variables. - ConnectionType getType() const { return m_conType; } - std::string getPort() const { return m_portMap.at(m_port); } - int getBaudRate() const { return m_baudRate; } - char getParity() const { return m_parityMap.at(m_parity); } - int getDataBits() const { return m_dataBits; } - int getStopBits() const { return m_stopBits; } + std::string getPort() const { return m_portMap.at(m_port); } ///< + ConnectionPort getPortEnum() const { return m_port; } ///< + int getBaudRate() const { return m_baudRate; } ///< + char getParity() const { return m_parityMap.at(m_parity); } ///< + int getDataBits() const { return m_dataBits; } ///< + int getStopBits() const { return m_stopBits; } ///< - std::string getIpAddress() const { return m_ipaddress; } - int getTcpPort() const { return m_portnumber; } - int getTimeOut() const { return m_timeOut; } + std::string getIpAddress() const { return m_ipaddress; } ///< + int getTcpPort() const { return m_portnumber; } ///< -private: + int getTimeOut() const { return m_timeOut; } ///< + ConnectionType getType() const { return m_conType; } ///< - ConnectionType m_conType; +private: /// Serial connections ConnectionPort m_port; @@ -78,6 +105,9 @@ private: /// Generic int m_timeOut; + ConnectionType m_conType; + + // Change accordingly to the devicenames on your platform. std::unordered_map m_portMap = { { ConnectionPort::CP_EXTERNAL, "/dev/ttyUSB0" }, diff --git a/src/IModbusAdapter.h b/src/IModbusAdapter.h index c565c22..18d7e11 100644 --- a/src/IModbusAdapter.h +++ b/src/IModbusAdapter.h @@ -36,7 +36,7 @@ public: * \param startAddress * \param noOfItems */ - virtual modbusData ModbusReadData( int slaveId, int startAddress, int noOfItems ) = 0; + virtual modbusData ModbusReadData( int slaveId, int functionCode, int startAddress, int noOfItems ) = 0; /*! * \brief ModbusReadHoldReg @@ -55,7 +55,7 @@ public: * \param noOfItems * \param values */ - virtual void ModBusWriteData( int slaveId, int funtionCode, int startAddress, int noOfItems, std::vectorvalues ) = 0; + virtual void ModBusWriteData( int slaveId, int functionCode, int startAddress, int noOfItems, std::vectorvalues ) = 0; /*! * \brief isConnected diff --git a/src/ModbusAdapter.cpp b/src/ModbusAdapter.cpp index f3e7723..a492586 100644 --- a/src/ModbusAdapter.cpp +++ b/src/ModbusAdapter.cpp @@ -205,7 +205,7 @@ bool ModbusAdapter::isConnected() const return m_connected; } -std::string ModbusAdapter::ErrorString( int errnum ) +std::string ModbusAdapter::ErrorString( int errnum ) const { switch(errnum) { diff --git a/src/ModbusAdapter.h b/src/ModbusAdapter.h index 708c2b6..9f3d4af 100644 --- a/src/ModbusAdapter.h +++ b/src/ModbusAdapter.h @@ -23,7 +23,7 @@ public: /*! * \brief ModbusAdapter */ - ModbusAdapter(); + explicit ModbusAdapter(); /*! * \brief ~ModbusAdapter @@ -34,12 +34,12 @@ public: * \brief ModbusConnect * \param conncfg */ - void ModbusConnect( const ConnectionConfig &conncfg ); + void ModbusConnect( const ConnectionConfig &conncfg ) override; /*! * \brief ModbusDisconnect */ - void ModbusDisconnect(); + void ModbusDisconnect() override; /*! * \brief ModbusReadData @@ -47,7 +47,7 @@ public: * \param startAddress * \param noOfItems */ - modbusData ModbusReadData( int slaveId, int functionCode, int startAddress, int noOfItems ); + modbusData ModbusReadData( int slaveId, int functionCode, int startAddress, int noOfItems ) override; /*! * \brief ModbusReadHoldReg @@ -56,7 +56,7 @@ public: * \param noOfItems * \return */ - modbusData ModbusReadHoldReg( int slaveId, int startAddress, int noOfItems ); + modbusData ModbusReadHoldReg( int slaveId, int startAddress, int noOfItems ) override; /*! * \brief ModBusWriteData @@ -66,20 +66,20 @@ public: * \param noOfItems * \param values */ - void ModBusWriteData( int slaveId, int functionCode, int startAddress, int noOfItems, std::vectorvalues ); + void ModBusWriteData( int slaveId, int functionCode, int startAddress, int noOfItems, std::vectorvalues ) override; /*! * \brief isConnected * \return */ - bool isConnected() const; + bool isConnected() const override; /*! * \brief ErrorString * \param errnum * \return */ - std::string ErrorString( int errnum ); + std::string ErrorString( int errnum ) const override; private: // Methods void ModbusConnectRTU( const std::string &serialport, int baud, char parity, int dataBits, int stopBits, int RTS, int timeOut ); diff --git a/src/ModbusConnections.cpp b/src/ModbusConnections.cpp new file mode 100644 index 0000000..3e438a6 --- /dev/null +++ b/src/ModbusConnections.cpp @@ -0,0 +1,130 @@ +#include "ModbusConnections.h" + +ModbusConnections::ModbusConnections() +{ + +} + +ModbusConnections::~ModbusConnections() +{ + if( m_mapSerial.size() > 0 ) + { + // Iterate, remove and destroy ( Searching, Seek & Destroy ) + } + + if( m_mapTcp.size() > 0 ) + { + // Iterate, remove and destroy ( Searching, Seek & Destroy ) + } +} + +bool ModbusConnections::CreateConnection( const ConnectionConfig &config ) +{ + std::shared_ptr ptrNewAdapter = std::make_shared(); + if( ptrNewAdapter == nullptr ) + { + // Log a message and bail out + return false; + } + + // It looks like the pointer is valid. Time to connect. + ptrNewAdapter->ModbusConnect( config ); + if( !ptrNewAdapter->isConnected() ) + { + // Unsuccessful. Delete the object and return false + ptrNewAdapter.reset(); + return false; + } + + std::shared_ptr ptr = connectionExist( config.getPortEnum(), createEndPoint( config ) ); + if( ptr != nullptr ) + { + if( !DeleteConnection( config.getPortEnum(), createEndPoint( config ) ) ) + { + // Something went wrong here.. Our administration is "wonky" so report false and bail. + // New connection is not created. + ptrNewAdapter.reset(); + return false; + } + } + + if( config.getType() == ConnectionType::CT_TCP ) + { + m_mapTcp.insert( { createEndPoint( config ), ptrNewAdapter } ); + } + else if( config.getType() == ConnectionType::CT_SERIAL ) + { + m_mapSerial.insert( { config.getPortEnum(), ptrNewAdapter } ); + } + else + { + // No idea what the type is but not something we recognize. + ptrNewAdapter.reset(); + return false; + } + return true; +} + +bool ModbusConnections::DeleteConnection( const ConnectionPort portName, const std::string &endpoint ) +{ + +} + +int ModbusConnections::ConnectionCount() +{ + return ( m_mapSerial.size() + m_mapTcp.size() ); +} + +std::shared_ptr ModbusConnections::getConnection( const ConnectionPort portName, const std::string &endpoint ) +{ + return this->connectionExist( portName, endpoint ); +} + +std::shared_ptr ModbusConnections::connectionExist( const ConnectionPort portName, const std::string &endpoint ) +{ + if( portName == ConnectionPort::CP_TCP ) + { + auto search = m_mapTcp.find( endpoint ); + if( search != m_mapTcp.end() ) + { + return search->second; + } + else + { + return nullptr; + } + } + else + { + auto search = m_mapSerial.find( portName ); + if( search != m_mapSerial.end() ) + { + return search->second; + } + else + { + return nullptr; + } + } +} + +std::string ModbusConnections::createEndPoint( const std::string &ipAddress, int portNumber ) +{ + if( portNumber > 0 && !ipAddress.empty() ) + { + return std::string( "tcp://" + ipAddress + ":" + std::to_string( portNumber ) ); + } + + return std::string(); +} + +std::string ModbusConnections::createEndPoint( const ConnectionConfig &config ) +{ + if( config.getType() != ConnectionType::CT_TCP ) + { + // Early opt-out + return std::string(); + } + + return createEndPoint( config.getIpAddress(), config.getTcpPort() ); +} diff --git a/src/ModbusConnections.h b/src/ModbusConnections.h new file mode 100644 index 0000000..7a402f8 --- /dev/null +++ b/src/ModbusConnections.h @@ -0,0 +1,94 @@ +/**************************************************************************** + * Copyright (c) 2022 Priva B.V. + ****************************************************************************/ +#pragma once + +// Flexblox +#include "IModbusAdapter.h" +#include "ModbusAdapter.h" + +// std +#include +#include +#include + +class ModbusConnections +{ +public: + /*! + * \brief ModbusConnections + */ + explicit ModbusConnections(); + + /*! + * \brief ~ModbusConnections + */ + virtual ~ModbusConnections(); + + /*! + * \brief CreateConnection + * \param config + * \return + */ + bool CreateConnection( const ConnectionConfig &config ); + + /*! + * \brief DeleteConnection + * \param portName + * \param endpoint + * \return + */ + bool DeleteConnection( const ConnectionPort portName, const std::string &endpoint = std::string() ); + + /*! + * \brief ConnectionCount + * \return + */ + int ConnectionCount(); + + /*! + * \brief getConnection + * \param portName + * \param endpoint + * \return + */ + std::shared_ptr getConnection( const ConnectionPort portName, const std::string &endpoint = std::string() ); + + // Convenient functions + /*! + * \brief getConnection + * \param portName + * \param ipAddress + * \param tcpPortNumber + * \return + */ + std::shared_ptr getConnection( const ConnectionPort portName, const std::string &ipAddress, int tcpPortNumber ); + +private: + /*! + * \brief connectionExist + * \param portName + * \param endpoint + * \return + */ + std::shared_ptr connectionExist( const ConnectionPort portName, const std::string &endpoint = std::string() ); + + /*! + * \brief createEndPoint + * \param ipAddress + * \param portNumber + * \return + */ + std::string createEndPoint( const std::string &ipAddress, int portNumber ); + + /*! + * \brief createEndPoint + * \param config + * \return + */ + std::string createEndPoint( const ConnectionConfig &config ); + +private: + std::unordered_map> m_mapSerial; ///< Unordered map holding the Modbus connections By PortName + std::unordered_map> m_mapTcp; ///< Unordered map holding the Modbus connections By tcp://endpoint:port +}; diff --git a/src/main.cpp b/src/main.cpp index da175c2..d85ae0f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,67 @@ #include +#include "ModbusAdapter.h" + int main( int argc, char* argv[] ) { + ModbusAdapter oModbus; + ConnectionConfig oConfig( ConnectionPort::CP_EXTERNAL, 9600, Parity::PAR_NONE, 8, 1, 1 ); + + std::cout << "========================= [START] Connection test ================================" << std::endl; + oModbus.ModbusConnect( oConfig ); + if( oModbus.isConnected() ) + { + std::cout << "Successful connected to : " << oConfig.getPort() << std::endl; + } + else + { + std::cout << "There was a problem connecting to : " << oConfig.getPort() << std::endl; + } + + oModbus.ModbusDisconnect(); + if( !oModbus.isConnected() ) + { + std::cout << "Successful disconnected from : " << oConfig.getPort() << std::endl; + } + else + { + std::cout << "There was a problem disconnecting from : " << oConfig.getPort() << std::endl; + } + std::cout << "========================= [END] Connection test ===============================" << std::endl; + + std::cout << "========================= [START] Reading test ================================" << std::endl; + std::cout << "== Reading the Hold Registers ==" << std::endl; + oModbus.ModbusConnect( oConfig ); + if( oModbus.isConnected() ) + { + std::cout << "Successful connected to : " << oConfig.getPort() << std::endl; + modbusData returnValues = oModbus.ModbusReadHoldReg( 1, MODBUS_FC_READ_HOLDING_REGISTERS, 2 ); + if( returnValues.size() == 2 ) + { + std::cout << "2 items returned from MODBUS_FC_READ_HOLDING_REGISTERS " << std::endl; + } + else + { + std::cout << "There was an error reading the Hold Registers " << std::endl; + std::cout << "Number of items returned : " << returnValues.size() << std::endl; + } + std::cout << "== Reading the Temperature ==" << std::endl; + returnValues = oModbus.ModbusReadData( 0x01, MODBUS_FC_READ_INPUT_REGISTERS, 0x00, 0x02 ); + if( returnValues.size() == 0 ) + { + std::cout << "No values returned " << std::endl; + } + else + { + std::cout << "Number of items returned : " << returnValues.size() << std::endl; + } + } + else + { + std::cout << "There was a problem connecting to : " << oConfig.getPort() << std::endl; + return -1; + } + oModbus.ModbusDisconnect(); + std::cout << "=========================== [END] Reading test ================================" << std::endl; } -- libgit2 0.21.4