55
66#include " Modbus_TCP_Slave.hpp"
77
8+ #include < algorithm>
9+ #include < arpa/inet.h>
10+ #include < cstring>
11+ #include < netinet/in.h>
12+ #include < netinet/tcp.h>
13+ #include < sstream>
814#include < stdexcept>
15+ #include < sys/socket.h>
16+ #include < system_error>
917#include < unistd.h>
1018
1119namespace Modbus {
1220namespace TCP {
1321
1422static constexpr int MAX_REGS = 0x10000 ;
1523
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 ) {
1725 // create modbus object
1826 modbus = modbus_new_tcp (ip.c_str (), static_cast <int >(port));
1927 if (modbus == nullptr ) {
@@ -42,6 +50,48 @@ Slave::Slave(const std::string &ip, unsigned short port, modbus_mapping_t *mappi
4250 const std::string error_msg = modbus_strerror (errno);
4351 throw std::runtime_error (" failed to create tcp socket: " + error_msg);
4452 }
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
4595}
4696
4797Slave::~Slave () {
@@ -60,12 +110,29 @@ void Slave::set_debug(bool debug) {
60110 }
61111}
62112
63- void Slave::connect_client () {
113+ std::string Slave::connect_client () {
64114 int tmp = modbus_tcp_accept (modbus, &socket);
65115 if (tmp < 0 ) {
66116 const std::string error_msg = modbus_strerror (errno);
67117 throw std::runtime_error (" modbus_tcp_accept failed: " + error_msg);
68118 }
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 ();
69136}
70137
71138bool Slave::handle_request () {
@@ -75,7 +142,11 @@ bool Slave::handle_request() {
75142
76143 if (rc > 0 ) {
77144 // 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+ }
79150 } else if (rc == -1 ) {
80151 if (errno == ECONNRESET) return true ;
81152
@@ -86,5 +157,67 @@ bool Slave::handle_request() {
86157 return false ;
87158}
88159
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+
89222} // namespace TCP
90223} // namespace Modbus
0 commit comments