5
5
6
6
#include " Modbus_TCP_Slave.hpp"
7
7
8
+ #include < algorithm>
9
+ #include < arpa/inet.h>
10
+ #include < cstring>
11
+ #include < netinet/in.h>
12
+ #include < netinet/tcp.h>
13
+ #include < sstream>
8
14
#include < stdexcept>
15
+ #include < sys/socket.h>
16
+ #include < system_error>
9
17
#include < unistd.h>
10
18
11
19
namespace Modbus {
12
20
namespace TCP {
13
21
14
22
static constexpr int MAX_REGS = 0x10000 ;
15
23
16
- Slave::Slave (const std::string &ip, unsigned short port, modbus_mapping_t *mapping) {
24
+ Slave::Slave (const std::string &ip, unsigned short port, modbus_mapping_t *mapping, std:: size_t tcp_timeout ) {
17
25
// create modbus object
18
26
modbus = modbus_new_tcp (ip.c_str (), static_cast <int >(port));
19
27
if (modbus == nullptr ) {
@@ -42,6 +50,48 @@ Slave::Slave(const std::string &ip, unsigned short port, modbus_mapping_t *mappi
42
50
const std::string error_msg = modbus_strerror (errno);
43
51
throw std::runtime_error (" failed to create tcp socket: " + error_msg);
44
52
}
53
+
54
+ // set socket options
55
+ // enable socket keepalive (--> fail if connection partner is not reachable)
56
+ int keepalive = 1 ;
57
+ int tmp = setsockopt (socket, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof (keepalive));
58
+ if (tmp != 0 ) {
59
+ throw std::system_error (errno, std::generic_category (), " Failed to set socket option SO_KEEPALIVE" );
60
+ }
61
+
62
+ #ifdef OS_LINUX
63
+ if (tcp_timeout) {
64
+ // set user timeout (~= timeout for tcp connection)
65
+ unsigned user_timeout = static_cast <unsigned >(tcp_timeout) * 1000 ;
66
+ tmp = setsockopt (socket, IPPROTO_TCP, TCP_USER_TIMEOUT, &user_timeout, sizeof (keepalive));
67
+ if (tmp != 0 ) {
68
+ throw std::system_error (errno, std::generic_category (), " Failed to set socket option TCP_USER_TIMEOUT" );
69
+ }
70
+
71
+ // start sending keepalive request after one second without request
72
+ unsigned keepidle = 1 ;
73
+ tmp = setsockopt (socket, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof (keepidle));
74
+ if (tmp != 0 ) {
75
+ throw std::system_error (errno, std::generic_category (), " Failed to set socket option TCP_KEEPIDLE" );
76
+ }
77
+
78
+ // send up to 5 keepalive requests during the timeout time, but not more than one per second
79
+ unsigned keepintvl = std::max (static_cast <unsigned >(tcp_timeout / 5 ), 1u );
80
+ tmp = setsockopt (socket, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl, sizeof (keepintvl));
81
+ if (tmp != 0 ) {
82
+ throw std::system_error (errno, std::generic_category (), " Failed to set socket option TCP_KEEPINTVL" );
83
+ }
84
+
85
+ // 5 keepalive requests if the timeout time is >= 5s; else send one request each second
86
+ unsigned keepcnt = std::min (static_cast <unsigned >(tcp_timeout), 5u );
87
+ tmp = setsockopt (socket, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof (keepcnt));
88
+ if (tmp != 0 ) {
89
+ throw std::system_error (errno, std::generic_category (), " Failed to set socket option TCP_KEEPCNT" );
90
+ }
91
+ }
92
+ #else
93
+ static_cast <void >(tcp_timeout);
94
+ #endif
45
95
}
46
96
47
97
Slave::~Slave () {
@@ -60,12 +110,29 @@ void Slave::set_debug(bool debug) {
60
110
}
61
111
}
62
112
63
- void Slave::connect_client () {
113
+ std::string Slave::connect_client () {
64
114
int tmp = modbus_tcp_accept (modbus, &socket);
65
115
if (tmp < 0 ) {
66
116
const std::string error_msg = modbus_strerror (errno);
67
117
throw std::runtime_error (" modbus_tcp_accept failed: " + error_msg);
68
118
}
119
+
120
+ struct sockaddr_in peer_addr;
121
+ socklen_t len = sizeof (peer_addr);
122
+ tmp = getpeername (modbus_get_socket (modbus), reinterpret_cast <struct sockaddr *>(&peer_addr), &len);
123
+
124
+ if (tmp < 0 ) {
125
+ const std::string error_msg = modbus_strerror (errno);
126
+ throw std::runtime_error (" getpeername failed: " + error_msg);
127
+ }
128
+
129
+ char buffer[INET_ADDRSTRLEN];
130
+ inet_ntop (peer_addr.sin_family , &peer_addr.sin_addr , buffer, sizeof (buffer));
131
+
132
+ std::ostringstream sstr;
133
+ sstr << buffer << ' :' << htons (peer_addr.sin_port );
134
+
135
+ return sstr.str ();
69
136
}
70
137
71
138
bool Slave::handle_request () {
@@ -75,7 +142,11 @@ bool Slave::handle_request() {
75
142
76
143
if (rc > 0 ) {
77
144
// handle request
78
- modbus_reply (modbus, query, rc, mapping);
145
+ int ret = modbus_reply (modbus, query, rc, mapping);
146
+ if (ret == -1 ) {
147
+ const std::string error_msg = modbus_strerror (errno);
148
+ throw std::runtime_error (" modbus_reply failed: " + error_msg + ' ' + std::to_string (errno));
149
+ }
79
150
} else if (rc == -1 ) {
80
151
if (errno == ECONNRESET) return true ;
81
152
@@ -86,5 +157,67 @@ bool Slave::handle_request() {
86
157
return false ;
87
158
}
88
159
160
+ struct timeout_t {
161
+ uint32_t sec;
162
+ uint32_t usec;
163
+ };
164
+
165
+ static inline timeout_t double_to_timeout_t (double timeout) {
166
+ timeout_t ret {};
167
+
168
+ ret.sec = static_cast <uint32_t >(timeout);
169
+
170
+ double fractional = timeout - static_cast <double >(ret.sec );
171
+ ret.usec = static_cast <uint32_t >(fractional * 1000.0 * 1000.0 );
172
+
173
+ return ret;
174
+ }
175
+
176
+ void Slave::set_byte_timeout (double timeout) {
177
+ const auto T = double_to_timeout_t (timeout);
178
+ auto ret = modbus_set_byte_timeout (modbus, T.sec , T.usec );
179
+
180
+ if (ret != 0 ) {
181
+ const std::string error_msg = modbus_strerror (errno);
182
+ throw std::runtime_error (" modbus_receive failed: " + error_msg + ' ' + std::to_string (errno));
183
+ }
184
+ }
185
+
186
+ void Slave::set_response_timeout (double timeout) {
187
+ const auto T = double_to_timeout_t (timeout);
188
+ auto ret = modbus_set_response_timeout (modbus, T.sec , T.usec );
189
+
190
+ if (ret != 0 ) {
191
+ const std::string error_msg = modbus_strerror (errno);
192
+ throw std::runtime_error (" modbus_receive failed: " + error_msg + ' ' + std::to_string (errno));
193
+ }
194
+ }
195
+
196
+ double Slave::get_byte_timeout () {
197
+ timeout_t timeout {};
198
+
199
+ auto ret = modbus_get_byte_timeout (modbus, &timeout.sec , &timeout.usec );
200
+
201
+ if (ret != 0 ) {
202
+ const std::string error_msg = modbus_strerror (errno);
203
+ throw std::runtime_error (" modbus_receive failed: " + error_msg + ' ' + std::to_string (errno));
204
+ }
205
+
206
+ return static_cast <double >(timeout.sec ) + (static_cast <double >(timeout.usec ) / (1000.0 * 1000.0 ));
207
+ }
208
+
209
+ double Slave::get_response_timeout () {
210
+ timeout_t timeout {};
211
+
212
+ auto ret = modbus_get_response_timeout (modbus, &timeout.sec , &timeout.usec );
213
+
214
+ if (ret != 0 ) {
215
+ const std::string error_msg = modbus_strerror (errno);
216
+ throw std::runtime_error (" modbus_receive failed: " + error_msg + ' ' + std::to_string (errno));
217
+ }
218
+
219
+ return static_cast <double >(timeout.sec ) + (static_cast <double >(timeout.usec ) / (1000.0 * 1000.0 ));
220
+ }
221
+
89
222
} // namespace TCP
90
223
} // namespace Modbus
0 commit comments