Skip to content

Commit 61e716d

Browse files
Merge pull request #29 from NikolasK-source/main
v1.4.0
2 parents e9e05d4 + 98a4d07 commit 61e716d

10 files changed

+88
-14
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@
77
[submodule "libs/cxxshm"]
88
path = libs/cxxshm
99
url = https://github.com/NikolasK-source/cxxshm.git
10+
[submodule "libs/cxxsemaphore"]
11+
path = libs/cxxsemaphore
12+
url = https://github.com/NikolasK-source/cxxsemaphore.git

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.13.4 FATAL_ERROR)
44
# ======================================================================================================================
55

66
# project
7-
project(Modbus_TCP_client_shm LANGUAGES CXX VERSION 1.3.2)
7+
project(Modbus_TCP_client_shm LANGUAGES CXX VERSION 1.4.0)
88

99
# settings
1010
set(Target "modbus-tcp-client-shm") # Executable name (without file extension!)

libs/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ target_link_libraries(${Target} PRIVATE cxxopts)
1313

1414
add_subdirectory(cxxshm EXCLUDE_FROM_ALL)
1515
target_link_libraries(${Target} PRIVATE cxxshm)
16+
17+
add_subdirectory(cxxsemaphore EXCLUDE_FROM_ALL)
18+
target_link_libraries(${Target} PRIVATE cxxsemaphore)

libs/cxxsemaphore

Submodule cxxsemaphore added at 9e7ea69

libs/libmodbus

Submodule libmodbus updated 161 files

network.koesling.modbus-tcp-client-shm.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
id: network.koesling.modbus-tcp-client-shm
22
runtime: org.freedesktop.Platform
3-
runtime-version: '21.08'
3+
runtime-version: '23.08'
44
sdk: org.freedesktop.Sdk
55
command: modbus-tcp-client-shm
66
finish-args:

network.koesling.test-modbus-tcp-client-shm.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
id: network.koesling.test-modbus-tcp-client-shm
22
runtime: org.freedesktop.Platform
3-
runtime-version: '21.08'
3+
runtime-version: '23.08'
44
sdk: org.freedesktop.Sdk
55
command: modbus-tcp-client-shm
66
finish-args:

