From f66d883adfd93bacedd96ba87069731f8797ada0 Mon Sep 17 00:00:00 2001 From: "RALFS-VIVOBOOK\\Admin" Date: Fri, 24 Nov 2023 21:23:24 +0100 Subject: [PATCH 1/3] ported to visual studio compiler --- CMakeLists.txt | 4 +-- include/MB/Serial/connection.hpp | 2 ++ include/MB/TCP/connection.hpp | 18 ++++++++-- include/MB/TCP/server.hpp | 11 ++++-- include/MB/modbusCell.hpp | 3 +- include/MB/modbusException.hpp | 4 +++ include/MB/modbusUtils.hpp | 6 ++-- src/Serial/connection.cpp | 3 ++ src/TCP/connection.cpp | 60 +++++++++++++++++++++----------- src/TCP/server.cpp | 21 +++++++---- src/modbusException.cpp | 2 +- src/modbusRequest.cpp | 6 ++-- src/modbusResponse.cpp | 4 +-- tests/CMakeLists.txt | 12 ++++++- 14 files changed, 113 insertions(+), 43 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea0338a..9712b51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.13) -project(protocolConverter) +project(modbus) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) if (DCMAKE_BUILD_TYPE MATCHES Debug) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -lpthread -Wall") diff --git a/include/MB/Serial/connection.hpp b/include/MB/Serial/connection.hpp index 772229a..712b060 100644 --- a/include/MB/Serial/connection.hpp +++ b/include/MB/Serial/connection.hpp @@ -7,6 +7,7 @@ #include #include #include +#ifndef _WIN32 #include #include @@ -125,3 +126,4 @@ class Connection { void setTimeout(int timeout) { _timeout = timeout; } }; } // namespace MB::Serial +#endif diff --git a/include/MB/TCP/connection.hpp b/include/MB/TCP/connection.hpp index cfadf60..2ca5d0d 100644 --- a/include/MB/TCP/connection.hpp +++ b/include/MB/TCP/connection.hpp @@ -5,13 +5,22 @@ #pragma once #include +#include #include #include + +#ifdef _WIN32 +#include +#include +#define poll(a, b, c) WSAPoll((a), (b), (c)) +#else +#define SOCKET (int) #include #include #include #include +#endif #include "MB/modbusException.hpp" #include "MB/modbusRequest.hpp" @@ -36,8 +45,13 @@ class Connection { if (this == &other) return *this; - if (_sockfd != -1 && _sockfd != other._sockfd) - ::close(_sockfd); + if (_sockfd != -1 && _sockfd != other._sockfd) { +#ifdef _WIN32 + closesocket(_sockfd); +#else + ::close(_sockfd); +#endif + } _sockfd = other._sockfd; _messageID = other._messageID; diff --git a/include/MB/TCP/server.hpp b/include/MB/TCP/server.hpp index 20eb6d4..c062ff7 100644 --- a/include/MB/TCP/server.hpp +++ b/include/MB/TCP/server.hpp @@ -8,13 +8,20 @@ #include #include +#ifdef _WIN32 +#include +#include +#define poll(a, b, c) WSAPoll((a), (b), (c)) +#else #include #include #include +#endif #include "connection.hpp" -namespace MB::TCP { +namespace MB { +namespace TCP { class Server { private: int _serverfd; @@ -45,4 +52,4 @@ class Server { std::optional awaitConnection(); }; -} // namespace MB::TCP +}} // namespace MB::TCP diff --git a/include/MB/modbusCell.hpp b/include/MB/modbusCell.hpp index 5d61323..0266658 100644 --- a/include/MB/modbusCell.hpp +++ b/include/MB/modbusCell.hpp @@ -7,6 +7,7 @@ #include #include #include +#include /** * Namespace that contains whole project @@ -18,7 +19,7 @@ namespace MB { */ class ModbusCell { private: - std::variant _value; + std::variant _value; public: /** diff --git a/include/MB/modbusException.hpp b/include/MB/modbusException.hpp index 6ee6d61..9f811d1 100644 --- a/include/MB/modbusException.hpp +++ b/include/MB/modbusException.hpp @@ -85,7 +85,11 @@ class ModbusException : public std::exception { [[nodiscard]] const char *what() const noexcept override { auto og = toString(); char *str = new char[og.size()]; +#ifdef _WIN32 + strncpy_s(str, og.size(), og.c_str(), og.size()); +#else stpcpy(str, og.c_str()); +#endif return str; } diff --git a/include/MB/modbusUtils.hpp b/include/MB/modbusUtils.hpp index 62a1f5f..80fc581 100644 --- a/include/MB/modbusUtils.hpp +++ b/include/MB/modbusUtils.hpp @@ -16,7 +16,8 @@ * Namespace that contains many useful utility functions and enums * that are used in the whole project. */ -namespace MB::utils { +namespace MB { +namespace utils { /*! All possible modbus error codes * @note Contains custom, non standard codes */ @@ -257,7 +258,7 @@ inline uint16_t calculateCRC(const uint8_t *buff, size_t len) { //! Calculate CRC wrapper inline uint16_t calculateCRC(const std::vector &buffer) { - return calculateCRC(buffer.begin().base(), buffer.size()); + return calculateCRC(buffer.data(), buffer.size()); } //! Split uint16_t to two uint8_t in big endian form @@ -275,4 +276,5 @@ inline void pushUint16(std::vector& buffer, const uint16_t val) { buffer.push_back(low); } +} } // namespace MB::utils diff --git a/src/Serial/connection.cpp b/src/Serial/connection.cpp index c552954..51d1998 100644 --- a/src/Serial/connection.cpp +++ b/src/Serial/connection.cpp @@ -2,6 +2,7 @@ // Copyright (c) 2020 Mateusz Mazur aka Mazurel // Licensed under: MIT License +#ifndef _WIN32 #include "Serial/connection.hpp" using namespace MB::Serial; @@ -152,3 +153,5 @@ Connection &Connection::operator=(Connection &&moved) { moved._fd = -1; return *this; } + +#endif diff --git a/src/TCP/connection.cpp b/src/TCP/connection.cpp index b6feef9..50499f5 100644 --- a/src/TCP/connection.cpp +++ b/src/TCP/connection.cpp @@ -15,7 +15,11 @@ Connection::~Connection() { if (_sockfd == -1) return; +#ifdef _WIN32 + closesocket(_sockfd); +#else ::close(_sockfd); +#endif _sockfd = -1; } @@ -30,13 +34,13 @@ std::vector Connection::sendRequest(const MB::ModbusRequest &req) { std::vector dat = req.toRaw(); - uint32_t size = dat.size(); - rawReq.push_back(reinterpret_cast(&size)[1]); - rawReq.push_back(reinterpret_cast(&size)[0]); + uint32_t size = (uint32_t)dat.size(); + rawReq.push_back((uint8_t)reinterpret_cast(&size)[1]); + rawReq.push_back((uint8_t)reinterpret_cast(&size)[0]); rawReq.insert(rawReq.end(), dat.begin(), dat.end()); - ::send(_sockfd, rawReq.begin().base(), rawReq.size(), 0); + ::send(_sockfd, (const char*)rawReq.data(), (int)rawReq.size(), 0); return rawReq; } @@ -52,13 +56,13 @@ std::vector Connection::sendResponse(const MB::ModbusResponse &res) { std::vector dat = res.toRaw(); - uint32_t size = dat.size(); - rawReq.push_back(reinterpret_cast(&size)[1]); - rawReq.push_back(reinterpret_cast(&size)[0]); + uint32_t size = (uint32_t)dat.size(); + rawReq.push_back((uint8_t)reinterpret_cast(&size)[1]); + rawReq.push_back((uint8_t)reinterpret_cast(&size)[0]); rawReq.insert(rawReq.end(), dat.begin(), dat.end()); - ::send(_sockfd, rawReq.begin().base(), rawReq.size(), 0); + ::send(_sockfd, (const char*)rawReq.data(), (int)rawReq.size(), 0); return rawReq; } @@ -74,19 +78,19 @@ std::vector Connection::sendException(const MB::ModbusException &ex) { std::vector dat = ex.toRaw(); - uint32_t size = dat.size(); - rawReq.push_back(reinterpret_cast(&size)[1]); - rawReq.push_back(reinterpret_cast(&size)[0]); + uint32_t size = (uint32_t)dat.size(); + rawReq.push_back((uint8_t)reinterpret_cast(&size)[1]); + rawReq.push_back((uint8_t)reinterpret_cast(&size)[0]); rawReq.insert(rawReq.end(), dat.begin(), dat.end()); - ::send(_sockfd, rawReq.begin().base(), rawReq.size(), 0); + ::send(_sockfd, (const char*)rawReq.data(), (int)rawReq.size(), 0); return rawReq; } std::vector Connection::awaitRawMessage() { - pollfd _pfd = {.fd = _sockfd, .events = POLLIN, .revents = POLLIN}; + pollfd _pfd = {.fd = (SOCKET)_sockfd, .events = POLLIN, .revents = POLLIN}; if (::poll(&_pfd, 1, 60 * 1000 /* 1 minute means the connection has died */) <= 0) { throw MB::ModbusException(MB::utils::ConnectionClosed); @@ -94,7 +98,7 @@ std::vector Connection::awaitRawMessage() { std::vector r(1024); - auto size = ::recv(_sockfd, r.begin().base(), r.size(), 0); + auto size = ::recv(_sockfd, (char*)r.data(), (int)r.size(), 0); if (size == -1) throw MB::ModbusException(MB::utils::ProtocolError); @@ -109,7 +113,7 @@ std::vector Connection::awaitRawMessage() { } MB::ModbusRequest Connection::awaitRequest() { - pollfd _pfd = {.fd = _sockfd, .events = POLLIN, .revents = POLLIN}; + pollfd _pfd = {.fd = (SOCKET)_sockfd, .events = POLLIN, .revents = POLLIN}; if (::poll(&_pfd, 1, 60 * 1000 /* 1 minute means the connection has died */) <= 0) { throw MB::ModbusException(MB::utils::Timeout); @@ -117,7 +121,7 @@ MB::ModbusRequest Connection::awaitRequest() { std::vector r(1024); - auto size = ::recv(_sockfd, r.begin().base(), r.size(), 0); + auto size = ::recv(_sockfd, (char*)r.data(), (int)r.size(), 0); if (size == -1) throw MB::ModbusException(MB::utils::ProtocolError); @@ -138,13 +142,13 @@ MB::ModbusRequest Connection::awaitRequest() { } MB::ModbusResponse Connection::awaitResponse() { - pollfd _pfd = {.fd = _sockfd, .events = POLLIN, .revents = POLLIN}; + pollfd _pfd = {.fd = (SOCKET)_sockfd, .events = POLLIN, .revents = POLLIN}; if (::poll(&_pfd, 1, _timeout) <= 0) { throw MB::ModbusException(MB::utils::Timeout); } std::vector r(1024); - auto size = ::recv(_sockfd, r.begin().base(), r.size(), 0); + auto size = ::recv(_sockfd, (char*)r.data(), (int)r.size(), 0); if (size == -1) throw MB::ModbusException(MB::utils::ProtocolError); @@ -169,8 +173,14 @@ MB::ModbusResponse Connection::awaitResponse() { } Connection::Connection(Connection &&moved) noexcept { - if (_sockfd != -1 && moved._sockfd != _sockfd) - ::close(_sockfd); + if (_sockfd != -1 && moved._sockfd != _sockfd) { +#ifdef _WIN32 + closesocket(_sockfd); +#else + ::close(_sockfd); +#endif + + } _sockfd = moved._sockfd; _messageID = moved._messageID; @@ -183,15 +193,23 @@ Connection Connection::with(std::string addr, int port) { throw std::runtime_error("Cannot open socket, errno = " + std::to_string(errno)); +#ifdef _WIN32 + sockaddr_in server = { .sin_family = AF_INET, + .sin_port = htons(port), + .sin_addr = {}, + .sin_zero = {} }; + inet_pton(AF_INET, addr.c_str(), &server.sin_addr); +#else sockaddr_in server = {.sin_family = AF_INET, .sin_port = htons(port), .sin_addr = {inet_addr(addr.c_str())}, .sin_zero = {}}; +#endif if (::connect(sock, reinterpret_cast(&server), sizeof(server)) < 0) throw std::runtime_error("Cannot connect, errno = " + std::to_string(errno)); - return Connection(sock); + return Connection((int)sock); } diff --git a/src/TCP/server.cpp b/src/TCP/server.cpp index a05ce8e..bf0c043 100644 --- a/src/TCP/server.cpp +++ b/src/TCP/server.cpp @@ -8,13 +8,17 @@ using namespace MB::TCP; Server::Server(int port) { _port = port; - _serverfd = socket(AF_INET, SOCK_STREAM, 0); + _serverfd = (int)socket(AF_INET, SOCK_STREAM, 0); if (_serverfd == -1) throw std::runtime_error("Cannot create socket"); - setsockopt(_serverfd, SOL_SOCKET, SO_REUSEADDR, new int(1), sizeof(int)); - setsockopt(_serverfd, SOL_SOCKET, SO_REUSEPORT, new int(1), sizeof(int)); + uint32_t reuseaddr = 1; + setsockopt(_serverfd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseaddr, sizeof(reuseaddr)); +#ifdef SO_REUSEPORT + setsockopt(_serverfd, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuseaddr, sizeof(reuseaddr)); + (const char*)&reuseaddr +#endif _server = {}; @@ -30,8 +34,13 @@ Server::Server(int port) { } Server::~Server() { - if (_serverfd >= 0) - ::close(_serverfd); + if (_serverfd >= 0) { +#ifdef _WIN32 + closesocket(_serverfd); +#else + ::close(_serverfd); +#endif + } _serverfd = -1; } @@ -45,5 +54,5 @@ std::optional Server::awaitConnection() { if (connfd < 0) throw; - return Connection(connfd); + return Connection((int)connfd); } diff --git a/src/modbusException.cpp b/src/modbusException.cpp index b9c4198..a9cee53 100644 --- a/src/modbusException.cpp +++ b/src/modbusException.cpp @@ -24,7 +24,7 @@ ModbusException::ModbusException(const std::vector &inputData, if (CRC) { auto CRC = *reinterpret_cast(&inputData[3]); - auto calculatedCRC = utils::calculateCRC(inputData.begin().base(), 3); + auto calculatedCRC = utils::calculateCRC(inputData.data(), 3); if (CRC != calculatedCRC) { _errorCode = utils::ErrorCodeCRCError; diff --git a/src/modbusRequest.cpp b/src/modbusRequest.cpp index 79c736d..769a74f 100644 --- a/src/modbusRequest.cpp +++ b/src/modbusRequest.cpp @@ -92,7 +92,7 @@ ModbusRequest::ModbusRequest(const std::vector &inputData, bool CRC) { throw ModbusException(utils::InvalidByteOrder); auto recvCRC = *reinterpret_cast(&inputData[crcIndex]); - auto myCRC = utils::calculateCRC(inputData.begin().base(), crcIndex); + auto myCRC = utils::calculateCRC(inputData.data(), crcIndex); if (recvCRC != myCRC) { throw ModbusException(utils::InvalidCRC, _slaveID); @@ -100,7 +100,7 @@ ModbusRequest::ModbusRequest(const std::vector &inputData, bool CRC) { } } catch (const ModbusException &ex) { throw ex; - } catch (const std::exception &ex) { + } catch (const std::exception /*&ex*/) { throw ModbusException(utils::InvalidByteOrder); } } @@ -158,7 +158,7 @@ std::vector ModbusRequest::toRaw() const noexcept { utils::pushUint16(result, value.reg()); } } else { - int end = result.size() - 1; + size_t end = result.size() - 1; for (std::size_t i = 0; i < _values.size(); i++) { if (i % 8 == 0) { result.push_back(0x00); diff --git a/src/modbusResponse.cpp b/src/modbusResponse.cpp index 7166b72..2c54042 100644 --- a/src/modbusResponse.cpp +++ b/src/modbusResponse.cpp @@ -96,7 +96,7 @@ ModbusResponse::ModbusResponse(std::vector inputData, bool CRC) { auto recievedCRC = *reinterpret_cast(&inputData[crcIndex]); - auto myCRC = utils::calculateCRC(inputData.begin().base(), crcIndex); + auto myCRC = utils::calculateCRC(inputData.data(), crcIndex); if (recievedCRC != myCRC) { throw ModbusException(utils::InvalidCRC, _slaveID); @@ -104,7 +104,7 @@ ModbusResponse::ModbusResponse(std::vector inputData, bool CRC) { } } catch (const ModbusException &ex) { throw ex; - } catch (const std::exception &ex) { + } catch (const std::exception /*&ex*/) { throw ModbusException(utils::InvalidByteOrder); } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5bb94ac..c4332ce 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,10 @@ project(Google_tests) + +if (MSVC) +set(CMAKE_CXX_STANDARD 20) +set(gtest_force_shared_crt on) +endif() + add_subdirectory(googletest) include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) @@ -10,5 +16,9 @@ set(TestFiles MB/ModbusRequestTests.cpp add_executable(Google_Tests_run ${TestFiles}) -target_link_libraries(Google_Tests_run Modbus) +if (MSVC) +target_link_libraries(Google_Tests_run Modbus_Core ws2_32.lib Iphlpapi.lib) +else() +target_link_libraries(Google_Tests_run Modbus_Core) +endif() target_link_libraries(Google_Tests_run gtest gtest_main) From 56a259ea1d52f5f7f89d0e5fabc0442b8624757a Mon Sep 17 00:00:00 2001 From: "RALFS-VIVOBOOK\\Admin" Date: Sun, 26 Nov 2023 10:08:14 +0100 Subject: [PATCH 2/3] further improvements --- .gitignore | 2 + CMakeLists.txt | 5 +- include/MB/Serial/connection.hpp | 256 +++++++++++++++---------------- include/MB/TCP/connection.hpp | 56 ++----- include/MB/TCP/server.hpp | 16 +- include/MB/modbusRequest.hpp | 4 +- include/MB/modbusResponse.hpp | 8 +- include/MB/modbusUtils.hpp | 4 +- src/CMakeLists.txt | 11 +- src/TCP/connection.cpp | 68 +++++--- src/TCP/server.cpp | 5 +- src/modbusResponse.cpp | 6 +- tests/CMakeLists.txt | 7 +- 13 files changed, 224 insertions(+), 224 deletions(-) diff --git a/.gitignore b/.gitignore index f928faf..35dbc63 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,8 @@ *.app build +out +.vs # Gtags GTAGS diff --git a/CMakeLists.txt b/CMakeLists.txt index 9712b51..6b2233c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 3.13) project(modbus) set(CMAKE_CXX_STANDARD 20) @@ -9,7 +8,8 @@ endif() option(MODBUS_EXAMPLE "Build example program" OFF) option(MODBUS_TESTS "Build tests" OFF) -option(MODBUS_COMMUNICATION "Use Modbus communication library" ON) +option(MODBUS_TCP_COMMUNICATION "Use Modbus TCP communication library" ON) +option(MODBUS_SERIAL_COMMUNICATION "Use Modbus serial communication library" OFF) # not supported by windows platform add_subdirectory(src) @@ -21,4 +21,3 @@ if(MODBUS_EXAMPLE) add_executable(ex example/main.cpp) target_link_libraries(ex Modbus) endif() - diff --git a/include/MB/Serial/connection.hpp b/include/MB/Serial/connection.hpp index 712b060..cb4af79 100644 --- a/include/MB/Serial/connection.hpp +++ b/include/MB/Serial/connection.hpp @@ -1,129 +1,127 @@ -// Modbus for c++ -// Copyright (c) 2020 Mateusz Mazur aka Mazurel -// Licensed under: MIT License - -#pragma once - -#include -#include -#include -#ifndef _WIN32 -#include -#include - -#include -#include -#include -#include -#include - -#include "MB/modbusException.hpp" -#include "MB/modbusRequest.hpp" -#include "MB/modbusResponse.hpp" -#include "MB/modbusUtils.hpp" - -namespace MB::Serial { -class Connection { -public: - // Pretty high timeout - static const unsigned int DefaultSerialTimeout = 100; - -private: - struct termios _termios; - int _fd; - - int _timeout = Connection::DefaultSerialTimeout; - -public: - constexpr explicit Connection() : _termios(), _fd(-1) {} - explicit Connection(const std::string &path); - explicit Connection(const Connection &) = delete; - explicit Connection(Connection &&) noexcept; - Connection &operator=(Connection &&); - ~Connection(); - - void connect(); - - std::vector sendRequest(const MB::ModbusRequest &request); - std::vector sendResponse(const MB::ModbusResponse &response); - std::vector sendException(const MB::ModbusException &exception); - - /** - * @brief Sends data through the serial - * @param data - Vectorized data - */ - std::vector send(std::vector data); - - void clearInput(); - - [[nodiscard]] std::tuple> awaitResponse(); - [[nodiscard]] std::tuple> awaitRequest(); - - [[nodiscard]] std::vector awaitRawMessage(); - - void enableParity(const bool parity) { - if (parity) - getTTY().c_cflag |= PARENB; - else - getTTY().c_cflag &= ~PARENB; - } - - void setEvenParity() { - enableParity(true); - getTTY().c_cflag &= ~PARODD; - } - - void setOddParity() { - enableParity(true); - getTTY().c_cflag |= PARODD; - } - - void setTwoStopBits(const bool two) { - if (two) { - getTTY().c_cflag |= CSTOPB; - } else { - getTTY().c_cflag &= ~CSTOPB; - } - } - -#define setBaud(s) \ - case s: \ - speed = B##s; \ - break; - void setBaudRate(speed_t speed) { - switch (speed) { - setBaud(0); - setBaud(50); - setBaud(75); - setBaud(110); - setBaud(134); - setBaud(150); - setBaud(200); - setBaud(300); - setBaud(600); - setBaud(1200); - setBaud(1800); - setBaud(2400); - setBaud(4800); - setBaud(9600); - setBaud(19200); - setBaud(38400); - setBaud(57600); - setBaud(115200); - setBaud(230400); - default: - throw std::runtime_error("Invalid baud rate"); - } - cfsetospeed(&_termios, speed); - cfsetispeed(&_termios, speed); - } -#undef setBaud - - termios &getTTY() { return _termios; } - - int getTimeout() const { return _timeout; } - - void setTimeout(int timeout) { _timeout = timeout; } -}; -} // namespace MB::Serial -#endif +// Modbus for c++ +// Copyright (c) 2020 Mateusz Mazur aka Mazurel +// Licensed under: MIT License + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "MB/modbusException.hpp" +#include "MB/modbusRequest.hpp" +#include "MB/modbusResponse.hpp" +#include "MB/modbusUtils.hpp" + +namespace MB::Serial { +class Connection { +public: + // Pretty high timeout + static const unsigned int DefaultSerialTimeout = 100; + +private: + struct termios _termios; + int _fd; + + int _timeout = Connection::DefaultSerialTimeout; + +public: + constexpr explicit Connection() : _termios(), _fd(-1) {} + explicit Connection(const std::string &path); + explicit Connection(const Connection &) = delete; + explicit Connection(Connection &&) noexcept; + Connection &operator=(Connection &&); + ~Connection(); + + void connect(); + + std::vector sendRequest(const MB::ModbusRequest &request); + std::vector sendResponse(const MB::ModbusResponse &response); + std::vector sendException(const MB::ModbusException &exception); + + /** + * @brief Sends data through the serial + * @param data - Vectorized data + */ + std::vector send(std::vector data); + + void clearInput(); + + [[nodiscard]] std::tuple> awaitResponse(); + [[nodiscard]] std::tuple> awaitRequest(); + + [[nodiscard]] std::vector awaitRawMessage(); + + void enableParity(const bool parity) { + if (parity) + getTTY().c_cflag |= PARENB; + else + getTTY().c_cflag &= ~PARENB; + } + + void setEvenParity() { + enableParity(true); + getTTY().c_cflag &= ~PARODD; + } + + void setOddParity() { + enableParity(true); + getTTY().c_cflag |= PARODD; + } + + void setTwoStopBits(const bool two) { + if (two) { + getTTY().c_cflag |= CSTOPB; + } else { + getTTY().c_cflag &= ~CSTOPB; + } + } + +#define setBaud(s) \ + case s: \ + speed = B##s; \ + break; + void setBaudRate(speed_t speed) { + switch (speed) { + setBaud(0); + setBaud(50); + setBaud(75); + setBaud(110); + setBaud(134); + setBaud(150); + setBaud(200); + setBaud(300); + setBaud(600); + setBaud(1200); + setBaud(1800); + setBaud(2400); + setBaud(4800); + setBaud(9600); + setBaud(19200); + setBaud(38400); + setBaud(57600); + setBaud(115200); + setBaud(230400); + default: + throw std::runtime_error("Invalid baud rate"); + } + cfsetospeed(&_termios, speed); + cfsetispeed(&_termios, speed); + } +#undef setBaud + + termios &getTTY() { return _termios; } + + int getTimeout() const { return _timeout; } + + void setTimeout(int timeout) { _timeout = timeout; } +}; +} // namespace MB::Serial diff --git a/include/MB/TCP/connection.hpp b/include/MB/TCP/connection.hpp index 2ca5d0d..ed80e46 100644 --- a/include/MB/TCP/connection.hpp +++ b/include/MB/TCP/connection.hpp @@ -4,29 +4,16 @@ #pragma once -#include #include -#include - -#include - -#ifdef _WIN32 -#include -#include -#define poll(a, b, c) WSAPoll((a), (b), (c)) -#else -#define SOCKET (int) -#include -#include -#include -#include -#endif - -#include "MB/modbusException.hpp" -#include "MB/modbusRequest.hpp" -#include "MB/modbusResponse.hpp" - -namespace MB::TCP { +#include + +#include "../modbusException.hpp" +#include "../modbusRequest.hpp" +#include "../modbusResponse.hpp" + +namespace MB { +namespace TCP { + class Connection { public: static const unsigned int DefaultTCPTimeout = 500; @@ -36,33 +23,18 @@ class Connection { uint16_t _messageID = 0; int _timeout = Connection::DefaultTCPTimeout; + void closeSockfd(void); + public: explicit Connection() noexcept : _sockfd(-1), _messageID(0){}; explicit Connection(int sockfd) noexcept; Connection(const Connection ©) = delete; Connection(Connection &&moved) noexcept; - Connection &operator=(Connection &&other) noexcept { - if (this == &other) - return *this; - - if (_sockfd != -1 && _sockfd != other._sockfd) { -#ifdef _WIN32 - closesocket(_sockfd); -#else - ::close(_sockfd); -#endif - } - - _sockfd = other._sockfd; - _messageID = other._messageID; - other._sockfd = -1; - - return *this; - } + Connection& operator=(Connection&& other) noexcept; [[nodiscard]] int getSockfd() const { return _sockfd; } - static Connection with(std::string addr, int port); + static Connection with(const std::string &addr, int port); ~Connection(); @@ -79,4 +51,4 @@ class Connection { void setMessageId(uint16_t messageId) { _messageID = messageId; } }; -} // namespace MB::TCP +}} // namespace MB::TCP diff --git a/include/MB/TCP/server.hpp b/include/MB/TCP/server.hpp index c062ff7..daa5379 100644 --- a/include/MB/TCP/server.hpp +++ b/include/MB/TCP/server.hpp @@ -4,14 +4,9 @@ #pragma once -#include -#include -#include - #ifdef _WIN32 #include #include -#define poll(a, b, c) WSAPoll((a), (b), (c)) #else #include #include @@ -22,6 +17,7 @@ namespace MB { namespace TCP { + class Server { private: int _serverfd; @@ -33,23 +29,25 @@ class Server { ~Server(); Server(const Server &) = delete; - Server(Server &&moved) { + Server(Server &&moved) noexcept { _serverfd = moved._serverfd; _port = moved._port; + _server = moved._server; moved._serverfd = -1; } - Server &operator=(Server &&moved) { + Server &operator=(Server &&moved) noexcept { if (this == &moved) return *this; _serverfd = moved._serverfd; _port = moved._port; + _server = moved._server; moved._serverfd = -1; return *this; } - [[nodiscard]] int nativeHandle() { return _serverfd; } + [[nodiscard]] int nativeHandle() const { return _serverfd; } - std::optional awaitConnection(); + Connection awaitConnection(); }; }} // namespace MB::TCP diff --git a/include/MB/modbusRequest.hpp b/include/MB/modbusRequest.hpp index aac29e8..d940681 100644 --- a/include/MB/modbusRequest.hpp +++ b/include/MB/modbusRequest.hpp @@ -17,7 +17,7 @@ */ namespace MB { /** - * This class represent Modbus response, it allows + * This class represent Modbus request, it allows * user to manipulate and display it in various ways. */ class ModbusRequest { @@ -69,7 +69,7 @@ class ModbusRequest { } /** - * Simple constructor, that allows to create "dummy" ModbusResponse + * Simple constructor, that allows to create "dummy" ModbusRequest * object. May be useful in some cases. */ explicit ModbusRequest(uint8_t slaveId = 0, diff --git a/include/MB/modbusResponse.hpp b/include/MB/modbusResponse.hpp index 02c04dd..83e9a71 100644 --- a/include/MB/modbusResponse.hpp +++ b/include/MB/modbusResponse.hpp @@ -46,14 +46,14 @@ class ModbusResponse { *exception if it is invalid * @throws ModbusException **/ - explicit ModbusResponse(std::vector inputData, bool CRC = false); + explicit ModbusResponse(const std::vector &inputData, bool CRC = false); /* * @description Constructs Response from raw data * @params inputData is a vector of bytes that will be interpreted * @throws ModbusException **/ - static ModbusResponse fromRaw(std::vector inputData) { + static ModbusResponse fromRaw(const std::vector &inputData) { return ModbusResponse(inputData); } /* @@ -63,7 +63,7 @@ class ModbusResponse { * @note This methods performs CRC check that may throw ModbusException on * invalid CRC **/ - static ModbusResponse fromRawCRC(std::vector inputData) { + static ModbusResponse fromRawCRC(const std::vector &inputData) { return ModbusResponse(inputData, true); } @@ -75,7 +75,7 @@ class ModbusResponse { utils::MBFunctionCode functionCode = static_cast(0), uint16_t address = 0, uint16_t registersNumber = 0, - std::vector values = {}); + const std::vector &values = {}); ModbusResponse(const ModbusResponse &) = default; diff --git a/include/MB/modbusUtils.hpp b/include/MB/modbusUtils.hpp index 80fc581..a70de30 100644 --- a/include/MB/modbusUtils.hpp +++ b/include/MB/modbusUtils.hpp @@ -18,6 +18,7 @@ */ namespace MB { namespace utils { + /*! All possible modbus error codes * @note Contains custom, non standard codes */ @@ -276,5 +277,4 @@ inline void pushUint16(std::vector& buffer, const uint16_t val) { buffer.push_back(low); } -} -} // namespace MB::utils +}} // namespace MB::utils diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 56b2940..0a660cc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,9 +19,14 @@ add_library(Modbus) target_link_libraries(Modbus Modbus_Core) -if(MODBUS_COMMUNICATION) - message("Modbus communication is experimental") +if(MODBUS_TCP_COMMUNICATION) + message("Modbus TCP communication is experimental") add_subdirectory(TCP) + target_link_libraries(Modbus Modbus_TCP) +endif() + +if(MODBUS_SERIAL_COMMUNICATION) + message("Modbus serial communication is experimental") add_subdirectory(Serial) - target_link_libraries(Modbus Modbus_TCP Modbus_Serial) + target_link_libraries(Modbus Modbus_Serial) endif() diff --git a/src/TCP/connection.cpp b/src/TCP/connection.cpp index 50499f5..2b444d8 100644 --- a/src/TCP/connection.cpp +++ b/src/TCP/connection.cpp @@ -2,8 +2,23 @@ // Copyright (c) 2020 Mateusz Mazur aka Mazurel // Licensed under: MIT License +#include +#include +#include #include "TCP/connection.hpp" +#ifdef _WIN32 +#include +#include +#define poll(a, b, c) WSAPoll((a), (b), (c)) +#else +#define SOCKET (int) +#include +#include +#include +#include +#endif + using namespace MB::TCP; Connection::Connection(const int sockfd) noexcept { @@ -11,15 +26,33 @@ Connection::Connection(const int sockfd) noexcept { _messageID = 0; } +Connection& Connection::operator=(Connection&& other) noexcept { + if (this == &other) + return *this; + + if (_sockfd != -1 && _sockfd != other._sockfd) { + closeSockfd(); + } + + _sockfd = other._sockfd; + _messageID = other._messageID; + other._sockfd = -1; + + return *this; +} + Connection::~Connection() { - if (_sockfd == -1) - return; + closeSockfd(); +} +void Connection::closeSockfd(void) { + if (_sockfd >= 0) { #ifdef _WIN32 - closesocket(_sockfd); + closesocket(_sockfd); #else - ::close(_sockfd); + ::close(_sockfd); #endif + } _sockfd = -1; } @@ -174,12 +207,7 @@ MB::ModbusResponse Connection::awaitResponse() { Connection::Connection(Connection &&moved) noexcept { if (_sockfd != -1 && moved._sockfd != _sockfd) { -#ifdef _WIN32 - closesocket(_sockfd); -#else - ::close(_sockfd); -#endif - + closeSockfd(); } _sockfd = moved._sockfd; @@ -187,24 +215,26 @@ Connection::Connection(Connection &&moved) noexcept { moved._sockfd = -1; } -Connection Connection::with(std::string addr, int port) { +Connection Connection::with(const std::string &addr, int port) { +#ifdef _WIN32 + // initialize Windows Socket API with given VERSION. + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData)) { + throw std::runtime_error("WSAStartup failure, errno = " + + std::to_string(errno)); + } +#endif + auto sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) throw std::runtime_error("Cannot open socket, errno = " + std::to_string(errno)); -#ifdef _WIN32 sockaddr_in server = { .sin_family = AF_INET, .sin_port = htons(port), .sin_addr = {}, .sin_zero = {} }; - inet_pton(AF_INET, addr.c_str(), &server.sin_addr); -#else - sockaddr_in server = {.sin_family = AF_INET, - .sin_port = htons(port), - .sin_addr = {inet_addr(addr.c_str())}, - .sin_zero = {}}; -#endif + ::inet_pton(AF_INET, addr.c_str(), &server.sin_addr); if (::connect(sock, reinterpret_cast(&server), sizeof(server)) < 0) diff --git a/src/TCP/server.cpp b/src/TCP/server.cpp index bf0c043..3e5cb89 100644 --- a/src/TCP/server.cpp +++ b/src/TCP/server.cpp @@ -2,6 +2,8 @@ // Copyright (c) 2020 Mateusz Mazur aka Mazurel // Licensed under: MIT License +#include +#include #include "TCP/server.hpp" using namespace MB::TCP; @@ -21,7 +23,6 @@ Server::Server(int port) { #endif _server = {}; - _server.sin_family = AF_INET; _server.sin_addr.s_addr = INADDR_ANY; _server.sin_port = ::htons(_port); @@ -45,7 +46,7 @@ Server::~Server() { _serverfd = -1; } -std::optional Server::awaitConnection() { +Connection Server::awaitConnection() { socklen_t addrLen = sizeof(_server); auto connfd = ::accept( diff --git a/src/modbusResponse.cpp b/src/modbusResponse.cpp index 2c54042..8c9b7bd 100644 --- a/src/modbusResponse.cpp +++ b/src/modbusResponse.cpp @@ -13,9 +13,9 @@ using namespace MB; ModbusResponse::ModbusResponse(uint8_t slaveId, utils::MBFunctionCode functionCode, uint16_t address, uint16_t registersNumber, - std::vector values) + const std::vector &values) : _slaveID(slaveId), _functionCode(functionCode), _address(address), - _registersNumber(registersNumber), _values(std::move(values)) { + _registersNumber(registersNumber), _values(values) { // Force proper modbuscell type switch (functionRegisters()) { case utils::OutputCoils: @@ -31,7 +31,7 @@ ModbusResponse::ModbusResponse(uint8_t slaveId, } } -ModbusResponse::ModbusResponse(std::vector inputData, bool CRC) { +ModbusResponse::ModbusResponse(const std::vector &inputData, bool CRC) { try { if (inputData.size() < 3) throw ModbusException(utils::InvalidByteOrder); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c4332ce..0503ba0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,12 +1,11 @@ project(Google_tests) if (MSVC) -set(CMAKE_CXX_STANDARD 20) set(gtest_force_shared_crt on) endif() add_subdirectory(googletest) -include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) +include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR} ../include) set(TestFiles MB/ModbusRequestTests.cpp MB/ModbusResponseTests.cpp @@ -16,9 +15,5 @@ set(TestFiles MB/ModbusRequestTests.cpp add_executable(Google_Tests_run ${TestFiles}) -if (MSVC) -target_link_libraries(Google_Tests_run Modbus_Core ws2_32.lib Iphlpapi.lib) -else() target_link_libraries(Google_Tests_run Modbus_Core) -endif() target_link_libraries(Google_Tests_run gtest gtest_main) From cf235ed96529bc2f43f741f4baa0d62380c8a08d Mon Sep 17 00:00:00 2001 From: Daan Smienk Date: Thu, 15 Feb 2024 14:08:36 +0000 Subject: [PATCH 3/3] Wrong order of message when exceptiontoRaw() is called. It should be first function code and then error code. --- src/modbusException.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modbusException.cpp b/src/modbusException.cpp index a9cee53..ec29b0d 100644 --- a/src/modbusException.cpp +++ b/src/modbusException.cpp @@ -54,8 +54,8 @@ std::vector ModbusException::toRaw() const noexcept { std::vector result(3); result[0] = _slaveId; - result[1] = static_cast(_errorCode | 0b10000000); - result[2] = static_cast(_functionCode); + result[1] = static_cast(_functionCode | 0b10000000); + result[2] = static_cast(_errorCode); return result; }