diff --git a/CMakeLists.txt b/CMakeLists.txt index e21f3aa..9d796c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,4 +8,6 @@ include(projectheader) project_header(modbus-cpp) add_subdirectory(src) +add_subdirectory(tests) +# add_subdirectory(tools) diff --git a/src/connectionconfig.h b/src/connectionconfig.h index d9bc7df..e6bc562 100644 --- a/src/connectionconfig.h +++ b/src/connectionconfig.h @@ -7,6 +7,7 @@ #pragma once #include +#include namespace osdev { namespace components { @@ -29,27 +30,17 @@ public: NONE }; - enum class E_BAUDRATE - { - R1200 = 1200, - R2400 = 2400, - R4800 = 4800, - R9600 = 9600, - R19200 = 19200, - R38400 = 38400, - R57600 = 57600, - R76800 = 76800, - R115200 = 115200 - }; - ConnectionConfig(); // Getters and Setters void setConnectionType( E_CONNECTIONTYPE type ) { m_conType = type; } E_CONNECTIONTYPE getConnectionType() const { return m_conType; } - void setBaudRate( E_BAUDRATE baud_rate ) { m_baudRate = baud_rate; } - E_BAUDRATE getBaudRate() const { return m_baudRate; } + void setPortName(const std::string &port_name) { m_portName = port_name; } + std::string getPortName() { return m_portName; } + + void setBaudRate( speed_t baud_rate ) { m_baudRate = baud_rate; } + speed_t getBaudRate() const { return m_baudRate; } void setParity(E_PARITY parity) { m_parity = parity; } E_PARITY getParity() const { return m_parity; } @@ -70,14 +61,15 @@ public: int getFrameTimeout() const { return m_frameTimeOut; } private: - E_CONNECTIONTYPE m_conType = E_CONNECTIONTYPE::UNKNOWN; - E_BAUDRATE m_baudRate = E_BAUDRATE::R9600; - E_PARITY m_parity = E_PARITY::NONE; - int m_dataBits = 8; - int m_stopBits = 1; - std::string m_ipAddress{}; - unsigned int m_portNumber = 502; - int m_frameTimeOut = -1; + E_CONNECTIONTYPE m_conType = E_CONNECTIONTYPE::UNKNOWN; + std::string m_portName = "/dev/ttyUSB0"; + speed_t m_baudRate = B9600; + E_PARITY m_parity = E_PARITY::NONE; + int m_dataBits = 8; + int m_stopBits = 1; + std::string m_ipAddress{}; + unsigned int m_portNumber = 502; + int m_frameTimeOut = -1; }; diff --git a/src/modbusbase.h b/src/modbusbase.h index 378cf82..9b517c4 100644 --- a/src/modbusbase.h +++ b/src/modbusbase.h @@ -57,8 +57,8 @@ namespace modbus { class ModbusBase { public: - ModbusBase(); - virtual ~ModbusBase(); + ModbusBase() {} + virtual ~ModbusBase() {} // Pure virtuals. Override when inherited. virtual bool Connect() = 0; diff --git a/src/modbusrtu.cpp b/src/modbusrtu.cpp index 46376c0..69aa03e 100644 --- a/src/modbusrtu.cpp +++ b/src/modbusrtu.cpp @@ -4,3 +4,96 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ + +#include "modbusrtu.h" + +#include + +// Linux headers +#include // Contains file controls like O_RDWR +#include // Error integer and strerror() function +#include // Contains POSIX terminal control definitions +#include // write(), read(), close() + +using namespace osdev::components::modbus; + +ModbusRtu::ModbusRtu( const ConnectionConfig &conf ) + : m_conConfig( conf ) + , m_socket( -1 ) +{ +} + +bool ModbusRtu::Connect() +{ + m_socket = open( m_conConfig.getPortName().c_str(), O_RDWR); + if(m_socket == -1) + { + return false; + } + + struct termios l_tty; + + // Read in existing settings, and handle any error + if( tcgetattr(m_socket, &l_tty) != 0 ) + { + // Replace later on with a logger line. + return false; + } + + /* Control modes */ + l_tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common) + l_tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common) + l_tty.c_cflag &= ~CSIZE; // Clear all bits that set the data field + l_tty.c_cflag |= CS8; // 8 bits per byte (most common) + l_tty.c_cflag &= ~CRTSCTS; // Disable RTS / CTS hardware flow control (most common) + l_tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1) + + /* local modes */ + l_tty.c_lflag &= ~ICANON; // Something, something CANONICAL.. Something, something Dark Side.... + l_tty.c_lflag &= ~ECHO; // Disable echo + l_tty.c_lflag &= ~ECHOE; // Disable Erasure + l_tty.c_lflag &= ~ECHONL; // Diasble new-line echo + l_tty.c_lflag &= ~ISIG; // Disable interpretation of INTS, QUIT and SUSP + l_tty.c_lflag &= ~(IXON | IXOFF | IXANY ); // Turn off s/w flow control + + /* Ouput modes */ + l_tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars) + l_tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed + + // Wait for up to 1 seconds ( 10 deciseconds), returning as soon as any data is received. + l_tty.c_cc[VTIME] = 10; + l_tty.c_cc[VMIN] = 0; + + // Set the read and write baudrate. + cfsetispeed(&l_tty, m_conConfig.getBaudRate()); + cfsetospeed(&l_tty, m_conConfig.getBaudRate()); + + // Save the tty settings. + if( tcsetattr(m_socket, TCSANOW, &l_tty) != 0 ) + { + Close(); + return false; + } + + return true; +} + +bool ModbusRtu::Close() +{ + if( close(m_socket) != 0 ) + { + return false; + } + return true; +} + +int ModbusRtu::modbusSend(uint8_t *to_send, size_t length) +{ + return write(m_socket, to_send, length); + +} + +int ModbusRtu::modbusReceive(uint8_t *buffer) +{ + return read(m_socket, buffer, sizeof(*buffer)); +} diff --git a/src/modbusrtu.h b/src/modbusrtu.h index c783b27..00cfc6c 100644 --- a/src/modbusrtu.h +++ b/src/modbusrtu.h @@ -6,6 +6,7 @@ */ #include "modbusbase.h" +#include "connectionconfig.h" namespace osdev { namespace components { @@ -13,7 +14,31 @@ namespace modbus { class ModbusRtu : public ModbusBase { +public: + explicit ModbusRtu( const ConnectionConfig &conf ); + virtual ~ModbusRtu() {} + virtual bool Connect() override; + virtual bool Close() override; + + /*! + * \brief modbusSend + * \param to_send + * \param length + * \return + */ + virtual int modbusSend(uint8_t *to_send, size_t length) override; + + /*! + * \brief modbusReceive + * \param buffer + * \return + */ + virtual int modbusReceive(uint8_t *buffer) override; + +private: + ConnectionConfig m_conConfig; + int m_socket; }; } /* End namespace modbus */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..c51b292 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,28 @@ +# **************************************************************** +# Copyright (c)2022 Peter M. Groen +# This file is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. +# **************************************************************** +add_executable(modbustest + connectionconfigtest.cpp +) + +target_include_directories(modbustest PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_BINARY_DIR} +) + +target_link_libraries(modbustest PRIVATE + gmock_main + gmock + gtest + modbus-cpp +) + +add_test(NAME modbustest COMMAND modbustest) + +set_tests_properties(modbustest PROPERTIES + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" +) diff --git a/tests/connectionconfigtest.cpp b/tests/connectionconfigtest.cpp new file mode 100644 index 0000000..1c96be9 --- /dev/null +++ b/tests/connectionconfigtest.cpp @@ -0,0 +1,27 @@ +/**************************************************************** + * Copyright (c)2022 Peter M. Groen + * This file is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + ****************************************************************/ + +#include +#include + +#include "connectionconfig.h" + +using namespace osdev::components::modbus; + +TEST(ConnectionConfigTest, SerialPortConfig) +{ + ConnectionConfig oConfig; + oConfig.setBaudRate( B9600 ); + oConfig.setConnectionType( ConnectionConfig::E_CONNECTIONTYPE::SERIAL ); + oConfig.setDataBits( 8 ); + oConfig.setStopBits( 1 ); + oConfig.setFrameTimeout( 10 ); + oConfig.setParity( ConnectionConfig::E_PARITY::NONE ); + oConfig.setPortName( "/dev/ttyUSB0" ); + + // Test all parameters + +} diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..1e00337 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.0) +project(modbus-cpp) +LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/submodules/cmake) + +# ============================================================================== +# = Include build information +include(projectheader) +project_header(modbus-cpp) + +add_subdirectory(modbus-master) +# add_subdirectory(modbus-slave) diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tools/README.md diff --git a/tools/modbus-master/CMakeLists.txt b/tools/modbus-master/CMakeLists.txt new file mode 100644 index 0000000..69d9684 --- /dev/null +++ b/tools/modbus-master/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.16) +LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/submodules/cmake) + +include(projectheader) +project_header(modbus-master) + +include(compiler) + +include_directories( + ${CMAKE_SOURCE_DIR}/include +) + +set(SRC_LIST +) + diff --git a/tools/modbus-slave/CMakeLists.txt b/tools/modbus-slave/CMakeLists.txt new file mode 100644 index 0000000..6fbcada --- /dev/null +++ b/tools/modbus-slave/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.16) +LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/submodules/cmake) + +include(projectheader) +project_header(modbus-cpp) + +include(compiler) + +include_directories( + ${CMAKE_SOURCE_DIR}/include +) + +set(SRC_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/modbusbase.h + ${CMAKE_CURRENT_SOURCE_DIR}/modbusbase.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/modbustcp.h + ${CMAKE_CURRENT_SOURCE_DIR}/modbustcp.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/modbusrtu.h + ${CMAKE_CURRENT_SOURCE_DIR}/modbusrtu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/connectionconfig.h + ${CMAKE_CURRENT_SOURCE_DIR}/connectionconfig.cpp +) + +include(library) +add_libraries(PUBLIC +) + +include(installation) +install_component() +