src/Modbus_TCP_Client_poll.cpp

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,21 @@
1919
namespace Modbus {
2020
namespace TCP {
2121

22+
//* maximum number of Modbus registers (per type)
2223
static constexpr int MAX_REGS = 0x10000;
2324

25+
//* value to increment error counter if semaphore could not be acquired
26+
static constexpr long SEMAPHORE_ERROR_INC = 10;
27+
28+
//* value to decrement error counter if semaphore could be acquired
29+
static constexpr long SEMAPHORE_ERROR_DEC = 1;
30+
31+
//* maximum value of semaphore error counter
32+
static constexpr long SEMAPHORE_ERROR_MAX = 1000;
33+
34+
//* maximum time to wait for semaphore (100ms)
35+
static constexpr struct timespec SEMAPHORE_MAX_TIME = {0, 100'000};
36+
2437
Client_Poll::Client_Poll(const std::string &host,
2538
const std::string &service,
2639
modbus_mapping_t *mapping,
@@ -56,7 +69,7 @@ Client_Poll::Client_Poll(const std::string &host,
5669

5770
// use mapping for all client ids
5871
for (std::size_t i = 0; i < MAX_CLIENT_IDS; ++i) {
59-
this->mappings[i] = mapping;
72+
this->mappings[i] = mb_mapping;
6073
}
6174

6275
listen();
@@ -174,6 +187,12 @@ void Client_Poll::set_tcp_timeout(std::size_t tcp_timeout) {
174187
}
175188
#endif
176189

190+
void Client_Poll::enable_semaphore(const std::string &name, bool force) {
191+
if (semaphore) throw std::logic_error("semaphore already enabled");
192+
193+
semaphore = std::make_unique<cxxsemaphore::Semaphore>(name, 1, force);
194+
}
195+
177196
void Client_Poll::set_debug(bool debug) {
178197
if (modbus_set_debug(modbus, debug)) {
179198
const std::string error_msg = modbus_strerror(errno);
@@ -369,7 +388,28 @@ Client_Poll::run_t Client_Poll::run(int signal_fd, bool reconnect, int timeout)
369388
auto mapping = mappings[CLIENT_ID];
370389

371390
// handle request
391+
if (semaphore) {
392+
if (!semaphore->wait(SEMAPHORE_MAX_TIME)) {
393+
std::cerr << Print_Time::iso << " WARNING: Failed to acquire semaphore '"
394+
<< semaphore->get_name() << "' within 100ms." << std::endl;
395+
396+
semaphore_error_counter += SEMAPHORE_ERROR_INC;
397+
398+
if (semaphore_error_counter >= SEMAPHORE_ERROR_MAX) {
399+
std::cerr << Print_Time::iso << "ERROR: Repeatedly failed to acquire the semaphore"
400+
<< std::endl;
401+
close_con(client_addrs);
402+
return run_t::semaphore;
403+
}
404+
} else {
405+
semaphore_error_counter -= SEMAPHORE_ERROR_DEC;
406+
if (semaphore_error_counter < 0) semaphore_error_counter = 0;
407+
}
408+
}
409+
372410
int ret = modbus_reply(modbus, query, rc, mapping);
411+
if (semaphore && semaphore->is_acquired()) semaphore->post();
412+
373413
if (ret == -1) {
374414
std::cerr << Print_Time::iso << " ERROR: modbus_reply failed: " << modbus_strerror(errno)
375415
<< std::endl;

src/Modbus_TCP_Client_poll.hpp

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#pragma once
66

77
#include <cstddef>
8+
#include <cxxsemaphore.hpp>
9+
#include <memory>
810
#include <modbus/modbus.h>
911
#include <string>
1012
#include <sys/poll.h>
@@ -20,13 +22,7 @@ class Client_Poll {
2022
public:
2123
static constexpr std::size_t MAX_CLIENT_IDS = 256;
2224

23-
enum class run_t {
24-
term_signal,
25-
term_nocon,
26-
ok,
27-
timeout,
28-
interrupted,
29-
};
25+
enum class run_t { ok, term_signal, term_nocon, timeout, interrupted, semaphore };
3026

3127
private:
3228
const std::size_t max_clients;
@@ -39,6 +35,10 @@ class Client_Poll {
3935
int server_socket = -1; //!< socket of the modbus connection
4036
std::unordered_map<int, std::string> client_addrs;
4137

38+
std::unique_ptr<cxxsemaphore::Semaphore> semaphore;
39+
40+
long semaphore_error_counter = 0;
41+
4242
public:
4343
/*! \brief create modbus client (TCP server)
4444
*
@@ -68,11 +68,19 @@ class Client_Poll {
6868
std::size_t tcp_timeout = 5,
6969
std::size_t max_clients = 1);
7070

71-
/*! \brief destroy the modbus client
72-
*
71+
/**
72+
* @brief destroy the modbus client
7373
*/
7474
~Client_Poll();
7575

76+
/**
77+
* @brief use the semaphore mechanism
78+
*
79+
* @param name name of the shared
80+
* @param force use the semaphore even if it already exists
81+
*/
82+
void enable_semaphore(const std::string &name, bool force = false);
83+
7684
/*! \brief enable/disable debugging output
7785
*
7886
* @param debug true: enable debug output

src/main.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,14 @@ int main(int argc, char **argv) {
171171
("separate-all",
172172
"like --separate, but for all client ids (creates 1028 shared memory files! "
173173
"check/set 'ulimit -n' before using this option.)")
174+
("semaphore",
175+
"protect the shared memory with a named semaphore against simultaneous access",
176+
cxxopts::value<std::string>())
177+
("semaphore-force",
178+
"Force the use of the semaphore even if it already exists. "
179+
"Do not use this option per default! "
180+
"It should only be used if the semaphore of an improperly terminated instance continues "
181+
"to exist as an orphan and is no longer used.")
174182
("h,help",
175183
"print usage")
176184
("version",
@@ -383,6 +391,16 @@ int main(int argc, char **argv) {
383391
return EX_SOFTWARE;
384392
}
385393

394+
// add semaphore if required
395+
try {
396+
if (args.count("semaphore")) {
397+
client->enable_semaphore(args["semaphore"].as<std::string>(), args.count("semaphore-force"));
398+
}
399+
} catch (const std::system_error &e) {
400+
std::cerr << Print_Time::iso << " ERROR: " << e.what() << std::endl;
401+
return EX_SOFTWARE;
402+
}
403+
386404
auto RECONNECT = args.count("reconnect") != 0;
387405

388406
std::cerr << Print_Time::iso << " INFO: Listening on " << client->get_listen_addr() << " for connections."
@@ -395,6 +413,7 @@ int main(int argc, char **argv) {
395413

396414
switch (ret) {
397415
case Modbus::TCP::Client_Poll::run_t::ok: continue;
416+
case Modbus::TCP::Client_Poll::run_t::semaphore:
398417
case Modbus::TCP::Client_Poll::run_t::term_signal: return;
399418
case Modbus::TCP::Client_Poll::run_t::term_nocon:
400419
std::cerr << Print_Time::iso << " INFO: No more active connections." << std::endl;

0 commit comments

Comments
 (0)