Skip to content

Commit b7449d8

Browse files
implemented modbus tcp shm client
1 parent 9bdf2df commit b7449d8

14 files changed

+568
-97
lines changed

.gitmodules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[submodule "libs/cxxopts"]
2+
path = libs/cxxopts
3+
url = git@github.com:NikolasK-source/cxxopts.git
4+
[submodule "libs/libmodbus"]
5+
path = libs/libmodbus
6+
url = https://github.com/stephane/libmodbus

CMakeLists.txt

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

66
# project
7-
project(CMAKE_Template_Project LANGUAGES CXX VERSION 0.0.1)
7+
project(Modbus_TCP_client_shm LANGUAGES CXX VERSION 0.0.1)
88

99
# settings
10-
set(Target "Template_Project") # Executable name (without file extension!)
11-
set(STANDARD 20) # C++ Standard
10+
set(Target "Modbus_TCP_client_shm") # Executable name (without file extension!)
11+
set(STANDARD 17) # C++ Standard
1212
set(ARCHITECTURE "native") # CPU architecture to optimize for (only relevant if OPTIMIZE_FOR_ARCHITECTURE is ON)
1313

1414
# options
15-
option(BUILD_DOC "Build documentation" ON)
15+
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" ON)
17+
option(ENABLE_MULTITHREADING "Link the default multithreading library for the current target system" OFF)
1818
option(MAKE_32_BIT_BINARY "Compile as 32 bit application. No effect on 32 bit Systems" OFF)
19-
option(OPENMP "enable openmp" ON)
19+
option(OPENMP "enable openmp" OFF)
2020
option(OPTIMIZE_DEBUG "apply optimizations also in debug mode" ON)
2121
option(CLANG_FORMAT "use clang-format" ON)
2222
option(CLANG_TIDY "use clang-tidy" OFF)
23-
option(OPTIMIZE_FOR_ARCHITECTURE "enable optimizations for specified architecture" ON)
23+
option(OPTIMIZE_FOR_ARCHITECTURE "enable optimizations for specified architecture" OFF)
2424
option(LTO_ENABLED "enable interprocedural and link time optimizations" ON)
2525
option(COMPILER_EXTENSIONS "enable compiler specific C++ extensions" OFF)
2626

Doxyfile.in

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8
3232
# title of most generated pages and in a few other places.
3333
# The default value is: My Project.
3434

35-
PROJECT_NAME = "CMAKE Template Project" # TODO ----------------------------------------------- !!! REPLACE !!!
35+
PROJECT_NAME = "Modbus TCP Client - Shared Memory"
3636

3737
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
3838
# could be handy for archiving the generated documentation or if some version
@@ -44,7 +44,7 @@ PROJECT_NUMBER =
4444
# for a project that appears at the top of each page and should give viewer a
4545
# quick idea about the purpose of the project. Keep the description short.
4646

47-
PROJECT_BRIEF = "An empty CMAKE project as template for new projects" # TODO ------------------ !!! REPLACE !!!
47+
PROJECT_BRIEF = "A modbus TCP client that stores its values in shared memory objects"
4848

4949
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
5050
# in the documentation. The maximum height of the logo should not exceed 55

README.md

