From c76baa99f9e6566bc0fb92672454c2d013d790f5 Mon Sep 17 00:00:00 2001 From: Ole Reinhardt Date: Mon, 2 Aug 2021 17:05:03 +0200 Subject: [PATCH 01/20] Include stdexcept header to fix compiler errors --- include/modbuspp/data.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/modbuspp/data.h b/include/modbuspp/data.h index 49aca7c..2ef97a7 100644 --- a/include/modbuspp/data.h +++ b/include/modbuspp/data.h @@ -16,6 +16,7 @@ */ #pragma once +#include #include // printf #include // memcpy ... #include From 8db93a5d78f95e1641c89db5f54cf85b671c0e93 Mon Sep 17 00:00:00 2001 From: Tamara Schmitz Date: Thu, 12 Dec 2024 17:23:21 +0100 Subject: [PATCH 02/20] rtu layer: fix parsing of settings string Throw exceptions on error instead of silently changing values. Check settings string for given stop bit value. Up until now, the stop bits value was hard coded to be 2 if the parity mode was set to none or 1 if the parity mode was set to even or odd. So a setting of "115200E1" would result in a stop bit value of 1. But "115200E2" would result also in a stop bit value of 1. And "115200N1" would result in a stop bit value of 2. This is not a restriction of libmodbus. The library supports all these modes just fine. In fact they are using 115200N1 in the documentation for modbus_new_rtu(). Rather this is a bug in the string parsing that libmodbuspp introduces in its constructor. libmodbus has no string parsing in modbus_new_rtu() and takes in the arguments as parameters directly. Fixes: b0f3dc4be5e82729825158ca651657afe1f6380b --- include/modbuspp/rtulayer.h | 11 +++++------ src/rtulayer.cpp | 32 ++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/include/modbuspp/rtulayer.h b/include/modbuspp/rtulayer.h index eac2b5f..6634911 100644 --- a/include/modbuspp/rtulayer.h +++ b/include/modbuspp/rtulayer.h @@ -169,23 +169,22 @@ namespace Modbus { /** * @brief Extracts the baudrate from a settings string. - * @return the baudrate found. if no value is found, returns the default - * value, ie 19200. + * @return the baudrate found. If no valid value is found, an exception is thrown. */ static int baud (const std::string & settings); /** * @brief Extracts the parity from a settings string. - * @return the parity found. if no value is found, returns the default - * value, ie E for Even parity. + * @return the parity found. If no valid value is found, an exception is thrown. */ static char parity (const std::string & settings); /** * @brief Return the stop bits from a settings string. * - * @return the number returned is determined based on the parity found. - * If the parity is None, this function returns 2, otherwise returns 1. + * @return the number of stop bits. + * It is parsed from the last character of the settings string. + * If the last character is neither '1' or '2' an exception is thrown. */ static int stop (const std::string & settings); diff --git a/src/rtulayer.cpp b/src/rtulayer.cpp index 6c265c5..dad21ac 100644 --- a/src/rtulayer.cpp +++ b/src/rtulayer.cpp @@ -234,40 +234,48 @@ namespace Modbus { // --------------------------------------------------------------------------- // static int RtuLayer::baud (const std::string & settings) { - int b; try { - b = std::stoi (settings); + return std::stoi (settings); } catch (...) { - b = 19200; + throw std::invalid_argument ("RtuLayer settings\"" + settings + "\" has an invalid baud rate setting."); } - return b; } // --------------------------------------------------------------------------- // static char RtuLayer::parity (const std::string & settings) { - char p = 'N'; size_t s = settings.length(); if (s >= 2) { char c = settings[s - 2]; - if ( (c == 'E') || (c == 'O')) { - return c; + switch (c) { + case 'N': + case 'E': + case 'O': + return c; } } - return p; + + throw std::invalid_argument ("RtuLayer settings\"" + settings + "\" has an invalid parity setting."); } // --------------------------------------------------------------------------- // static int RtuLayer::stop (const std::string & settings) { + size_t s = settings.length(); - if (parity (settings) == 'N') { - - return 2; + if (s >= 3) { + char c = settings[s - 1]; + switch (c) { + case '1': + return 1; + case '2': + return 2; + } } - return 1; + + throw std::invalid_argument ("RtuLayer settings\"" + settings + "\" has an invalid stop bit setting."); } // --------------------------------------------------------------------------- From da9f2f02e442dea6b7ad881371c1fecc9efa38d4 Mon Sep 17 00:00:00 2001 From: epsilonrt Date: Sun, 5 Apr 2020 22:11:45 +0200 Subject: [PATCH 03/20] Add ReleaseWithDoc project settings --- libmodbuspp.project | 44 ++++++++++++++++++++++++++++++++++++++++++-- modbuspp.workspace | 40 ++++++++++++++++++++++++++++++++-------- 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/libmodbuspp.project b/libmodbuspp.project index ce321fc..e2f02ee 100644 --- a/libmodbuspp.project +++ b/libmodbuspp.project @@ -107,6 +107,8 @@ + + @@ -157,6 +159,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + None + + + + + + + + + + + + + + @@ -197,6 +239,4 @@ - - diff --git a/modbuspp.workspace b/modbuspp.workspace index 92bc5b1..706a099 100644 --- a/modbuspp.workspace +++ b/modbuspp.workspace @@ -41,19 +41,43 @@ - + - + + + + + + + + + + + - + - - - - - + + + + + + + + + + + + + + + + + + + From a0b862a3bdd07e536ae0a3ccc34729d0512b103c Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Wed, 23 Jun 2021 11:59:29 +0200 Subject: [PATCH 04/20] move configure_file call for config.h behind all check_symbol_exists calls to ensure the defines are set correctly --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c81ba20..f7c0b73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,8 +108,6 @@ include (GitVersion) GetGitVersion(MODBUSPP_VERSION) set(MODBUSPP_VERSION ${MODBUSPP_VERSION_MAJOR}.${MODBUSPP_VERSION_MINOR}.${MODBUSPP_VERSION_PATCH}) -configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/config.h.in - ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h @ONLY) @@ -127,6 +125,9 @@ check_symbol_exists(TIOCM_RTS sys/ioctl.h MODBUSPP_HAVE_TIOCM_RTS) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBMODBUS_NAME}) check_symbol_exists(modbus_rtu_set_recv_filter ${LIBMODBUS_NAME}/modbus-rtu.h MODBUSPP_HAVE_RTU_MULTI_SLAVES) +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/config.h.in + ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) + # Make relative paths absolute (needed later on) foreach(p LIB BIN INCLUDE CMAKE DATA DOC CODELITE) set(var INSTALL_${p}_DIR) From fe0438d28ebf32e894f5ef2ef798c88d3bcff399 Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Wed, 23 Jun 2021 12:00:44 +0200 Subject: [PATCH 05/20] adjust all defines and function call to libmodbus-ascii-support --- CMakeLists.txt | 4 ++-- README.md | 2 +- config.h.in | 2 +- include/modbuspp/global.h | 6 +++--- src/rtulayer.cpp | 14 +++++++------- src/server.cpp | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f7c0b73..f322b02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,7 +123,7 @@ check_symbol_exists(TIOCSRS485 sys/ioctl.h MODBUSPP_HAVE_TIOCRS485) check_symbol_exists(TIOCM_RTS sys/ioctl.h MODBUSPP_HAVE_TIOCM_RTS) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBMODBUS_NAME}) -check_symbol_exists(modbus_rtu_set_recv_filter ${LIBMODBUS_NAME}/modbus-rtu.h MODBUSPP_HAVE_RTU_MULTI_SLAVES) +check_symbol_exists(modbus_serial_set_recv_filter ${LIBMODBUS_NAME}/modbus-serial.h MODBUSPP_HAVE_SERIAL_MULTI_SLAVES) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) @@ -308,7 +308,7 @@ if (CPACK_GENERATOR STREQUAL "DEB") set(CPACK_DEBIAN_DEV_PACKAGE_NAME "libmodbuspp-dev") set(CPACK_COMPONENT_DEV_DESCRIPTION "${CPACK_DEBIAN_LIB_PACKAGE_NAME} - ${PROJECT_DESCRIPTION} (development files)\n${PROJECT_DESCRIPTION_TEXT}\n This package provides the development files.") set(CPACK_DEBIAN_DEV_FILE_NAME "lib${PROJECT_NAME}-dev_${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb") - set(CPACK_DEBIAN_DEV_PACKAGE_DEPENDS "${CPACK_DEBIAN_LIB_PACKAGE_NAME} (= ${CPACK_PACKAGE_VERSION}),pkg-config,git-core,${LIBMODBUSDEV_PACKAGE} (>= ${LIBMODBUS_VERSION})") + set(CPACK_DEBIAN_DEV_PACKAGE_DEPENDS "${CPACK_DEBIAN_LIB_PACKAGE_NAME} (= ${CPACK_PACKAGE_VERSION}),pkg-fconfig,git-core,${LIBMODBUSDEV_PACKAGE} (>= ${LIBMODBUS_VERSION})") set(CPACK_DEBIAN_DEV_PACKAGE_SECTION "libdevel") #set(CPACK_PACKAGE_DESCRIPTION_FILE "${MODBUSPP_SRC_DIR}/doc/README-deb.md") diff --git a/README.md b/README.md index b4fe13a..129ec72 100644 --- a/README.md +++ b/README.md @@ -299,7 +299,7 @@ _"Filter on the Modbus unit identifier (slave) in RTU mode to avoid useless CRC To benefit from the routing capacity of the Router and Server classes in RTU, you must therefore use the fork of libmodbus named **libmodbusepsi**. In this fork released from the [piduino](http://apt.piduino.org) -repository, filtering can be disabled (with _modbus_rtu_set_recv_filter()_). +repository, filtering can be disabled (with _modbus_serial_set_recv_filter()_). Thus, it is the Server class which performs this filtering (after checking the CRC therefore). Effectively, this has the effect of loading the microprocessor, but, at present, the computing power of our machines is such that it does not diff --git a/config.h.in b/config.h.in index 4ce219c..187b07a 100644 --- a/config.h.in +++ b/config.h.in @@ -19,6 +19,6 @@ #include "version.h" #cmakedefine01 MODBUSPP_HAVE_TIOCRS485 #cmakedefine01 MODBUSPP_HAVE_TIOCM_RTS -#cmakedefine01 MODBUSPP_HAVE_RTU_MULTI_SLAVES +#cmakedefine01 MODBUSPP_HAVE_SERIAL_MULTI_SLAVES /* ========================================================================== */ diff --git a/include/modbuspp/global.h b/include/modbuspp/global.h index 3a538d4..6162a45 100644 --- a/include/modbuspp/global.h +++ b/include/modbuspp/global.h @@ -105,9 +105,9 @@ namespace Modbus { * The @b RtsDown mode applies the same procedure but with an inverted RTS flag. */ enum SerialRts { - RtsNone = MODBUS_RTU_RTS_NONE, ///< no use of the RTS. - RtsUp = MODBUS_RTU_RTS_UP, ///< RTS flag ON during communication, OFF outside. - RtsDown = MODBUS_RTU_RTS_DOWN, ///< RTS flag OFF during communication, ON outside. + RtsNone = MODBUS_SERIAL_RTS_NONE, ///< no use of the RTS. + RtsUp = MODBUS_SERIAL_RTS_UP, ///< RTS flag ON during communication, OFF outside. + RtsDown = MODBUS_SERIAL_RTS_DOWN, ///< RTS flag OFF during communication, ON outside. UnknownRts = Unknown ///< Unknown RTS mode. }; diff --git a/src/rtulayer.cpp b/src/rtulayer.cpp index dad21ac..6785c4c 100644 --- a/src/rtulayer.cpp +++ b/src/rtulayer.cpp @@ -104,7 +104,7 @@ namespace Modbus { SerialMode RtuLayer::serialMode() { PIMP_D (RtuLayer); - int m = modbus_rtu_get_serial_mode (d->ctx); + int m = modbus_serial_get_serial_mode (d->ctx); if (m != -1) { return static_cast (m); } @@ -115,14 +115,14 @@ namespace Modbus { bool RtuLayer::setSerialMode (SerialMode mode) { PIMP_D (RtuLayer); - return (modbus_rtu_set_serial_mode (d->ctx, static_cast (mode)) != -1); + return (modbus_serial_set_serial_mode (d->ctx, static_cast (mode)) != -1); } // --------------------------------------------------------------------------- SerialRts RtuLayer::rts() { PIMP_D (RtuLayer); - int r = modbus_rtu_get_rts (d->ctx); + int r = modbus_serial_get_rts (d->ctx); if (r != -1) { return static_cast (r); @@ -134,21 +134,21 @@ namespace Modbus { bool RtuLayer::setRts (SerialRts r) { PIMP_D (RtuLayer); - return (modbus_rtu_set_rts (d->ctx, static_cast (r)) != -1); + return (modbus_serial_set_rts (d->ctx, static_cast (r)) != -1); } // --------------------------------------------------------------------------- int RtuLayer::rtsDelay() { PIMP_D (RtuLayer); - return modbus_rtu_get_rts_delay (d->ctx); + return modbus_serial_get_rts_delay (d->ctx); } // --------------------------------------------------------------------------- bool RtuLayer::setRtsDelay (int delay) { PIMP_D (RtuLayer); - return (modbus_rtu_set_rts_delay (d->ctx, delay) != -1); + return (modbus_serial_set_rts_delay (d->ctx, delay) != -1); } // --------------------------------------------------------------------------- @@ -316,7 +316,7 @@ namespace Modbus { "Unable to create RTU Modbus Backend(" + port + "," + settings + ")\n" + lastError()); } - oneByteTime = modbus_rtu_get_rts_delay (ctx); + oneByteTime = modbus_serial_get_rts_delay (ctx); } } diff --git a/src/server.cpp b/src/server.cpp index f975f54..25fe6f7 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -252,9 +252,9 @@ namespace Modbus { const std::string & settings) { Device::Private::setBackend (net, connection, settings); -#if MODBUSPP_HAVE_RTU_MULTI_SLAVES +#if MODBUSPP_HAVE_SERIAL_MULTI_SLAVES if (net == Rtu) { - modbus_rtu_set_recv_filter (ctx(), FALSE); + modbus_serial_set_recv_filter (ctx(), FALSE); } #endif } From 0cb6ff5063a74edf4ac3cb3dec3192512251529b Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Wed, 23 Jun 2021 15:01:48 +0200 Subject: [PATCH 06/20] ascii support is only available in the custom libmodbusepsi starting with version 3.1.8; remove dependency in default libmodbus --- CMakeLists.txt | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f322b02..55e0c09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,16 +72,11 @@ set(CMAKE_THREAD_PREFER_PTHREAD TRUE) set(THREADS_PREFER_PTHREAD_FLAG TRUE) find_package(Threads REQUIRED) -set(LIBMODBUS_MIN_VERSION "3.1.6") +set(LIBMODBUS_MIN_VERSION "3.1.8") find_package(PkgConfig REQUIRED) -pkg_check_modules (LIBMODBUS libmodbusepsi>=${LIBMODBUS_MIN_VERSION}) -if (LIBMODBUS_FOUND) - set(LIBMODBUS_NAME modbusepsi) -else() - pkg_check_modules (LIBMODBUS REQUIRED libmodbus>=${LIBMODBUS_MIN_VERSION}) - set(LIBMODBUS_NAME modbus) -endif() +pkg_check_modules (LIBMODBUS REQUIRED libmodbusepsi>=${LIBMODBUS_MIN_VERSION}) +set(LIBMODBUS_NAME modbusepsi) set(LIBMODBUS_PACKAGE lib${LIBMODBUS_NAME}5) set(LIBMODBUSDEV_PACKAGE lib${LIBMODBUS_NAME}-dev) From 3daeb9438c9ee16e0addc202873270b786316000 Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Wed, 23 Jun 2021 16:27:27 +0200 Subject: [PATCH 07/20] Create a copy of RTU Layer as a starting poiont for building the ASCII Layer --- include/modbuspp.h | 1 + include/modbuspp/asciilayer.h | 206 ++++++++++++++++++++++ include/modbuspp/global.h | 8 + src/asciilayer.cpp | 315 ++++++++++++++++++++++++++++++++++ src/asciilayer_p.h | 32 ++++ src/device.cpp | 5 + src/json_p.h | 1 + src/message.cpp | 5 + 8 files changed, 573 insertions(+) create mode 100644 include/modbuspp/asciilayer.h create mode 100644 src/asciilayer.cpp create mode 100644 src/asciilayer_p.h diff --git a/include/modbuspp.h b/include/modbuspp.h index 85f2155..b17dbe4 100644 --- a/include/modbuspp.h +++ b/include/modbuspp.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include #include diff --git a/include/modbuspp/asciilayer.h b/include/modbuspp/asciilayer.h new file mode 100644 index 0000000..ed62103 --- /dev/null +++ b/include/modbuspp/asciilayer.h @@ -0,0 +1,206 @@ +/* Copyright © 2018-2019 Pascal JEAN, All rights reserved. + * This file is part of the libmodbuspp Library. + * + * The libmodbuspp Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * The libmodbuspp Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the libmodbuspp Library; if not, see . + */ +#pragma once + +#include + +namespace Modbus { + + /** + * @class AsciiLayer + * @brief RTU serial link layer + * + * This class can not and should not be instantiated by the user. + * It provides access to properties and methods specific to the RTU layer. + * + * An instance of this class is created by the constructor @b Device::Device() + * of the @b Device class (or its derived classes) if the RTU layer is selected. + * + * Access to this instance is done using the Device::rtu() method. + * + * @sa Device::Device() + * @sa Device::rtu() + * + * @author Pascal JEAN, aka epsilonrt + * @copyright GNU Lesser General Public License + */ + class AsciiLayer : public NetLayer { + public: + + /** + * @brief Constructor + */ + AsciiLayer (const std::string & port, const std::string & settings); + + /** + * @brief Name of the serial port + * + * This property specifies the name of the serial port handled by the + * OS, eg. "/dev/ttyS0" or "/dev/ttyUSB0". + */ + const std::string & port() const; + + /** + * @brief Return the baudrate + */ + int baud() const; + + /** + * @brief Return the parity + */ + char parity() const; + + /** + * @brief Return the bits of stop + */ + int stop() const; + + /** + * @brief Get the current serial mode + * + * This function shall return the serial mode currently used. + * + * - @b Rs232: the serial line is set for RS232 communication. RS-232 + * (Recommended Standard 232) is the traditional name for a series of + * standards for serial binary single-ended data and control signals + * connecting between a DTE (Data Terminal Equipment) and a DCE + * (Data Circuit-terminating Equipment). + * It is commonly used in computer serial ports + * - @b Rs485: the serial line is set for RS485 communication. EIA-485, + * also known as TIA/EIA-485 or RS-485, is a standard defining the electrical + * characteristics of drivers and receivers for use in balanced digital + * multipoint systems. This standard is widely used for communications in + * industrial automation because it can be used effectively over long + * distances and in electrically noisy environments. + * . + * This function is only available on Linux kernels 2.6.28 onwards. + * + * @return return the current mode if successful. + * Otherwise it shall return @b UnknownMode (-1) and set errno. + * @sa setSerialMode() + */ + SerialMode serialMode(); + + /** + * @brief Set the serial mode + * + * This function shall set the selected serial mode @b mode. + * + * @return true if successful. + * Otherwise it shall return false and set errno. + * @sa serialMode() + */ + bool setSerialMode (SerialMode mode); + + /** + * @brief Get the current RTS mode + * + * This function shall get the current Request To Send mode + * + * @return the current RTS mode if successful. + * Otherwise it shall return @b UnknownRts (-1) and set errno. + * @sa setRts() + */ + SerialRts rts(); + + /** + * @brief Set the RTS mode + * + * This function shall set the Request To Send mode to communicate on a + * RS485 serial bus. + * + * @return true if successful. + * Otherwise it shall return false and set errno. + * @sa rts() + */ + bool setRts (SerialRts rts); + + /** + * @brief Get the current RTS delay + * + * This function shall get the current Request To Send delay period. + * @return the current RTS delay in microseconds if successful. + * Otherwise it shall return -1 and set errno. + * @sa setRtsDelay() + */ + int rtsDelay(); + + /** + * @brief Set the RTS delay + * + * This function shall set the Request To Send delay period in microseconds. + * + * @return true if successful. + * Otherwise it shall return false and set errno. + * @sa rtsDelay() + */ + bool setRtsDelay (int us); + + /** + * @overload + * + * @warning This function is not supported by Windows ! + */ + virtual int sendRawMessage (const Message * msg); + + /** + * @overload + */ + virtual bool prepareToSend (Message & msg); + + /** + * @overload + */ + static bool checkMessage (const Message & msg); + + /** + * @brief Extracts the baudrate from a settings string. + * @return the baudrate found. if no value is found, returns the default + * value, ie 19200. + */ + static int baud (const std::string & settings); + + /** + * @brief Extracts the parity from a settings string. + * @return the parity found. if no value is found, returns the default + * value, ie E for Even parity. + */ + static char parity (const std::string & settings); + + /** + * @brief Return the stop bits from a settings string. + * + * @return the number returned is determined based on the parity found. + * If the parity is None, this function returns 2, otherwise returns 1. + */ + static int stop (const std::string & settings); + + /** + * @brief Performing Modbus CRC16 generation of the buffer @b buf + */ + static uint16_t crc16 (const uint8_t * buf, uint16_t count); + + protected: + class Private; + AsciiLayer (Private &dd); + + private: + PIMP_DECLARE_PRIVATE (AsciiLayer) + }; +} + +/* ========================================================================== */ diff --git a/include/modbuspp/global.h b/include/modbuspp/global.h index 6162a45..db69cf6 100644 --- a/include/modbuspp/global.h +++ b/include/modbuspp/global.h @@ -48,6 +48,14 @@ namespace Modbus { * protocol communication. */ Rtu, + /** + * @brief ASCII backend + * + * The ASCII backend is used in serial communication. + * The hex value of the binary data is transmitted + * in ASCII character representation. + */ + Ascii, /** * @brief TCP backend * diff --git a/src/asciilayer.cpp b/src/asciilayer.cpp new file mode 100644 index 0000000..4c39015 --- /dev/null +++ b/src/asciilayer.cpp @@ -0,0 +1,315 @@ +/* Copyright © 2018-2019 Pascal JEAN, All rights reserved. + * This file is part of the libmodbuspp Library. + * + * The libmodbuspp Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * The libmodbuspp Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the libmodbuspp Library; if not, see . + */ +#include +#include "asciilayer_p.h" +#include "config.h" + +#ifndef _WIN32 +#include +#endif + +namespace Modbus { + + namespace ascii { + + // ------------------------------------------------------------------------- + const uint8_t CrcHiTable[] = { + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 + }; + + // ------------------------------------------------------------------------- + const uint8_t CrcLoTable[] = { + 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, + 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, + 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, + 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, + 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, + 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, + 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, + 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, + 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, + 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, + 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, + 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, + 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, + 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, + 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, + 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, + 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, + 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, + 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, + 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, + 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, + 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, + 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, + 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, + 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, + 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 + }; + } + + // --------------------------------------------------------------------------- + // + // AsciiLayer Class + // + // --------------------------------------------------------------------------- + + // --------------------------------------------------------------------------- + AsciiLayer::AsciiLayer (AsciiLayer::Private &dd) : NetLayer (dd) {} + + // --------------------------------------------------------------------------- + AsciiLayer::AsciiLayer (const std::string & port, const std::string & settings) : + NetLayer (*new Private (port, settings)) {} + + // --------------------------------------------------------------------------- + SerialMode AsciiLayer::serialMode() { + PIMP_D (AsciiLayer); + + int m = modbus_serial_get_serial_mode (d->ctx); + if (m != -1) { + return static_cast (m); + } + return UnknownMode; + } + + // --------------------------------------------------------------------------- + bool AsciiLayer::setSerialMode (SerialMode mode) { + PIMP_D (AsciiLayer); + + return (modbus_serial_set_serial_mode (d->ctx, static_cast (mode)) != -1); + } + + // --------------------------------------------------------------------------- + SerialRts AsciiLayer::rts() { + PIMP_D (AsciiLayer); + + int r = modbus_serial_get_rts (d->ctx); + if (r != -1) { + + return static_cast (r); + } + return UnknownRts; + } + + // --------------------------------------------------------------------------- + bool AsciiLayer::setRts (SerialRts r) { + PIMP_D (AsciiLayer); + + return (modbus_serial_set_rts (d->ctx, static_cast (r)) != -1); + } + + // --------------------------------------------------------------------------- + int AsciiLayer::rtsDelay() { + PIMP_D (AsciiLayer); + + return modbus_serial_get_rts_delay (d->ctx); + } + + // --------------------------------------------------------------------------- + bool AsciiLayer::setRtsDelay (int delay) { + PIMP_D (AsciiLayer); + + return (modbus_serial_set_rts_delay (d->ctx, delay) != -1); + } + + // --------------------------------------------------------------------------- + const std::string & AsciiLayer::port() const { + + return connection(); + } + + // --------------------------------------------------------------------------- + int AsciiLayer::baud() const { + PIMP_D (const AsciiLayer); + + return baud (d->settings); + } + + // --------------------------------------------------------------------------- + char AsciiLayer::parity() const { + PIMP_D (const AsciiLayer); + + return parity (d->settings); + } + + // --------------------------------------------------------------------------- + int AsciiLayer::stop() const { + PIMP_D (const AsciiLayer); + + return stop (d->settings); + } + + // --------------------------------------------------------------------------- + int AsciiLayer::sendRawMessage (const Message * msg) { + PIMP_D (const AsciiLayer); +#if defined(_WIN32) + errno = ENOTSUP; + return -1; +#else +#if MODBUSPP_HAVE_TIOCM_RTS + if (rts() != RtsNone) { + ssize_t size; + SerialRts r = (rts() == RtsDown) ? RtsUp : RtsDown; // complement the Rts state + + setRts (r); + usleep (rtsDelay()); + + size = write (modbus_get_socket (d->ctx), msg->adu(), msg->aduSize()); + + usleep (d->oneByteTime * msg->aduSize() + rtsDelay()); + setRts ( (r == RtsDown) ? RtsUp : RtsDown); // restore initial state + + return size; + } + else { +#endif + return write (modbus_get_socket (d->ctx), msg->adu(), msg->aduSize()); +#if MODBUSPP_HAVE_TIOCM_RTS + } +#endif +#endif + } + + // --------------------------------------------------------------------------- + bool AsciiLayer::prepareToSend (Message & msg) { + + if (msg.net() == Rtu && msg.size() >= 1) { + size_t aduSize = msg.aduSize(); + uint8_t * adu = msg.adu(); + + uint16_t crc = crc16 (adu, aduSize); + adu[aduSize++] = crc >> 8; + adu[aduSize++] = crc & 0xFF; + msg.setAduSize (aduSize); + return true; + } + return false; + } + + // --------------------------------------------------------------------------- + bool AsciiLayer::checkMessage (const Message & msg) { + + return crc16 (msg.adu(), msg.aduSize() - 2) == msg.crc (); + } + + // --------------------------------------------------------------------------- + // static + int AsciiLayer::baud (const std::string & settings) { + int b; + try { + b = std::stoi (settings); + } + catch (...) { + b = 19200; + } + return b; + } + + // --------------------------------------------------------------------------- + // static + char AsciiLayer::parity (const std::string & settings) { + char p = 'N'; + size_t s = settings.length(); + + if (s >= 2) { + char c = settings[s - 2]; + if ( (c == 'E') || (c == 'O')) { + return c; + } + } + return p; + } + + // --------------------------------------------------------------------------- + // static + int AsciiLayer::stop (const std::string & settings) { + + if (parity (settings) == 'N') { + + return 2; + } + return 1; + } + + // --------------------------------------------------------------------------- + // static + uint16_t AsciiLayer::crc16 (const uint8_t * buf, uint16_t count) { + uint8_t crcHi = 0xFF; /* high CRC byte initialized */ + uint8_t crcLo = 0xFF; /* low CRC byte initialized */ + unsigned int i; /* will index into CRC lookup */ + + /* pass through message buffer */ + while (count--) { + + i = crcHi ^ *buf++; /* calculate the CRC */ + crcHi = crcLo ^ ascii::CrcHiTable[i]; + crcLo = ascii::CrcLoTable[i]; + } + + return (crcHi << 8 | crcLo); + } + + // --------------------------------------------------------------------------- + // + // AsciiLayer::Private Class + // + // --------------------------------------------------------------------------- + + // --------------------------------------------------------------------------- + AsciiLayer::Private::Private (const std::string & port, const std::string & settings) : + NetLayer::Private (Rtu, port, settings, MODBUS_ASCII_MAX_ADU_LENGTH) { + + // RTU MUST BE 8-bits + ctx = modbus_new_ascii (port.c_str(), AsciiLayer::baud (settings), + AsciiLayer::parity (settings), 8, + AsciiLayer::stop (settings)); + if (! ctx) { + + throw std::invalid_argument ( + "Unable to create ASCII Modbus Backend(" + + port + "," + settings + ")\n" + lastError()); + } + oneByteTime = modbus_serial_get_rts_delay (ctx); + } +} + +/* ========================================================================== */ diff --git a/src/asciilayer_p.h b/src/asciilayer_p.h new file mode 100644 index 0000000..d395f7e --- /dev/null +++ b/src/asciilayer_p.h @@ -0,0 +1,32 @@ +/* Copyright © 2018-2019 Pascal JEAN, All rights reserved. + * This file is part of the libmodbuspp Library. + * + * The libmodbuspp Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * The libmodbuspp Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the libmodbuspp Library; if not, see . + */ +#pragma once + +#include +#include "netlayer_p.h" + +namespace Modbus { + + class AsciiLayer::Private : public NetLayer::Private { + + public: + Private (const std::string & port, const std::string & settings); + int oneByteTime; + }; +} + +/* ========================================================================== */ diff --git a/src/device.cpp b/src/device.cpp index 1d0df1d..dce1225 100644 --- a/src/device.cpp +++ b/src/device.cpp @@ -15,6 +15,7 @@ * along with the libmodbuspp Library; if not, see . */ #include +#include #include #include #include "device_p.h" @@ -507,6 +508,10 @@ namespace Modbus { backend = new RtuLayer (connection, settings); break; + case Ascii: + backend = new AsciiLayer (connection, settings); + break; + default: throw std::invalid_argument ( "Unable to create Modbus Device for this net !"); diff --git a/src/json_p.h b/src/json_p.h index c77e0b7..e1c2fd1 100644 --- a/src/json_p.h +++ b/src/json_p.h @@ -36,6 +36,7 @@ namespace Modbus { NLOHMANN_JSON_SERIALIZE_ENUM (Net, { {Rtu, "rtu"}, + {Ascii, "ascii"}, {Tcp, "tcp"}, {NoNet, nullptr}, }) diff --git a/src/message.cpp b/src/message.cpp index a59ed61..3172807 100644 --- a/src/message.cpp +++ b/src/message.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "message_p.h" #include "config.h" @@ -492,6 +493,10 @@ namespace Modbus { b = new RtuLayer ("COM1", "9600E1"); break; + case Ascii: + b = new AsciiLayer("COM1", "9600E1"); + break; + default: throw std::invalid_argument ( "Unable to create Modbus Device for this net !"); From eb1966f78a255071033c93f9d46f2359e6cd1a1d Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Thu, 24 Jun 2021 08:20:56 +0200 Subject: [PATCH 08/20] use the recv_filter flag also for ASCII mode --- src/server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.cpp b/src/server.cpp index 25fe6f7..a076943 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -253,7 +253,7 @@ namespace Modbus { Device::Private::setBackend (net, connection, settings); #if MODBUSPP_HAVE_SERIAL_MULTI_SLAVES - if (net == Rtu) { + if (net == Rtu || net == Ascii) { modbus_serial_set_recv_filter (ctx(), FALSE); } #endif From 9c63d31060b43599cd7ab2164aa7e6bedcc08499 Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Thu, 24 Jun 2021 11:40:00 +0200 Subject: [PATCH 09/20] implement quick n dirty ascii layer functionality --- include/modbuspp/asciilayer.h | 2 +- include/modbuspp/device.h | 9 +++ include/modbuspp/message.h | 8 ++ src/asciilayer.cpp | 141 +++++++++++++--------------------- src/device.cpp | 32 +++++++- src/master.cpp | 1 + src/message.cpp | 38 ++++++++- src/server.cpp | 1 + 8 files changed, 138 insertions(+), 94 deletions(-) diff --git a/include/modbuspp/asciilayer.h b/include/modbuspp/asciilayer.h index ed62103..b608d40 100644 --- a/include/modbuspp/asciilayer.h +++ b/include/modbuspp/asciilayer.h @@ -192,7 +192,7 @@ namespace Modbus { /** * @brief Performing Modbus CRC16 generation of the buffer @b buf */ - static uint16_t crc16 (const uint8_t * buf, uint16_t count); + static uint8_t lrc8 (const uint8_t * buffer, uint16_t buffer_length); protected: class Private; diff --git a/include/modbuspp/device.h b/include/modbuspp/device.h index 185e0da..9883177 100644 --- a/include/modbuspp/device.h +++ b/include/modbuspp/device.h @@ -24,6 +24,7 @@ namespace Modbus { class NetLayer; class RtuLayer; + class AsciiLayer; class TcpLayer; class Message; @@ -477,6 +478,14 @@ namespace Modbus { */ RtuLayer & rtu(); + /** + * @brief underlying RTU layer (backend) + * + * This function shall return the RTU layer if it is the layer used by + * the device. If it does not, a @b std::domain_error exception is thrown. + */ + AsciiLayer & ascii(); + /** * @brief underlying TCP layer (backend) * diff --git a/include/modbuspp/message.h b/include/modbuspp/message.h index cb72260..b9d6b72 100644 --- a/include/modbuspp/message.h +++ b/include/modbuspp/message.h @@ -219,6 +219,14 @@ namespace Modbus { */ uint16_t crc () const; + /** + * @brief Return the LRC read in the message + * + * throw an std::domain_error exception if net()!=Rtu, + * an std::invalid_argument exception if aduSize()<8. + */ + uint8_t lrc () const; + /** * @brief Returns TCP/IP transaction identifier * diff --git a/src/asciilayer.cpp b/src/asciilayer.cpp index 4c39015..6364ff0 100644 --- a/src/asciilayer.cpp +++ b/src/asciilayer.cpp @@ -15,78 +15,16 @@ * along with the libmodbuspp Library; if not, see . */ #include +#include #include "asciilayer_p.h" #include "config.h" +#include "modbuspp/global.h" #ifndef _WIN32 #include #endif namespace Modbus { - - namespace ascii { - - // ------------------------------------------------------------------------- - const uint8_t CrcHiTable[] = { - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, - 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, - 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, - 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, - 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, - 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, - 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, - 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, - 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, - 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, - 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, - 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, - 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 - }; - - // ------------------------------------------------------------------------- - const uint8_t CrcLoTable[] = { - 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, - 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, - 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, - 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, - 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, - 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, - 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, - 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, - 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, - 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, - 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, - 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, - 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, - 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, - 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, - 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, - 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, - 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, - 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, - 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, - 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, - 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, - 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, - 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, - 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, - 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 - }; - } - // --------------------------------------------------------------------------- // // AsciiLayer Class @@ -209,17 +147,51 @@ namespace Modbus { #endif } +static char nibble_to_hex_ascii(uint8_t nibble) +{ + char c; + + if (nibble < 10) { + c = nibble + '0'; + } else { + c = nibble - 10 + 'A'; + } + return c; +} + // --------------------------------------------------------------------------- bool AsciiLayer::prepareToSend (Message & msg) { - - if (msg.net() == Rtu && msg.size() >= 1) { + + if (msg.net() == Ascii && msg.size() >= 1) { size_t aduSize = msg.aduSize(); uint8_t * adu = msg.adu(); - uint16_t crc = crc16 (adu, aduSize); - adu[aduSize++] = crc >> 8; - adu[aduSize++] = crc & 0xFF; - msg.setAduSize (aduSize); + /* Skip colon */ + uint8_t lrc = lrc8(adu + 1, aduSize- 1); + adu[aduSize++] = lrc; + + uint8_t ascii_adu[MODBUS_ASCII_MAX_ADU_LENGTH]; + memset(ascii_adu, 0, MODBUS_ASCII_MAX_ADU_LENGTH); + ssize_t i, j = 0; + + for (i = 0; i < aduSize; i++) { + if ((i == 0 && adu[i] == ':') || + (i == aduSize - 2 && adu[i] == '\r') || + (i == aduSize - 1 && adu[i] == '\n')) { + ascii_adu[j++] = adu[i]; + } else { + ascii_adu[j++] = nibble_to_hex_ascii(adu[i] >> 4); + ascii_adu[j++] = nibble_to_hex_ascii(adu[i] & 0x0f); + } + } + ascii_adu[j++] = '\r'; + ascii_adu[j++] = '\n'; + ascii_adu[j] = '\0'; + + msg.setAduSize (j); + + std::memcpy(adu, ascii_adu, j); + return true; } return false; @@ -228,7 +200,7 @@ namespace Modbus { // --------------------------------------------------------------------------- bool AsciiLayer::checkMessage (const Message & msg) { - return crc16 (msg.adu(), msg.aduSize() - 2) == msg.crc (); + return lrc8 (msg.adu(), msg.aduSize() - 2) == msg.lrc (); } // --------------------------------------------------------------------------- @@ -272,21 +244,15 @@ namespace Modbus { // --------------------------------------------------------------------------- // static - uint16_t AsciiLayer::crc16 (const uint8_t * buf, uint16_t count) { - uint8_t crcHi = 0xFF; /* high CRC byte initialized */ - uint8_t crcLo = 0xFF; /* low CRC byte initialized */ - unsigned int i; /* will index into CRC lookup */ - - /* pass through message buffer */ - while (count--) { - - i = crcHi ^ *buf++; /* calculate the CRC */ - crcHi = crcLo ^ ascii::CrcHiTable[i]; - crcLo = ascii::CrcLoTable[i]; - } - - return (crcHi << 8 | crcLo); - } + uint8_t AsciiLayer::lrc8(const uint8_t *buffer, uint16_t buffer_length) + { + uint8_t lrc = 0; + while (buffer_length--) { + lrc += *buffer++; + } + /* Return two's complementing of the result */ + return -lrc; + } // --------------------------------------------------------------------------- // @@ -296,12 +262,13 @@ namespace Modbus { // --------------------------------------------------------------------------- AsciiLayer::Private::Private (const std::string & port, const std::string & settings) : - NetLayer::Private (Rtu, port, settings, MODBUS_ASCII_MAX_ADU_LENGTH) { + NetLayer::Private (Ascii, port, settings, MODBUS_ASCII_MAX_ADU_LENGTH) { // RTU MUST BE 8-bits ctx = modbus_new_ascii (port.c_str(), AsciiLayer::baud (settings), AsciiLayer::parity (settings), 8, AsciiLayer::stop (settings)); + if (! ctx) { throw std::invalid_argument ( diff --git a/src/device.cpp b/src/device.cpp index dce1225..0864df8 100644 --- a/src/device.cpp +++ b/src/device.cpp @@ -20,6 +20,7 @@ #include #include "device_p.h" #include "config.h" +#include "modbuspp/global.h" #include #include // for debug purposes @@ -202,6 +203,17 @@ namespace Modbus { throw std::domain_error ("Unable to return RTU layer !"); } + // --------------------------------------------------------------------------- + AsciiLayer & Device::ascii() { + + if (net() == Ascii) { + PIMP_D (Device); + + return * reinterpret_cast (d->backend); + } + throw std::domain_error ("Unable to return ASCII layer !"); + } + // --------------------------------------------------------------------------- TcpLayer & Device::tcp() { @@ -544,8 +556,8 @@ namespace Modbus { int Device::Private::defaultSlave (int addr) const { if (addr < 0 && backend != 0) { - - return backend->net() == Rtu ? Broadcast : TcpSlave; + Net n = backend->net(); + return (n == Rtu || n == Ascii) ? Broadcast : TcpSlave; } return addr; } @@ -624,6 +636,22 @@ namespace Modbus { dev->rtu().setRtsDelay (r); } } + if (config.contains ("ascii") && net == Ascii) { + auto ascii = config["ascii"]; + + if (ascii.contains ("mode")) { + auto m = ascii["mode"].get(); + dev->ascii().setSerialMode (m); + } + if (ascii.contains ("rts")) { + auto r = ascii["rts"].get(); + dev->ascii().setRts (r); + } + if (ascii.contains ("rts-delay")) { + auto r = ascii["rts-delay"].get(); + dev->ascii().setRtsDelay (r); + } + } } } } diff --git a/src/master.cpp b/src/master.cpp index f7dc3c0..990f885 100644 --- a/src/master.cpp +++ b/src/master.cpp @@ -201,6 +201,7 @@ namespace Modbus { break; case Rtu: + case Ascii: (void) addSlave (Broadcast); break; } diff --git a/src/message.cpp b/src/message.cpp index 3172807..ee9bc6f 100644 --- a/src/message.cpp +++ b/src/message.cpp @@ -261,6 +261,13 @@ namespace Modbus { } break; + case Ascii: { + uint8_t lrc = AsciiLayer::lrc8 (adu(), aduSize()); + + setByte(size(), lrc); + } + break; + default: throw std::invalid_argument ( "Unable to set PDU for this net !"); @@ -339,6 +346,32 @@ namespace Modbus { return word (size() - 2); } + // --------------------------------------------------------------------------- + uint8_t Message::lrc () const { + std::cout << "message::lrc\n"; + if (net() != Ascii) { + + throw std::domain_error ("Unable to return LRC if backend is not ASCII !"); + } + if (aduSize() >= 4) { + + throw std::invalid_argument ("Unable to return LRC if ADU size less than 8 !"); + } + uint16_t lrc = word (size() - 2); + uint8_t lrc_hi = lrc >> 8 & 0xFF; + uint8_t lrc_lo = lrc & 0xFF; + lrc_hi -= '0'; + lrc_lo -= '0'; + + std::cout << "lrc_hi: " << lrc_hi << "\n"; + std::cout << "lrc_lo: " << lrc_lo << "\n"; + + uint8_t lrc_8_bit = lrc_hi << 4; + lrc_8_bit |= lrc_lo; + + return lrc_8_bit; + } + // --------------------------------------------------------------------------- void Message::setAduSize (size_t size) { PIMP_D (Message); @@ -490,13 +523,10 @@ namespace Modbus { break; case Rtu: + case Ascii: b = new RtuLayer ("COM1", "9600E1"); break; - case Ascii: - b = new AsciiLayer("COM1", "9600E1"); - break; - default: throw std::invalid_argument ( "Unable to create Modbus Device for this net !"); diff --git a/src/server.cpp b/src/server.cpp index a076943..4796293 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -296,6 +296,7 @@ namespace Modbus { break; case Rtu: + case Ascii: isOk = Device::Private::open(); default: From b62858cabc3813332d550e6006ca04830bf026a6 Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Fri, 25 Jun 2021 13:38:33 +0200 Subject: [PATCH 10/20] remove the modbusepsi include path from CFLAGS and use a relative path instead (works better with bitbake) --- CMakeLists.txt | 2 +- modbuspp.pc.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 55e0c09..b75209d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,7 +93,7 @@ else() add_subdirectory(3rdparty/json) endif() -set (MODBUSPP_CFLAGS_OTHER ${CMAKE_THREAD_LIBS_INIT} ${LIBMODBUS_CFLAGS}) +set (MODBUSPP_CFLAGS_OTHER ${CMAKE_THREAD_LIBS_INIT}) set (MODBUSPP_LDFLAGS_OTHER ${LIBMODBUS_LDFLAGS} -lpthread) include (GetDate) diff --git a/modbuspp.pc.in b/modbuspp.pc.in index 8e86d4a..3e4014e 100644 --- a/modbuspp.pc.in +++ b/modbuspp.pc.in @@ -12,5 +12,5 @@ URL: https://github.com/epsilonrt/libmodbuspp Version: @MODBUSPP_VERSION@ Requires: Libs: -L${libdir} -lmodbuspp ${ldflags_other} -Cflags: -I${includedir} ${cflags_other} +Cflags: -I${includedir} -I${includedir}/@LIBMODBUS_NAME@ ${cflags_other} From 68009b4e4a9e154bd7f610671f8cac77d998f48c Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Fri, 25 Jun 2021 14:40:11 +0200 Subject: [PATCH 11/20] make the installation of codelite templates optional with cmake option INSTALL_TEMPLATES default OFF --- dev/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dev/CMakeLists.txt b/dev/CMakeLists.txt index 47ef5c9..9ddb21e 100644 --- a/dev/CMakeLists.txt +++ b/dev/CMakeLists.txt @@ -17,6 +17,9 @@ cmake_minimum_required(VERSION 2.8.11) +option(INSTALL_TEMPLATES "Install codelite project templates to /usr/share/codelite" OFF) + +if(INSTALL_TEMPLATES) # set packaging dir if(NOT CPACK_PACKAGE_DIRECTORY) set(CPACK_PACKAGE_DIRECTORY ${CMAKE_BINARY_DIR}/packages) @@ -37,3 +40,4 @@ install (DIRECTORY codelite/unit-test-modbuspp DIRECTORY_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ COMPONENT dev ) +endif(INSTALL_TEMPLATES) From 42f859a4f4bc2393eebe06fb8c902e84f6445651 Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Mon, 18 Oct 2021 10:52:40 +0200 Subject: [PATCH 12/20] [async] fix race condition if destroy server object while call async receive --- src/server.cpp | 3 +++ src/server_p.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/server.cpp b/src/server.cpp index 4796293..2d2e6a6 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -27,6 +27,7 @@ # include # include #endif +#include #include "server_p.h" #include "config.h" @@ -314,6 +315,7 @@ namespace Modbus { // --------------------------------------------------------------------------- void Server::Private::close() { + std::lock_guard lg (d_guard); if (backend->net() == Tcp) { @@ -404,6 +406,7 @@ namespace Modbus { // --------------------------------------------------------------------------- // static int Server::Private::receive (Private * d) { + std::lock_guard lg (d->d_guard); int rc; if ( (d->backend->net() == Tcp) && !d->isConnected()) { diff --git a/src/server_p.h b/src/server_p.h index 616a7ea..e17dfd4 100644 --- a/src/server_p.h +++ b/src/server_p.h @@ -50,6 +50,8 @@ namespace Modbus { std::promise stopDaemon; Message::Callback messageCB; + std::mutex d_guard; + PIMP_DECLARE_PUBLIC (Server) }; } From e19ea3fcb2626dba58ca9687c52103f78cbc6e08 Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Mon, 25 Oct 2021 17:23:58 +0200 Subject: [PATCH 13/20] [message] change Callback from simple pointer to std::function; enables use of methods and lambda captures --- include/modbuspp/message.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/modbuspp/message.h b/include/modbuspp/message.h index b9d6b72..6936910 100644 --- a/include/modbuspp/message.h +++ b/include/modbuspp/message.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -46,7 +47,7 @@ namespace Modbus { * @return 1 if the message has been completely processed, 0 if the * message has not been processed, -1 if error. */ - typedef int (*Callback) (Message * msg, Device * sender); + using Callback = std::function; /** * @brief Constructors From d20311e745ec53784201734515cf86b9f8164a91 Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Tue, 2 Nov 2021 08:44:44 +0100 Subject: [PATCH 14/20] [data] make method Data::updateValues public --- include/modbuspp/data.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/include/modbuspp/data.h b/include/modbuspp/data.h index 2ef97a7..365a3d9 100644 --- a/include/modbuspp/data.h +++ b/include/modbuspp/data.h @@ -220,6 +220,19 @@ namespace Modbus { print ( (const uint8_t *) m_registers.data(), size()); } + // update data value from MODBUS registers + // to call after reading the modbus registers + void updateValue() { + T v; + + for (auto & r : m_registers) { + r = hton (r); + } + std::memcpy (&v, m_registers.data(), sizeof (T)); + swap (v); + m_value = ntoh (v); + } + friend class Slave; friend class BufferedSlave; friend class Message; @@ -242,18 +255,6 @@ namespace Modbus { } } - // update data value from MODBUS registers - // to call after reading the modbus registers - void updateValue() { - T v; - - for (auto & r : m_registers) { - r = hton (r); - } - std::memcpy (&v, m_registers.data(), sizeof (T)); - swap (v); - m_value = ntoh (v); - } #endif /* __DOXYGEN__ not defined */ private: From c6bfe7a2f149c9e03844f0cac9e6228cfab26251 Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Wed, 24 Nov 2021 16:03:34 +0100 Subject: [PATCH 15/20] make pointer to Private NetLayer implementation a unique_ptr, also make modbus_t a unique_ptr --- include/modbuspp/asciilayer.h | 2 +- include/modbuspp/netlayer.h | 4 ++-- include/modbuspp/rtulayer.h | 2 +- include/modbuspp/tcplayer.h | 2 +- src/asciilayer.cpp | 26 +++++++++++++------------- src/asciilayer_p.h | 1 + src/netlayer.cpp | 6 +++--- src/netlayer_p.h | 8 +++----- src/rtulayer.cpp | 27 +++++++++++++-------------- src/rtulayer_p.h | 1 + src/tcplayer.cpp | 8 ++++---- src/tcplayer_p.h | 1 + 12 files changed, 44 insertions(+), 44 deletions(-) diff --git a/include/modbuspp/asciilayer.h b/include/modbuspp/asciilayer.h index b608d40..c0393c1 100644 --- a/include/modbuspp/asciilayer.h +++ b/include/modbuspp/asciilayer.h @@ -196,7 +196,7 @@ namespace Modbus { protected: class Private; - AsciiLayer (Private &dd); + AsciiLayer (std::unique_ptr &&dd); private: PIMP_DECLARE_PRIVATE (AsciiLayer) diff --git a/include/modbuspp/netlayer.h b/include/modbuspp/netlayer.h index aaf622f..f568b3f 100644 --- a/include/modbuspp/netlayer.h +++ b/include/modbuspp/netlayer.h @@ -118,8 +118,8 @@ namespace Modbus { protected: class Private; - NetLayer (Private &dd); - std::unique_ptr d_ptr; + NetLayer (std::unique_ptr &&dd); + std::unique_ptr d_ptr; private: PIMP_DECLARE_PRIVATE (NetLayer) diff --git a/include/modbuspp/rtulayer.h b/include/modbuspp/rtulayer.h index 6634911..2f91945 100644 --- a/include/modbuspp/rtulayer.h +++ b/include/modbuspp/rtulayer.h @@ -195,7 +195,7 @@ namespace Modbus { protected: class Private; - RtuLayer (Private &dd); + RtuLayer (std::unique_ptr &&dd); private: PIMP_DECLARE_PRIVATE (RtuLayer) diff --git a/include/modbuspp/tcplayer.h b/include/modbuspp/tcplayer.h index 9f1ba03..4e4c586 100644 --- a/include/modbuspp/tcplayer.h +++ b/include/modbuspp/tcplayer.h @@ -78,7 +78,7 @@ namespace Modbus { protected: class Private; - TcpLayer (Private &dd); + TcpLayer (std::unique_ptr &&dd); private: PIMP_DECLARE_PRIVATE (TcpLayer) diff --git a/src/asciilayer.cpp b/src/asciilayer.cpp index 6364ff0..b35abec 100644 --- a/src/asciilayer.cpp +++ b/src/asciilayer.cpp @@ -32,17 +32,17 @@ namespace Modbus { // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- - AsciiLayer::AsciiLayer (AsciiLayer::Private &dd) : NetLayer (dd) {} + AsciiLayer::AsciiLayer (std::unique_ptr &&dd) : NetLayer (std::move(dd)) {} // --------------------------------------------------------------------------- AsciiLayer::AsciiLayer (const std::string & port, const std::string & settings) : - NetLayer (*new Private (port, settings)) {} + NetLayer (std::unique_ptr(new AsciiLayer::Private (port, settings))) {} // --------------------------------------------------------------------------- SerialMode AsciiLayer::serialMode() { PIMP_D (AsciiLayer); - int m = modbus_serial_get_serial_mode (d->ctx); + int m = modbus_serial_get_serial_mode (d->ctx.get()); if (m != -1) { return static_cast (m); } @@ -53,14 +53,14 @@ namespace Modbus { bool AsciiLayer::setSerialMode (SerialMode mode) { PIMP_D (AsciiLayer); - return (modbus_serial_set_serial_mode (d->ctx, static_cast (mode)) != -1); + return (modbus_serial_set_serial_mode (d->ctx.get(), static_cast (mode)) != -1); } // --------------------------------------------------------------------------- SerialRts AsciiLayer::rts() { PIMP_D (AsciiLayer); - int r = modbus_serial_get_rts (d->ctx); + int r = modbus_serial_get_rts (d->ctx.get()); if (r != -1) { return static_cast (r); @@ -72,21 +72,21 @@ namespace Modbus { bool AsciiLayer::setRts (SerialRts r) { PIMP_D (AsciiLayer); - return (modbus_serial_set_rts (d->ctx, static_cast (r)) != -1); + return (modbus_serial_set_rts (d->ctx.get(), static_cast (r)) != -1); } // --------------------------------------------------------------------------- int AsciiLayer::rtsDelay() { PIMP_D (AsciiLayer); - return modbus_serial_get_rts_delay (d->ctx); + return modbus_serial_get_rts_delay (d->ctx.get()); } // --------------------------------------------------------------------------- bool AsciiLayer::setRtsDelay (int delay) { PIMP_D (AsciiLayer); - return (modbus_serial_set_rts_delay (d->ctx, delay) != -1); + return (modbus_serial_set_rts_delay (d->ctx.get(), delay) != -1); } // --------------------------------------------------------------------------- @@ -131,7 +131,7 @@ namespace Modbus { setRts (r); usleep (rtsDelay()); - size = write (modbus_get_socket (d->ctx), msg->adu(), msg->aduSize()); + size = write (modbus_get_socket (d->ctx.get()), msg->adu(), msg->aduSize()); usleep (d->oneByteTime * msg->aduSize() + rtsDelay()); setRts ( (r == RtsDown) ? RtsUp : RtsDown); // restore initial state @@ -140,7 +140,7 @@ namespace Modbus { } else { #endif - return write (modbus_get_socket (d->ctx), msg->adu(), msg->aduSize()); + return write (modbus_get_socket (d->ctx.get()), msg->adu(), msg->aduSize()); #if MODBUSPP_HAVE_TIOCM_RTS } #endif @@ -265,9 +265,9 @@ static char nibble_to_hex_ascii(uint8_t nibble) NetLayer::Private (Ascii, port, settings, MODBUS_ASCII_MAX_ADU_LENGTH) { // RTU MUST BE 8-bits - ctx = modbus_new_ascii (port.c_str(), AsciiLayer::baud (settings), + ctx.reset( modbus_new_ascii (port.c_str(), AsciiLayer::baud (settings), AsciiLayer::parity (settings), 8, - AsciiLayer::stop (settings)); + AsciiLayer::stop (settings)) ); if (! ctx) { @@ -275,7 +275,7 @@ static char nibble_to_hex_ascii(uint8_t nibble) "Unable to create ASCII Modbus Backend(" + port + "," + settings + ")\n" + lastError()); } - oneByteTime = modbus_serial_get_rts_delay (ctx); + oneByteTime = modbus_serial_get_rts_delay (ctx.get()); } } diff --git a/src/asciilayer_p.h b/src/asciilayer_p.h index d395f7e..d406252 100644 --- a/src/asciilayer_p.h +++ b/src/asciilayer_p.h @@ -25,6 +25,7 @@ namespace Modbus { public: Private (const std::string & port, const std::string & settings); + int oneByteTime; }; } diff --git a/src/netlayer.cpp b/src/netlayer.cpp index aa2e87e..f21a7e5 100644 --- a/src/netlayer.cpp +++ b/src/netlayer.cpp @@ -26,7 +26,7 @@ namespace Modbus { // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- - NetLayer::NetLayer (NetLayer::Private &dd) : d_ptr (&dd) {} + NetLayer::NetLayer (std::unique_ptr &&dd) : d_ptr (std::move(dd)) {} // --------------------------------------------------------------------------- NetLayer::NetLayer () : @@ -53,14 +53,14 @@ namespace Modbus { modbus_t * NetLayer::context() { PIMP_D (NetLayer); - return d->ctx; + return d->ctx.get(); } // --------------------------------------------------------------------------- const modbus_t * NetLayer::context() const { PIMP_D (const NetLayer); - return d->ctx; + return d->ctx.get(); } // --------------------------------------------------------------------------- diff --git a/src/netlayer_p.h b/src/netlayer_p.h index dc510e3..a8a87d9 100644 --- a/src/netlayer_p.h +++ b/src/netlayer_p.h @@ -23,16 +23,14 @@ namespace Modbus { class NetLayer::Private { public: Private (Net n, const std::string & c, const std::string & s, uint16_t m) : - ctx (0), net (n), connection (c), settings (s), maxAduLength (m) {} - virtual ~Private() { - modbus_free (ctx); - } + net (n), connection (c), settings (s), maxAduLength (m) {} - modbus_t * ctx; Net net; std::string connection; std::string settings; uint16_t maxAduLength; + std::unique_ptr ctx = + {nullptr, [](modbus_t* p){ modbus_free(p); }}; }; } diff --git a/src/rtulayer.cpp b/src/rtulayer.cpp index 6785c4c..a51266b 100644 --- a/src/rtulayer.cpp +++ b/src/rtulayer.cpp @@ -94,17 +94,17 @@ namespace Modbus { // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- - RtuLayer::RtuLayer (RtuLayer::Private &dd) : NetLayer (dd) {} + RtuLayer::RtuLayer (std::unique_ptr &&dd) : NetLayer (std::move(dd)) {} // --------------------------------------------------------------------------- RtuLayer::RtuLayer (const std::string & port, const std::string & settings) : - NetLayer (*new Private (port, settings)) {} + NetLayer (std::unique_ptr(new Private(port, settings))) {} // --------------------------------------------------------------------------- SerialMode RtuLayer::serialMode() { PIMP_D (RtuLayer); - int m = modbus_serial_get_serial_mode (d->ctx); + int m = modbus_serial_get_serial_mode (d->ctx.get()); if (m != -1) { return static_cast (m); } @@ -115,14 +115,14 @@ namespace Modbus { bool RtuLayer::setSerialMode (SerialMode mode) { PIMP_D (RtuLayer); - return (modbus_serial_set_serial_mode (d->ctx, static_cast (mode)) != -1); + return (modbus_serial_set_serial_mode (d->ctx.get(), static_cast (mode)) != -1); } // --------------------------------------------------------------------------- SerialRts RtuLayer::rts() { PIMP_D (RtuLayer); - int r = modbus_serial_get_rts (d->ctx); + int r = modbus_serial_get_rts (d->ctx.get()); if (r != -1) { return static_cast (r); @@ -134,21 +134,21 @@ namespace Modbus { bool RtuLayer::setRts (SerialRts r) { PIMP_D (RtuLayer); - return (modbus_serial_set_rts (d->ctx, static_cast (r)) != -1); + return (modbus_serial_set_rts (d->ctx.get(), static_cast (r)) != -1); } // --------------------------------------------------------------------------- int RtuLayer::rtsDelay() { PIMP_D (RtuLayer); - return modbus_serial_get_rts_delay (d->ctx); + return modbus_serial_get_rts_delay (d->ctx.get()); } // --------------------------------------------------------------------------- bool RtuLayer::setRtsDelay (int delay) { PIMP_D (RtuLayer); - return (modbus_serial_set_rts_delay (d->ctx, delay) != -1); + return (modbus_serial_set_rts_delay (d->ctx.get(), delay) != -1); } // --------------------------------------------------------------------------- @@ -193,7 +193,7 @@ namespace Modbus { setRts (r); usleep (rtsDelay()); - size = write (modbus_get_socket (d->ctx), msg->adu(), msg->aduSize()); + size = write (modbus_get_socket (d->ctx.get()), msg->adu(), msg->aduSize()); usleep (d->oneByteTime * msg->aduSize() + rtsDelay()); setRts ( (r == RtsDown) ? RtsUp : RtsDown); // restore initial state @@ -202,7 +202,7 @@ namespace Modbus { } else { #endif - return write (modbus_get_socket (d->ctx), msg->adu(), msg->aduSize()); + return write (modbus_get_socket (d->ctx.get()), msg->adu(), msg->aduSize()); #if MODBUSPP_HAVE_TIOCM_RTS } #endif @@ -307,16 +307,15 @@ namespace Modbus { NetLayer::Private (Rtu, port, settings, MODBUS_RTU_MAX_ADU_LENGTH) { // RTU MUST BE 8-bits - ctx = modbus_new_rtu (port.c_str(), RtuLayer::baud (settings), + ctx.reset( modbus_new_rtu (port.c_str(), RtuLayer::baud (settings), RtuLayer::parity (settings), 8, - RtuLayer::stop (settings)); + RtuLayer::stop (settings)) ); if (! ctx) { - throw std::invalid_argument ( "Unable to create RTU Modbus Backend(" + port + "," + settings + ")\n" + lastError()); } - oneByteTime = modbus_serial_get_rts_delay (ctx); + oneByteTime = modbus_serial_get_rts_delay (ctx.get()); } } diff --git a/src/rtulayer_p.h b/src/rtulayer_p.h index 087258a..f80bb9d 100644 --- a/src/rtulayer_p.h +++ b/src/rtulayer_p.h @@ -25,6 +25,7 @@ namespace Modbus { public: Private (const std::string & port, const std::string & settings); + int oneByteTime; }; } diff --git a/src/tcplayer.cpp b/src/tcplayer.cpp index 14a4865..e113ce9 100644 --- a/src/tcplayer.cpp +++ b/src/tcplayer.cpp @@ -37,11 +37,11 @@ namespace Modbus { // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- - TcpLayer::TcpLayer (TcpLayer::Private &dd) : NetLayer (dd) {} + TcpLayer::TcpLayer (std::unique_ptr &&dd) : NetLayer (std::move(dd)) {} // --------------------------------------------------------------------------- TcpLayer::TcpLayer (const std::string & host, const std::string & service) : - NetLayer (*new Private (host, service)) {} + NetLayer (std::unique_ptr(new Private (host, service))) {} // --------------------------------------------------------------------------- const std::string & TcpLayer::node() const { @@ -59,7 +59,7 @@ namespace Modbus { int TcpLayer::sendRawMessage (const Message * msg) { PIMP_D (TcpLayer); - return send (modbus_get_socket (d->ctx), msg->adu(), msg->aduSize(), + return send (modbus_get_socket (d->ctx.get()), msg->adu(), msg->aduSize(), MSG_NOSIGNAL); } @@ -107,7 +107,7 @@ namespace Modbus { node = host.c_str(); } - ctx = modbus_new_tcp_pi (node, service.c_str()); + ctx.reset( modbus_new_tcp_pi (node, service.c_str()) ); if (! ctx) { throw std::invalid_argument ( diff --git a/src/tcplayer_p.h b/src/tcplayer_p.h index a42b47d..0aa28fe 100644 --- a/src/tcplayer_p.h +++ b/src/tcplayer_p.h @@ -25,6 +25,7 @@ namespace Modbus { public: Private (const std::string & host, const std::string & service); + uint16_t transactionId; }; } From 9e679cdce7a0d3ba6aa6869ab091b17044ccbc50 Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Wed, 24 Nov 2021 16:21:32 +0100 Subject: [PATCH 16/20] make pointer to Private Device implementation a unique_ptr --- include/modbuspp/device.h | 4 ++-- include/modbuspp/master.h | 2 +- include/modbuspp/router.h | 2 +- include/modbuspp/server.h | 4 ++-- src/device.cpp | 26 ++++++++++---------------- src/device_p.h | 17 ++++++++++++----- src/master.cpp | 7 ++----- src/master_p.h | 11 ++++++----- src/message.cpp | 15 +++++++-------- src/message_p.h | 6 +++--- src/request.cpp | 3 --- src/request_p.h | 2 +- src/response.cpp | 2 -- src/response_p.h | 3 ++- src/router.cpp | 7 ++----- src/router_p.h | 1 - src/server.cpp | 4 ++-- src/slave.cpp | 5 ----- src/slave_p.h | 1 - 19 files changed, 53 insertions(+), 69 deletions(-) diff --git a/include/modbuspp/device.h b/include/modbuspp/device.h index 9883177..6379d60 100644 --- a/include/modbuspp/device.h +++ b/include/modbuspp/device.h @@ -555,8 +555,8 @@ namespace Modbus { protected: class Private; - Device (Private &dd); - std::unique_ptr d_ptr; + Device (std::unique_ptr &&dd); + std::unique_ptr d_ptr; private: PIMP_DECLARE_PRIVATE (Device) diff --git a/include/modbuspp/master.h b/include/modbuspp/master.h index 0699ee0..f8053e3 100644 --- a/include/modbuspp/master.h +++ b/include/modbuspp/master.h @@ -259,7 +259,7 @@ namespace Modbus { protected: class Private; - Master (Private &dd); + Master (std::unique_ptr &&dd); private: PIMP_DECLARE_PRIVATE (Master) diff --git a/include/modbuspp/router.h b/include/modbuspp/router.h index f76ce7a..38ff60d 100644 --- a/include/modbuspp/router.h +++ b/include/modbuspp/router.h @@ -276,7 +276,7 @@ namespace Modbus { protected: class Private; - Router (Private &dd); + Router (std::unique_ptr &&dd); private: PIMP_DECLARE_PRIVATE (Router) diff --git a/include/modbuspp/server.h b/include/modbuspp/server.h index d935c85..b4f4c15 100644 --- a/include/modbuspp/server.h +++ b/include/modbuspp/server.h @@ -170,7 +170,7 @@ namespace Modbus { /** * @overload */ - virtual void close(); + virtual void close() override; /** * @brief Performs all server operations @@ -307,7 +307,7 @@ namespace Modbus { protected: class Private; - Server (Private &dd); + Server (std::unique_ptr &&dd); private: PIMP_DECLARE_PRIVATE (Server) diff --git a/src/device.cpp b/src/device.cpp index 0864df8..911bf68 100644 --- a/src/device.cpp +++ b/src/device.cpp @@ -39,7 +39,7 @@ namespace Modbus { // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- - Device::Device (Device::Private &dd) : d_ptr (&dd) {} + Device::Device (std::unique_ptr &&dd) : d_ptr (std::move(dd)) {} // --------------------------------------------------------------------------- Device::Device () : d_ptr (new Private (this)) {} @@ -198,7 +198,7 @@ namespace Modbus { if (net() == Rtu) { PIMP_D (Device); - return * reinterpret_cast (d->backend); + return * reinterpret_cast (d->backend.get()); } throw std::domain_error ("Unable to return RTU layer !"); } @@ -209,7 +209,7 @@ namespace Modbus { if (net() == Ascii) { PIMP_D (Device); - return * reinterpret_cast (d->backend); + return * reinterpret_cast (d->backend.get()); } throw std::domain_error ("Unable to return ASCII layer !"); } @@ -220,7 +220,7 @@ namespace Modbus { if (net() == Tcp) { PIMP_D (Device); - return * reinterpret_cast (d->backend); + return * reinterpret_cast (d->backend.get()); } throw std::domain_error ("Unable to return TCP layer !"); } @@ -419,7 +419,7 @@ namespace Modbus { } while (d->recoveryLink && rc == -1 && !msg->isResponse()); - if (rc > 0 && rc != msg->size()) { + if (rc > 0 && static_cast(rc) != msg->aduSize()) { errno = EMBBADDATA; return -1; @@ -451,15 +451,9 @@ namespace Modbus { // --------------------------------------------------------------------------- Device::Private::Private (Device * q) : - q_ptr (q), isOpen (false), backend (0), recoveryLink (false), + q_ptr (q), isOpen (false), backend (nullptr), recoveryLink (false), debug (false) {} - // --------------------------------------------------------------------------- - Device::Private::~Private() { - - delete backend; - } - // --------------------------------------------------------------------------- void Device::Private::setConfigFromFile (const std::string & jsonfile, const std::string & key) { @@ -513,15 +507,15 @@ namespace Modbus { switch (net) { case Tcp: - backend = new TcpLayer (connection, settings); + backend = std::unique_ptr{new TcpLayer (connection, settings)}; break; case Rtu: - backend = new RtuLayer (connection, settings); + backend = std::unique_ptr{new RtuLayer (connection, settings)}; break; case Ascii: - backend = new AsciiLayer (connection, settings); + backend = std::unique_ptr{new AsciiLayer (connection, settings)}; break; default: @@ -555,7 +549,7 @@ namespace Modbus { // --------------------------------------------------------------------------- int Device::Private::defaultSlave (int addr) const { - if (addr < 0 && backend != 0) { + if (addr < 0 && backend != nullptr) { Net n = backend->net(); return (n == Rtu || n == Ascii) ? Broadcast : TcpSlave; } diff --git a/src/device_p.h b/src/device_p.h index bf090fb..1685b3c 100644 --- a/src/device_p.h +++ b/src/device_p.h @@ -28,7 +28,6 @@ namespace Modbus { public: Private (Device * q); - virtual ~Private(); virtual void setBackend (Net net, const std::string & connection, const std::string & settings); virtual void setConfig (const nlohmann::json & config); @@ -39,18 +38,26 @@ namespace Modbus { virtual bool open(); virtual void close(); inline modbus_t * ctx() { - return backend->context(); + if ( backend != nullptr ) { + return backend->context(); + } else { + return nullptr; + } } inline modbus_t * ctx() const { - return backend->context(); + if ( backend != nullptr ) { + return backend->context(); + } else { + return nullptr; + } } int defaultSlave (int addr) const; bool isConnected () const; void printError (const char * what = nullptr) const; - Device * const q_ptr; + Device * const q_ptr = nullptr; bool isOpen; - NetLayer * backend; + std::unique_ptr backend = nullptr; bool recoveryLink; bool debug; diff --git a/src/master.cpp b/src/master.cpp index 990f885..b9cd862 100644 --- a/src/master.cpp +++ b/src/master.cpp @@ -28,10 +28,10 @@ namespace Modbus { // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- - Master::Master (Master::Private &dd) : Device (dd) {} + Master::Master (std::unique_ptr &&dd) : Device (std::move(dd)) {} // --------------------------------------------------------------------------- - Master::Master () : Device (*new Private (this)) {} + Master::Master () : Device (std::unique_ptr(new Private (this))) {} // --------------------------------------------------------------------------- Master::Master (Net net, const std::string & connection, @@ -185,9 +185,6 @@ namespace Modbus { // --------------------------------------------------------------------------- Master::Private::Private (Master * q) : Device::Private (q) {} - // --------------------------------------------------------------------------- - Master::Private::~Private() = default; - // --------------------------------------------------------------------------- // virtual void Master::Private::setBackend (Net net, const std::string & connection, diff --git a/src/master_p.h b/src/master_p.h index 1452222..6b3ba2e 100644 --- a/src/master_p.h +++ b/src/master_p.h @@ -26,12 +26,12 @@ namespace Modbus { public: Private (Master * q); - virtual ~Private(); - virtual void setBackend (Net net, const std::string & connection, - const std::string & settings); - virtual void setConfig (const nlohmann::json & config); - virtual Slave * addSlave (int slaveAddr); + void setBackend (Net net, const std::string & connection, + const std::string & settings) override; + void setConfig (const nlohmann::json & config) override; + + Slave * addSlave (int slaveAddr); std::map > slave; PIMP_DECLARE_PUBLIC (Master) @@ -39,3 +39,4 @@ namespace Modbus { } /* ========================================================================== */ + diff --git a/src/message.cpp b/src/message.cpp index ee9bc6f..2a2bd32 100644 --- a/src/message.cpp +++ b/src/message.cpp @@ -512,19 +512,22 @@ namespace Modbus { // --------------------------------------------------------------------------- Message::Private::Private (Message * q, Net n) : - q_ptr (q), net (n), aduSize (0), isResponse (false), backend (0), + q_ptr (q), net (n), aduSize (0), isResponse (false), backend (nullptr), transactionId (1) { - NetLayer * b; + std::unique_ptr b = nullptr; switch (net) { case Tcp: - b = new TcpLayer ("*", "1502"); + b = std::unique_ptr (new TcpLayer{"*", "1502"} ); break; case Rtu: + b = std::unique_ptr (new RtuLayer{"*", "1502"} ); + break; + case Ascii: - b = new RtuLayer ("COM1", "9600E1"); + b = std::unique_ptr (new AsciiLayer{"*", "1502"} ); break; default: @@ -535,7 +538,6 @@ namespace Modbus { pduBegin = modbus_get_header_length (b->context()); maxAduLength = b->maxAduLength(); - delete b; adu.resize (maxAduLength, 0); if (net == Tcp) { adu[6] = MODBUS_TCP_SLAVE; @@ -556,9 +558,6 @@ namespace Modbus { adu[pduBegin] = static_cast (func); } - - // --------------------------------------------------------------------------- - Message::Private::~Private() = default; } /* ========================================================================== */ diff --git a/src/message_p.h b/src/message_p.h index ca41bf3..0fe14a7 100644 --- a/src/message_p.h +++ b/src/message_p.h @@ -28,15 +28,15 @@ namespace Modbus { Private (Message * q, Net n); Private (Message * q, Net n, const std::vector & m); Private (Message * q, Net n, Function f); - virtual ~Private(); + virtual ~Private() = default; - Message * const q_ptr; + Message * const q_ptr = nullptr; Net net; int pduBegin; size_t aduSize; uint16_t maxAduLength; bool isResponse; - NetLayer * backend; + NetLayer * backend = nullptr; uint16_t transactionId; std::vector adu; }; diff --git a/src/request.cpp b/src/request.cpp index 2898856..2dc240e 100644 --- a/src/request.cpp +++ b/src/request.cpp @@ -220,9 +220,6 @@ namespace Modbus { Request::Private::Private (Request * q, Net n, Function f) : Message::Private (q, n, f) {} - // --------------------------------------------------------------------------- - Request::Private::~Private() = default; - } /* ========================================================================== */ diff --git a/src/request_p.h b/src/request_p.h index b160083..06374a1 100644 --- a/src/request_p.h +++ b/src/request_p.h @@ -29,7 +29,7 @@ namespace Modbus { Private (Request * q, Net n); Private (Request * q, Net n, const std::vector & m); Private (Request * q, Net n, Function f); - virtual ~Private(); + virtual ~Private() = default; PIMP_DECLARE_PUBLIC (Request) }; } diff --git a/src/response.cpp b/src/response.cpp index 8cd20be..bcf1aa4 100644 --- a/src/response.cpp +++ b/src/response.cpp @@ -209,8 +209,6 @@ namespace Modbus { Response::Private::Private (Response * q, NetLayer * b, Function f) : Message::Private (q, b, f) {} - // --------------------------------------------------------------------------- - Response::Private::~Private() = default; } diff --git a/src/response_p.h b/src/response_p.h index f53c51d..bb49afd 100644 --- a/src/response_p.h +++ b/src/response_p.h @@ -26,7 +26,8 @@ namespace Modbus { Private (Response * q, NetLayer * b); Private (Response * q, NetLayer * b, const std::vector & m); Private (Response * q, NetLayer * b, Function f); - virtual ~Private(); + virtual ~Private() = default; + PIMP_DECLARE_PUBLIC (Response) }; } diff --git a/src/router.cpp b/src/router.cpp index 0f197a1..db5ad59 100644 --- a/src/router.cpp +++ b/src/router.cpp @@ -28,10 +28,10 @@ namespace Modbus { // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- - Router::Router (Router::Private &dd) : Server (dd) {} + Router::Router (std::unique_ptr &&dd) : Server (std::move(dd)) {} // --------------------------------------------------------------------------- - Router::Router () : Server (*new Private (this)) {} + Router::Router () : Server (std::unique_ptr(new Private (this))) {} // --------------------------------------------------------------------------- Router::Router (Net net, const std::string & connection, @@ -152,9 +152,6 @@ namespace Modbus { // --------------------------------------------------------------------------- Router::Private::Private (Router * q) : Server::Private (q) {} - // --------------------------------------------------------------------------- - Router::Private::~Private() = default; - // --------------------------------------------------------------------------- // virtual void Router::Private::setConfig (const nlohmann::json & config) { diff --git a/src/router_p.h b/src/router_p.h index 00947eb..31197a2 100644 --- a/src/router_p.h +++ b/src/router_p.h @@ -25,7 +25,6 @@ namespace Modbus { public: Private (Router * q); - virtual ~Private(); virtual void setConfig (const nlohmann::json & config); virtual bool open(); diff --git a/src/server.cpp b/src/server.cpp index 2d2e6a6..faa1170 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -42,11 +42,11 @@ namespace Modbus { // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- - Server::Server (Server::Private &dd) : Device (dd) {} + Server::Server (std::unique_ptr &&dd) : Device (std::move(dd)) {} // --------------------------------------------------------------------------- Server::Server () : - Device (*new Private (this)) {} + Device (std::unique_ptr(new Private (this))) {} // --------------------------------------------------------------------------- Server::Server (Net net, const std::string & connection, diff --git a/src/slave.cpp b/src/slave.cpp index 1cdf2ed..0fbd5b6 100644 --- a/src/slave.cpp +++ b/src/slave.cpp @@ -35,8 +35,6 @@ namespace Modbus { Slave::Slave (int slaveAddr, Device * dev) : d_ptr (new Private (this, slaveAddr, dev)) {} - // --------------------------------------------------------------------------- - Slave::~Slave() = default; // --------------------------------------------------------------------------- int Slave::number() const { @@ -293,9 +291,6 @@ namespace Modbus { dev = d; } - // --------------------------------------------------------------------------- - Slave::Private::~Private() = default; - // --------------------------------------------------------------------------- // // Modbus::Json Namespace diff --git a/src/slave_p.h b/src/slave_p.h index d6f5071..736f303 100644 --- a/src/slave_p.h +++ b/src/slave_p.h @@ -28,7 +28,6 @@ namespace Modbus { public: Private (Slave * q); Private (Slave * q, int s, Device * d); - virtual ~Private(); inline modbus_t * ctx() { return dev->backend().context(); From 61cc8d20cf41bb56d4b6d1e3c6c9b1011bf2c8eb Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Wed, 24 Nov 2021 16:24:20 +0100 Subject: [PATCH 17/20] enable tcp server to handle multiple client connections --- CMakeLists.txt | 2 +- include/modbuspp/slave.h | 2 +- src/server.cpp | 159 +++++++++++++++++++++++++++------------ src/server_p.h | 8 +- 4 files changed, 118 insertions(+), 53 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b75209d..8108664 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,7 +72,7 @@ set(CMAKE_THREAD_PREFER_PTHREAD TRUE) set(THREADS_PREFER_PTHREAD_FLAG TRUE) find_package(Threads REQUIRED) -set(LIBMODBUS_MIN_VERSION "3.1.8") +set(LIBMODBUS_MIN_VERSION "3.1.10") find_package(PkgConfig REQUIRED) pkg_check_modules (LIBMODBUS REQUIRED libmodbusepsi>=${LIBMODBUS_MIN_VERSION}) diff --git a/include/modbuspp/slave.h b/include/modbuspp/slave.h index 4774774..938cd1e 100644 --- a/include/modbuspp/slave.h +++ b/include/modbuspp/slave.h @@ -54,7 +54,7 @@ namespace Modbus { * The destructor closes the connection if it is open and releases all * affected resources. */ - virtual ~Slave(); + virtual ~Slave() = default; /** * @brief Get slave number diff --git a/src/server.cpp b/src/server.cpp index faa1170..60558ba 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -83,24 +83,15 @@ namespace Modbus { // --------------------------------------------------------------------------- int Server::poll (long timeout) { + if(isRunning() || not isOpen()){ + // cann not proceed if the `loop()` is running + // OR connection is not established + return -1; + } - if (!isRunning() && isOpen()) { - PIMP_D (Server); - - if (!d->receiveTask.valid()) { - // starts the receiving thread - d->receiveTask = std::async (std::launch::async, Server::Private::receive, d); - } - - if (d->receiveTask.wait_for (std::chrono::milliseconds (timeout)) == std::future_status::ready) { + PIMP_D (Server); + return d->poll(timeout); - // message received ? - int rc = d->receiveTask.get(); - return d->task (rc); - } - return 0; - } - return -1; } // --------------------------------------------------------------------------- @@ -184,9 +175,9 @@ namespace Modbus { void Server::terminate () { PIMP_D (Server); - if (d->sock != -1) { - - ::shutdown (d->sock, SHUT_RDWR); + if (d->listen_sock != -1) { + ::shutdown (d->listen_sock, SHUT_RDWR); + ::close(d->listen_sock); } if (isRunning()) { @@ -196,6 +187,7 @@ namespace Modbus { // Wait for thread to join d->daemon.join(); } + } // --------------------------------------------------------------------------- @@ -234,10 +226,10 @@ namespace Modbus { // --------------------------------------------------------------------------- Server::Private::Private (Server * q) : - Device::Private (q), sock (-1), req (0) {} + Device::Private (q), listen_sock (-1), req (0) { + all_pollfds.reserve(MAX_CONNECTIONS +1); + } - // --------------------------------------------------------------------------- - Server::Private::~Private() = default; // --------------------------------------------------------------------------- // virtual @@ -292,14 +284,23 @@ namespace Modbus { switch (backend->net()) { case Tcp: - sock = modbus_tcp_pi_listen (ctx(), 1); - isOk = (sock != -1); + listen_sock = modbus_tcp_pi_listen (ctx(), MAX_CONNECTIONS); + if ( listen_sock != -1 ) { + isOk = true; + all_pollfds.push_back(pollfd{.fd=listen_sock, .events=POLL_IN, .revents=0}); + } break; case Rtu: - case Ascii: - isOk = Device::Private::open(); - + case Ascii: { + if ( Device::Private::open() ) { + isOk = true; + int listen_sock = modbus_get_socket(ctx()); + all_pollfds.push_back(pollfd{.fd=listen_sock, .events=POLL_IN, .revents=0}); + } else { + std::cout << "fd after open: " << listen_sock << "\n"; + } + } default: break; } @@ -315,18 +316,16 @@ namespace Modbus { // --------------------------------------------------------------------------- void Server::Private::close() { - std::lock_guard lg (d_guard); - + std::lock_guard lg (d_guard); if (backend->net() == Tcp) { - - if (sock != -1) { - + if (listen_sock != -1) { #ifdef _WIN32 - ::closesocket (sock); + ::closesocket (listen_sock); #else - ::close (sock); + ::close (listen_sock); #endif - sock = -1; + all_pollfds.clear(); + listen_sock = -1; } } Device::Private::close(); @@ -393,9 +392,7 @@ namespace Modbus { rc = 0; } else { - if (messageCB) { - rc = messageCB (req.get(), q); } } @@ -403,25 +400,91 @@ namespace Modbus { return rc; } - // --------------------------------------------------------------------------- - // static - int Server::Private::receive (Private * d) { - std::lock_guard lg (d->d_guard); - int rc; - if ( (d->backend->net() == Tcp) && !d->isConnected()) { + int Server::Private::poll(int timeout) + { + int eventCount = ::poll(all_pollfds.data(), all_pollfds.size(), timeout); - // accept blocking call ! - if (modbus_tcp_pi_accept (d->ctx(), &d->sock) < 0) { + if ( eventCount == 0) { + // there is nothing to process + return 0; + } + if ( eventCount < 0 ) { + // handle error return -1; + } + + // handle events + std::vector new_pfds{}; + + for ( pollfd& pfd : all_pollfds ) { + + if ( pfd.revents & POLLIN ) { + + if ( backend->net() == Net::Tcp && pfd.fd == listen_sock ) { + // if there is an event on the 'listening' socket + // handle incomming connection request aka `connect()` + // but only Server::Private::MAX_CONNECTIONS + if ( all_pollfds.size() < MAX_CONNECTIONS ) { + int new_socket = modbus_tcp_accept(ctx(), &listen_sock); + if ( new_socket != -1 ) { + new_pfds.push_back(pollfd{ + .fd=new_socket, + .events=POLL_IN, + .revents=0}); + } + } + } else { + // handle incomming request + modbus_set_socket(ctx(), pfd.fd); + int rc = Server::Private::receive(this); + if ( rc == -1 ) { + // if receive fails after a successfull poll + // probably the connection is broken + ::close(pfd.fd); + pfd.fd = -1; + continue; + } + + task(rc); + } + } else if ( pfd.revents & POLLHUP ) { + ::close(pfd.fd); + pfd.fd = -1; } } + + // remove bad file descriptors from watch list + auto badFds = std::remove_if(all_pollfds.begin(), all_pollfds.end(), [](const pollfd& pfd) { + return (pfd.revents & (POLLERR|POLLHUP|POLLNVAL)) || (pfd.fd == -1); + }); + all_pollfds.erase(badFds, all_pollfds.end()); + + if ( all_pollfds.empty() ) { + return -1; + } + + // add new accepted file descriptors to watch list + if ( not new_pfds.empty() ) { + all_pollfds.insert(all_pollfds.end(), new_pfds.begin(), new_pfds.end()); + } + + return 0; + } + + // --------------------------------------------------------------------------- + // static + int Server::Private::receive (Private * d) { + std::lock_guard lg (d->d_guard); + + int rc = 0; + d->req->clear(); rc = modbus_receive (d->ctx(), d->req->adu()); - if (rc > 0) { - - d->req->setAduSize (rc); + if ( rc > 0 ) { + d->req->setAduSize(rc); } + return rc; } diff --git a/src/server_p.h b/src/server_p.h index e17dfd4..e1fe1c7 100644 --- a/src/server_p.h +++ b/src/server_p.h @@ -16,6 +16,7 @@ */ #pragma once +#include #include #include #include @@ -26,9 +27,9 @@ namespace Modbus { class Server::Private : public Device::Private { + static const int MAX_CONNECTIONS = 16; public: Private (Server * q); - virtual ~Private(); virtual void setBackend (Net net, const std::string & connection, const std::string & settings); virtual void setConfig (const nlohmann::json & config); @@ -36,16 +37,17 @@ namespace Modbus { virtual bool open(); virtual void close(); int task (int rc); + int poll (int timeout); BufferedSlave * addSlave (int slaveAddr, Device * master); static void * loop (std::future run, Private * d); static int receive (Private * d); - int sock; + int listen_sock = -1; + std::vector all_pollfds {}; std::shared_ptr req; std::map > slave; - std::future receiveTask; std::thread daemon; std::promise stopDaemon; Message::Callback messageCB; From c00c7d9ba326d2a932def4591fe975bdc93cf70b Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Tue, 11 Jan 2022 09:55:42 +0100 Subject: [PATCH 18/20] set working dir of GitVersion to source dir; this helps with out of source builds --- dev/cmake/GitVersion.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev/cmake/GitVersion.cmake b/dev/cmake/GitVersion.cmake index 9490997..a85d2ae 100644 --- a/dev/cmake/GitVersion.cmake +++ b/dev/cmake/GitVersion.cmake @@ -36,7 +36,9 @@ function(GetGitVersion _prefix) if(GIT_FOUND) execute_process(COMMAND ${GIT_EXECUTABLE} describe - RESULT_VARIABLE ret OUTPUT_VARIABLE str OUTPUT_STRIP_TRAILING_WHITESPACE + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE ret + OUTPUT_VARIABLE str OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) endif() From a81892c04bdee2bf98806b7ff9ee5ac1aa9e5ed3 Mon Sep 17 00:00:00 2001 From: Karl Herbig Date: Thu, 3 Feb 2022 11:16:59 +0100 Subject: [PATCH 19/20] [server] allow access to file descriptors, so the user can handle multiple instances with a single poll --- include/modbuspp/server.h | 4 ++++ src/server.cpp | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/include/modbuspp/server.h b/include/modbuspp/server.h index b4f4c15..2abdefc 100644 --- a/include/modbuspp/server.h +++ b/include/modbuspp/server.h @@ -18,6 +18,7 @@ #include #include +#include namespace Modbus { @@ -167,6 +168,9 @@ namespace Modbus { */ virtual ~Server(); + + virtual std::vector fds(); + /** * @overload */ diff --git a/src/server.cpp b/src/server.cpp index 60558ba..853ab26 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -218,6 +218,12 @@ namespace Modbus { d->messageCB = cb; } + std::vector Server::fds() { + PIMP_D(Server); + + return d->all_pollfds; + } + // --------------------------------------------------------------------------- // // Server::Private Class From 8125121d533020a6cac52b64f54a15133816200f Mon Sep 17 00:00:00 2001 From: Dennis Purdack Date: Thu, 11 Sep 2025 14:23:19 +0200 Subject: [PATCH 20/20] re-added virtual destructors to prevent memory leaks --- include/modbuspp/asciilayer.h | 5 +++++ src/asciilayer_p.h | 1 + src/device_p.h | 1 + src/master_p.h | 2 +- src/netlayer_p.h | 1 + src/router_p.h | 1 + src/rtulayer_p.h | 1 + src/server.cpp | 2 +- src/server_p.h | 3 ++- src/slave_p.h | 1 + src/tcplayer_p.h | 1 + 11 files changed, 16 insertions(+), 3 deletions(-) diff --git a/include/modbuspp/asciilayer.h b/include/modbuspp/asciilayer.h index c0393c1..c5094d8 100644 --- a/include/modbuspp/asciilayer.h +++ b/include/modbuspp/asciilayer.h @@ -46,6 +46,11 @@ namespace Modbus { */ AsciiLayer (const std::string & port, const std::string & settings); + /** + * @brief Destructor + */ + virtual ~AsciiLayer() = default; + /** * @brief Name of the serial port * diff --git a/src/asciilayer_p.h b/src/asciilayer_p.h index d406252..aa73813 100644 --- a/src/asciilayer_p.h +++ b/src/asciilayer_p.h @@ -25,6 +25,7 @@ namespace Modbus { public: Private (const std::string & port, const std::string & settings); + virtual ~Private() = default; int oneByteTime; }; diff --git a/src/device_p.h b/src/device_p.h index 1685b3c..bf58f86 100644 --- a/src/device_p.h +++ b/src/device_p.h @@ -28,6 +28,7 @@ namespace Modbus { public: Private (Device * q); + virtual ~Private() = default; virtual void setBackend (Net net, const std::string & connection, const std::string & settings); virtual void setConfig (const nlohmann::json & config); diff --git a/src/master_p.h b/src/master_p.h index 6b3ba2e..c6aa5d5 100644 --- a/src/master_p.h +++ b/src/master_p.h @@ -26,6 +26,7 @@ namespace Modbus { public: Private (Master * q); + virtual ~Private() = default; void setBackend (Net net, const std::string & connection, const std::string & settings) override; @@ -39,4 +40,3 @@ namespace Modbus { } /* ========================================================================== */ - diff --git a/src/netlayer_p.h b/src/netlayer_p.h index a8a87d9..d39cf9a 100644 --- a/src/netlayer_p.h +++ b/src/netlayer_p.h @@ -24,6 +24,7 @@ namespace Modbus { public: Private (Net n, const std::string & c, const std::string & s, uint16_t m) : net (n), connection (c), settings (s), maxAduLength (m) {} + virtual ~Private() = default; Net net; std::string connection; diff --git a/src/router_p.h b/src/router_p.h index 31197a2..3f8eccc 100644 --- a/src/router_p.h +++ b/src/router_p.h @@ -25,6 +25,7 @@ namespace Modbus { public: Private (Router * q); + virtual ~Private() = default; virtual void setConfig (const nlohmann::json & config); virtual bool open(); diff --git a/src/rtulayer_p.h b/src/rtulayer_p.h index f80bb9d..ea3d754 100644 --- a/src/rtulayer_p.h +++ b/src/rtulayer_p.h @@ -25,6 +25,7 @@ namespace Modbus { public: Private (const std::string & port, const std::string & settings); + virtual ~Private() = default; int oneByteTime; }; diff --git a/src/server.cpp b/src/server.cpp index 853ab26..840fa42 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -496,7 +496,7 @@ namespace Modbus { // --------------------------------------------------------------------------- // static - void * Server::Private::loop (std::future run, Private * d) { + void Server::Private::loop (std::future run, Private * d) { int rc; while (run.wait_for (std::chrono::milliseconds (100)) == std::future_status::timeout) { diff --git a/src/server_p.h b/src/server_p.h index e1fe1c7..0fcc824 100644 --- a/src/server_p.h +++ b/src/server_p.h @@ -30,6 +30,7 @@ namespace Modbus { static const int MAX_CONNECTIONS = 16; public: Private (Server * q); + virtual ~Private() = default; virtual void setBackend (Net net, const std::string & connection, const std::string & settings); virtual void setConfig (const nlohmann::json & config); @@ -41,7 +42,7 @@ namespace Modbus { BufferedSlave * addSlave (int slaveAddr, Device * master); - static void * loop (std::future run, Private * d); + static void loop (std::future run, Private * d); static int receive (Private * d); int listen_sock = -1; diff --git a/src/slave_p.h b/src/slave_p.h index 736f303..b3c8700 100644 --- a/src/slave_p.h +++ b/src/slave_p.h @@ -28,6 +28,7 @@ namespace Modbus { public: Private (Slave * q); Private (Slave * q, int s, Device * d); + virtual ~Private() = default; inline modbus_t * ctx() { return dev->backend().context(); diff --git a/src/tcplayer_p.h b/src/tcplayer_p.h index 0aa28fe..a3fb590 100644 --- a/src/tcplayer_p.h +++ b/src/tcplayer_p.h @@ -25,6 +25,7 @@ namespace Modbus { public: Private (const std::string & host, const std::string & service); + virtual ~Private() = default; uint16_t transactionId; };