Skip to content

Commit ec93114

Browse files
experimental: multi master support
1 parent ed9fa5a commit ec93114

File tree

7 files changed

+300
-27
lines changed

7 files changed

+300
-27
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ set(ARCHITECTURE "native") # CPU architecture to optimize for (only relev
1414
# options
1515
option(BUILD_DOC "Build documentation" OFF)
1616
option(COMPILER_WARNINGS "Enable compiler warnings" ON)
17-
option(ENABLE_MULTITHREADING "Link the default multithreading library for the current target system" OFF)
17+
option(ENABLE_MULTITHREADING "Link the default multithreading library for the current target system" ON)
1818
option(MAKE_32_BIT_BINARY "Compile as 32 bit application. No effect on 32 bit Systems" OFF)
1919
option(OPENMP "enable openmp" OFF)
2020
option(OPTIMIZE_DEBUG "apply optimizations also in debug mode" ON)

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ target_sources(${Target} PRIVATE main.cpp)
55
target_sources(${Target} PRIVATE modbus_shm.cpp)
66
target_sources(${Target} PRIVATE Modbus_TCP_Client.cpp)
77
target_sources(${Target} PRIVATE license.cpp)
8+
target_sources(${Target} PRIVATE Modbus_TCP_connection.cpp)
89

910

1011
# ---------------------------------------- header files (*.jpp, *.h, ...) ----------------------------------------------
1112
# ======================================================================================================================
1213
target_sources(${Target} PRIVATE modbus_shm.hpp)
1314
target_sources(${Target} PRIVATE Modbus_TCP_Client.hpp)
1415
target_sources(${Target} PRIVATE license.hpp)
16+
target_sources(${Target} PRIVATE Modbus_TCP_connection.hpp)
1517

1618

1719

src/Modbus_TCP_Client.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <netinet/tcp.h>
1313
#include <sstream>
1414
#include <stdexcept>
15+
#include <sys/poll.h>
1516
#include <sys/socket.h>
1617
#include <system_error>
1718
#include <unistd.h>
@@ -220,7 +221,22 @@ std::string Client::get_listen_addr() {
220221
return sstr.str();
221222
}
222223