Lines changed: 39 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,39 @@
1-
# CMake Project template
2-
3-
This repository is a template for CMake C++ Projects.
4-
5-
## Supported Compiilers
6-
7-
- gcc
8-
- clang
9-
10-
## Project structure
11-
12-
### Directory src
13-
Use this directory for all source files of the project.
14-
15-
### Directory libs
16-
Place libraries here. This Directory is added to the include path.
17-
18-
## Scripts
19-
20-
### check_format.sh
21-
This script checks all ```*.cpp``` and ```*.hpp``` files for conformity with the file ```.clang-format```.
22-
23-
### format.sh
24-
This script formats all ```*.cpp``` and ```*.hpp``` files in the src directory with clang format.
25-
The files are changed by it!
26-
27-
## Options
28-
29-
### Target
30-
The name of the executable that is generated.
31-
32-
### STANDARD
33-
The minimum required C++ standard: 98, 03, 11, 14, 17, 20
34-
35-
### ARCHITECTURE
36-
The CPU architecture for which the code is generated.
37-
It is only relevant if the option ```OPTIMIZE_FOR_ARCHITECTURE``` is enabled.
38-
39-
```native``` should be the best choice in most cases.
40-
41-
### BUILD_DOC
42-
Enables the automatic generation of a doxygen documentation.
43-
Doxygen must be installed on the system and a ```Doxyfile.in``` file must be provided.
44-
An additional CMake target is created.
45-
46-
### COMPILER_WARNINGS
47-
Enable/Disable compiler warnings.
48-
49-
### ENABLE_MULTITHREADING
50-
Link the default multithreading library for the current target system.
51-
Prefers ```pthread``` if available.
52-
53-
### MAKE_32_BIT_BINARY
54-
Forces the compiler to generate a 32 bit application by setting the ```-m32``` flag.
55-
56-
### OPENMP
57-
Enables the support for openmp.
58-
59-
### OPTIMIZE_DEBUG
60-
Enables Optimization ```-O3``` also in debug configuration.
61-
Should only be enabled if the resulting binary is too slow.
62-
63-
### CLANG_FORMAT
64-
Enable automatic formatting via clang-format.
65-
An additional CMake target is created.
66-
67-
### CLANG_TIDY
68-
Enable static code checks with clang tidy.
69-
An additional CMake target is created.
70-
71-
Not usable in this version due to contradictory warnings.
72-
73-
### LTO_ENABLED
74-
Enable interprocedural and link time optimizations.
75-
76-
### COMPILER_EXTENSIONS
77-
Enable compiler specific C++ extensions.
78-
Should be disabled for reasons of portability.
79-
80-
1+
# Modbus TCP client shm
2+
3+
Modbus tcp client that stores its data (registers) in shared memory objects.
4+
5+
## Build
6+
```
7+
git submodule init
8+
mkdir build
9+
cd build
10+
cmake .. -DCMAKE_CXX_COMPILER=$(which clang++)
11+
cmake -build .
12+
```
13+
14+
## Use
15+
```
16+
Modbus_TCP_client_shm [OPTION...]
17+
18+
-i, --ip arg ip to listen for incoming connections (default:
19+
0.0.0.0)
20+
-p, --port arg port to listen for incoming connections (default:
21+
502)
22+
-n, --name-prefix arg shared memory name prefix (default: modbus_)
23+
--do_registers arg number of digital output registers (default:
24+
65536)
25+
--di_registers arg number of digital input registers (default:
26+
65536)
27+
--ao_registers arg number of analog output registers (default:
28+
65536)
29+
--ai_registers arg number of analog input registers (default: 65536)
30+
-m, --monitor output all incoming and outgoing packets to
31+
stdout
32+
-r, --reconnect do not terminate if Master disconnects.
33+
-h, --help print usage
34+
```
35+
36+
## Libraries
37+
This application uses the following libraries:
38+
- cxxopts by jarro2783 (https://github.com/jarro2783/cxxopts)
39+
- libmodbus by Stéphane Raimbault (https://github.com/stephane/libmodbus)

libs/CMakeLists.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11

22
# ---------------------------------------- subdirectories --------------------------------------------------------------
33
# ======================================================================================================================
4-
5-
4+
include(../modbus.cmake)
5+
target_link_libraries(${Target} PRIVATE modbus)
66

77
# ---------------------------------------- link libraries --------------------------------------------------------------
88
# ======================================================================================================================
9+
target_link_libraries(${Target} PRIVATE rt)
910

10-
11-
# ---------------------------------------- utils lib -------------------------------------------------------------------
12-
# ======================================================================================================================
11+
add_subdirectory(cxxopts EXCLUDE_FROM_ALL)
12+
target_link_libraries(${Target} PRIVATE cxxopts)

libs/cxxopts

Submodule cxxopts added at 97ce703

libs/libmodbus

Submodule libmodbus added at 31f7791

modbus.cmake

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#
2+
# Builds the modbus library
3+
#
4+
# Outputs the following target:
5+
# modbus
6+
#
7+
include(ExternalProject)
8+
set(MODBUS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libmodbus)
9+
set(MODBUS_BIN ${CMAKE_CURRENT_BINARY_DIR}/libmodbus)
10+
set(MODBUS_STATIC_LIB ${MODBUS_BIN}/lib/libmodbus.a)
11+
set(MODBUS_INCLUDES ${MODBUS_BIN}/include)
12+
13+
file(MAKE_DIRECTORY ${MODBUS_INCLUDES})
14+
15+
ExternalProject_Add(
16+
libmodbus
17+
PREFIX ${MODBUS_BIN}
18+
SOURCE_DIR ${MODBUS_DIR}
19+
DOWNLOAD_COMMAND cd ${MODBUS_DIR} && git clean -dfX && ${MODBUS_DIR}/autogen.sh
20+
CONFIGURE_COMMAND ${MODBUS_DIR}/configure --srcdir=${MODBUS_DIR} --prefix=${MODBUS_BIN} --enable-static=yes --disable-shared
21+
BUILD_COMMAND make
22+
INSTALL_COMMAND make install
23+
BUILD_BYPRODUCTS ${MODBUS_STATIC_LIB}
24+
)
25+
26+
add_library(modbus STATIC IMPORTED GLOBAL)
27+
28+
add_dependencies(modbus libmodbus)
29+
30+
set_target_properties(modbus PROPERTIES IMPORTED_LOCATION ${MODBUS_STATIC_LIB})
31+
set_target_properties(modbus PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${MODBUS_INCLUDES})

src/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
# ======================================================================================================================
33

44
target_sources(${Target} PRIVATE main.cpp)
5+
target_sources(${Target} PRIVATE modbus_shm.cpp)
6+
target_sources(${Target} PRIVATE Modbus_TCP_Slave.cpp)
57

68

79
# ---------------------------------------- header files (*.jpp, *.h, ...) ----------------------------------------------
810
# ======================================================================================================================
11+
target_sources(${Target} PRIVATE modbus_shm.hpp)
12+
target_sources(${Target} PRIVATE Modbus_TCP_Slave.hpp)
913

1014

1115

src/Modbus_TCP_Slave.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#include "Modbus_TCP_Slave.hpp"
2+
3+
#include <stdexcept>
4+
#include <unistd.h>
5+
6+
namespace Modbus {
7+
namespace TCP {
8+
9+
static constexpr int MAX_REGS = 0x10000;
10+
11+
Slave::Slave(const std::string &ip, unsigned short port, modbus_mapping_t *mapping) {
12+
// create modbus object
13+
modbus = modbus_new_tcp(ip.c_str(), static_cast<int>(port));
14+
if (modbus == nullptr) {
15+
const std::string error_msg = modbus_strerror(errno);
16+
throw std::runtime_error("failed to create modbus instance: " + error_msg);
17+
}
18+
19+
if (mapping == nullptr) {
20+
// create new mapping with the maximum number of registers
21+
this->mapping = modbus_mapping_new(MAX_REGS, MAX_REGS, MAX_REGS, MAX_REGS);
22+
if (this->mapping == nullptr) {
23+
const std::string error_msg = modbus_strerror(errno);
24+
modbus_free(modbus);
25+
throw std::runtime_error("failed to allocate memory: " + error_msg);
26+
}
27+
delete_mapping = true;
28+
} else {
29+
// use the provided mapping object
30+
this->mapping = mapping;
31+
delete_mapping = false;
32+
}
33+
34+
// create tcp socket
35+
socket = modbus_tcp_listen(modbus, 1);
36+
if (socket == -1) {
37+
const std::string error_msg = modbus_strerror(errno);
38+
throw std::runtime_error("failed to create tcp socket: " + error_msg);
39+
}
40+
}
41+
42+
Slave::~Slave() {
43+
if (modbus != nullptr) {
44+
modbus_close(modbus);
45+
modbus_free(modbus);
46+
}
47+
if (mapping != nullptr && delete_mapping) modbus_mapping_free(mapping);
48+
if (socket != -1) { close(socket); }
49+
}
50+
51+
void Slave::set_debug(bool debug) {
52+
if (modbus_set_debug(modbus, debug)) {
53+
const std::string error_msg = modbus_strerror(errno);
54+
throw std::runtime_error("failed to enable modbus debugging mode: " + error_msg);
55+
}
56+
}
57+
58+
void Slave::connect_client() {
59+
int tmp = modbus_tcp_accept(modbus, &socket);
60+
if (tmp < 0) {
61+
const std::string error_msg = modbus_strerror(errno);
62+
throw std::runtime_error("modbus_tcp_accept failed: " + error_msg);
63+
}
64+
}
65+
66+
bool Slave::handle_request() {
67+
// receive modbus request
68+
uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
69+
int rc = modbus_receive(modbus, query);
70+
71+
if (rc > 0) {
72+
// handle request
73+
modbus_reply(modbus, query, rc, mapping);
74+
} else if (rc == -1) {
75+
if (errno == ECONNRESET) return true;
76+
77+
const std::string error_msg = modbus_strerror(errno);
78+
throw std::runtime_error("modbus_receive failed: " + error_msg + ' ' + std::to_string(errno));
79+
}
80+
81+
return false;
82+
}
83+
84+
} // namespace TCP
85+
} // namespace Modbus

0 commit comments

Comments
 (0)