223-
std::string Client::connect_client() {
224+
std::shared_ptr<Connection> Client::connect_client() {
225+
struct pollfd fd;
226+
memset(&fd, 0, sizeof(fd));
227+
fd.fd = socket;
228+
fd.events = POLL_IN;
229+
do {
230+
int tmp = poll(&fd, 1, -1);
231+
if (tmp <= 0) {
232+
if (errno == EINTR) continue;
233+
throw std::system_error(errno, std::generic_category(), "Failed to poll server socket");
234+
} else
235+
break;
236+
} while (true);
237+
238+
std::lock_guard<decltype(modbus_lock)> guard(modbus_lock);
239+
224240
int tmp = modbus_tcp_pi_accept(modbus, &socket);
225241
if (tmp < 0) {
226242
const std::string error_msg = modbus_strerror(errno);
@@ -242,10 +258,12 @@ std::string Client::connect_client() {
242258
// the port entries have the same offset and size in sockaddr_in and sockaddr_in6
243259
sstr << ':' << htons(reinterpret_cast<const struct sockaddr_in *>(&peer_addr)->sin_port);
244260

245-
return sstr.str();
261+
return std::make_shared<Connection>(sstr.str(), modbus_get_socket(modbus), modbus_lock, modbus, mappings);
246262
}
247263

248264
bool Client::handle_request() {
265+
std::lock_guard<decltype(modbus_lock)> guard(modbus_lock);
266+
249267
// receive modbus request
250268
uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
251269
int rc = modbus_receive(modbus, query);

src/Modbus_TCP_Client.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@
55

66
#pragma once
77

8+
#include <memory>
89
#include <modbus/modbus.h>
10+
#include <mutex>
911
#include <string>
1012
#include <unordered_map>
1113

14+
#include "Modbus_TCP_connection.hpp"
15+
1216
namespace Modbus {
1317
namespace TCP {
1418

@@ -22,6 +26,7 @@ class Client {
2226
*mappings[MAX_CLIENT_IDS]; //!< modbus data objects (one per possible client id) (see libmodbus library)
2327
modbus_mapping_t *delete_mapping; //!< contains a pointer to a mapping that is to be deleted
2428
int socket = -1; //!< socket of the modbus connection
29+
std::mutex modbus_lock;
2530

2631
public:
2732
/*! \brief create modbus client (TCP server)
@@ -71,7 +76,7 @@ class Client {
7176
*
7277
* @return ip of the connected client
7378
*/
74-
std::string connect_client();
79+
std::shared_ptr<Connection> connect_client();
7580

7681
/*! \brief wait for request from Modbus Server and generate reply
7782
*

src/Modbus_TCP_connection.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright (C) 2022 Nikolas Koesling <nikolas@koesling.info>.
3+
* This program is free software. You can redistribute it and/or modify it under the terms of the MIT License.
4+
*/
5+
6+
#include "Modbus_TCP_connection.hpp"
7+
8+
#include <cstring>
9+
#include <sys/poll.h>
10+
11+
namespace Modbus {
12+
namespace TCP {
13+
14+
bool Connection::handle_request() {
15+
struct pollfd fd;
16+
memset(&fd, 0, sizeof(fd));
17+
fd.fd = socket;
18+
fd.events = POLL_IN;
19+
do {
20+
int tmp = poll(&fd, 1, -1);
21+
if (tmp <= 0) {
22+
if (errno == EINTR) continue;
23+
throw std::system_error(errno, std::generic_category(), "Failed to poll client socket");
24+
} else
25+
break;
26+
} while (true);
27+
28+
29+
std::lock_guard<std::mutex> guard(modbus_lock);
30+
31+
// set client socket
32+
int restore_socket = modbus_get_socket(modbus);
33+
modbus_set_socket(modbus, socket);
34+
35+
// receive modbus request
36+
uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
37+
int rc = modbus_receive(modbus, query);
38+
39+
if (rc > 0) {
40+
const auto CLIENT_ID = query[6];
41+
42+
// get mapping
43+
auto mapping = mappings[CLIENT_ID];
44+
45+
// handle request
46+
int ret = modbus_reply(modbus, query, rc, mapping);
47+
if (ret == -1) {
48+
modbus_set_socket(modbus, restore_socket);
49+
const std::string error_msg = modbus_strerror(errno);
50+
throw std::runtime_error("modbus_reply failed: " + error_msg + ' ' + std::to_string(errno));
51+
}
52+
} else if (rc == -1) {
53+
if (errno == ECONNRESET) {
54+
modbus_set_socket(modbus, restore_socket);
55+
return true;
56+
}
57+
58+
modbus_set_socket(modbus, restore_socket);
59+
const std::string error_msg = modbus_strerror(errno);
60+
throw std::runtime_error("modbus_receive failed: " + error_msg + ' ' + std::to_string(errno));
61+
}
62+
63+
modbus_set_socket(modbus, restore_socket);
64+
return false;
65+
}
66+
67+
} // namespace TCP
68+
} // namespace Modbus

src/Modbus_TCP_connection.hpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (C) 2022 Nikolas Koesling <nikolas@koesling.info>.
3+
* This program is free software. You can redistribute it and/or modify it under the terms of the MIT License.
4+
*/
5+
6+
#pragma once
7+
8+
#include <modbus/modbus.h>
9+
#include <mutex>
10+
#include <string>
11+
#include <utility>
12+
13+
namespace Modbus {
14+
namespace TCP {
15+
16+
class Connection {
17+
private:
18+
std::string peer;
19+
int socket;
20+
std::mutex &modbus_lock;
21+
modbus_t *modbus;
22+
modbus_mapping_t **mappings;
23+
24+
public:
25+
Connection(std::string peer, int socket, std::mutex &modbus_lock, modbus_t *modbus, modbus_mapping_t **mappings)
26+
: peer(std::move(peer)), socket(socket), modbus_lock(modbus_lock), modbus(modbus), mappings(mappings) {}
27+
28+
/**
29+
* @brief wait for request from Modbus Server and generate reply
30+
* @return true: connection closed
31+
*/
32+
bool handle_request();
33+
34+
/**
35+
* @brief get connection peer
36+
* @return connection peer
37+
*/
38+
[[nodiscard]] const std::string &get_peer() const { return peer; }
39+
};
40+
41+
} // namespace TCP
42+
} // namespace Modbus

0 commit comments

Comments
 (0)