Commit d1f1854338bbf480356fd074f4ca14b51d88f224

Authored by Stéphane Raimbault
1 parent ac6ba5c1

New error recovery modes: link and protocol

The two modes are complementary, MODBUS_ERROR_RECOVERY_LINK handles
errors at data link level (bad file descriptor, timeout, etc) and
MODBUS_ERROR_RECOVERY_PROTOCOL checks Modbus error (eg. invalid
function code or trame length).

This change introduces the use of the Sleep function for Windows.
Some duplicated code has been moved from backends to modbus core.
A new debug message is now available when a flush occurs.

The unit tests are now based on this error recovery code.
configure.ac
@@ -66,6 +66,7 @@ LT_INIT([disable-static win32-dll]) @@ -66,6 +66,7 @@ LT_INIT([disable-static win32-dll])
66 AC_CHECK_HEADERS([ \ 66 AC_CHECK_HEADERS([ \
67 termios.h \ 67 termios.h \
68 sys/time.h \ 68 sys/time.h \
  69 + time.h \
69 unistd.h \ 70 unistd.h \
70 errno.h \ 71 errno.h \
71 limits.h \ 72 limits.h \
src/modbus-rtu.c
@@ -297,7 +297,7 @@ int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg, @@ -297,7 +297,7 @@ int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg,
297 fprintf(stderr, "ERROR CRC received %0X != CRC calculated %0X\n", 297 fprintf(stderr, "ERROR CRC received %0X != CRC calculated %0X\n",
298 crc_received, crc_calculated); 298 crc_received, crc_calculated);
299 } 299 }
300 - if (ctx->error_recovery) { 300 + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
301 _modbus_rtu_flush(ctx); 301 _modbus_rtu_flush(ctx);
302 } 302 }
303 errno = EMBBADCRC; 303 errno = EMBBADCRC;
@@ -790,15 +790,7 @@ int _modbus_rtu_select(modbus_t *ctx, fd_set *rfds, @@ -790,15 +790,7 @@ int _modbus_rtu_select(modbus_t *ctx, fd_set *rfds,
790 } 790 }
791 791
792 if (s_rc < 0) { 792 if (s_rc < 0) {
793 - _error_print(ctx, "select");  
794 - if (ctx->error_recovery && (errno == EBADF)) {  
795 - modbus_close(ctx);  
796 - modbus_connect(ctx);  
797 - errno = EBADF;  
798 - return -1;  
799 - } else {  
800 - return -1;  
801 - } 793 + return -1;
802 } 794 }
803 #else 795 #else
804 while ((s_rc = select(ctx->s+1, rfds, NULL, NULL, tv)) == -1) { 796 while ((s_rc = select(ctx->s+1, rfds, NULL, NULL, tv)) == -1) {
@@ -810,22 +802,13 @@ int _modbus_rtu_select(modbus_t *ctx, fd_set *rfds, @@ -810,22 +802,13 @@ int _modbus_rtu_select(modbus_t *ctx, fd_set *rfds,
810 FD_ZERO(rfds); 802 FD_ZERO(rfds);
811 FD_SET(ctx->s, rfds); 803 FD_SET(ctx->s, rfds);
812 } else { 804 } else {
813 - _error_print(ctx, "select");  
814 - if (ctx->error_recovery && (errno == EBADF)) {  
815 - modbus_close(ctx);  
816 - modbus_connect(ctx);  
817 - errno = EBADF;  
818 - return -1;  
819 - } else {  
820 - return -1;  
821 - } 805 + return -1;
822 } 806 }
823 } 807 }
824 808
825 if (s_rc == 0) { 809 if (s_rc == 0) {
826 /* Timeout */ 810 /* Timeout */
827 errno = ETIMEDOUT; 811 errno = ETIMEDOUT;
828 - _error_print(ctx, "select");  
829 return -1; 812 return -1;
830 } 813 }
831 #endif 814 #endif
src/modbus-tcp.c
@@ -342,6 +342,7 @@ void _modbus_tcp_close(modbus_t *ctx) @@ -342,6 +342,7 @@ void _modbus_tcp_close(modbus_t *ctx)
342 int _modbus_tcp_flush(modbus_t *ctx) 342 int _modbus_tcp_flush(modbus_t *ctx)
343 { 343 {
344 int rc; 344 int rc;
  345 + int rc_sum = 0;
345 346
346 do { 347 do {
347 /* Extract the garbage from the socket */ 348 /* Extract the garbage from the socket */
@@ -367,12 +368,12 @@ int _modbus_tcp_flush(modbus_t *ctx) @@ -367,12 +368,12 @@ int _modbus_tcp_flush(modbus_t *ctx)
367 rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, 0); 368 rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, 0);
368 } 369 }
369 #endif 370 #endif
370 - if (ctx->debug && rc != -1) {  
371 - printf("%d bytes flushed\n", rc); 371 + if (rc > 0) {
  372 + rc_sum += rc;
372 } 373 }
373 } while (rc == MODBUS_TCP_MAX_ADU_LENGTH); 374 } while (rc == MODBUS_TCP_MAX_ADU_LENGTH);
374 375
375 - return rc; 376 + return rc_sum;
376 } 377 }
377 378
378 /* Listens for any request from one or many modbus masters in TCP */ 379 /* Listens for any request from one or many modbus masters in TCP */
@@ -565,21 +566,12 @@ int _modbus_tcp_select(modbus_t *ctx, fd_set *rfds, struct timeval *tv, int leng @@ -565,21 +566,12 @@ int _modbus_tcp_select(modbus_t *ctx, fd_set *rfds, struct timeval *tv, int leng
565 FD_ZERO(rfds); 566 FD_ZERO(rfds);
566 FD_SET(ctx->s, rfds); 567 FD_SET(ctx->s, rfds);
567 } else { 568 } else {
568 - _error_print(ctx, "select");  
569 - if (ctx->error_recovery && (errno == EBADF)) {  
570 - modbus_close(ctx);  
571 - modbus_connect(ctx);  
572 - errno = EBADF;  
573 - return -1;  
574 - } else {  
575 - return -1;  
576 - } 569 + return -1;
577 } 570 }
578 } 571 }
579 572
580 if (s_rc == 0) { 573 if (s_rc == 0) {
581 errno = ETIMEDOUT; 574 errno = ETIMEDOUT;
582 - _error_print(ctx, "select");  
583 return -1; 575 return -1;
584 } 576 }
585 577
src/modbus.c
@@ -22,11 +22,9 @@ @@ -22,11 +22,9 @@
22 #include <stdio.h> 22 #include <stdio.h>
23 #include <string.h> 23 #include <string.h>
24 #include <stdlib.h> 24 #include <stdlib.h>
25 -#ifndef _MSC_VER  
26 -#include <unistd.h>  
27 -#endif  
28 #include <errno.h> 25 #include <errno.h>
29 #include <limits.h> 26 #include <limits.h>
  27 +#include <time.h>
30 28
31 #include <config.h> 29 #include <config.h>
32 30
@@ -98,9 +96,31 @@ void _error_print(modbus_t *ctx, const char *context) @@ -98,9 +96,31 @@ void _error_print(modbus_t *ctx, const char *context)
98 } 96 }
99 } 97 }
100 98
  99 +int _sleep_and_flush(modbus_t *ctx)
  100 +{
  101 +#ifdef _WIN32
  102 + /* usleep doesn't exist on Windows */
  103 + Sleep((ctx->response_timeout.tv_sec * 1000) +
  104 + (ctx->response_timeout.tv_usec / 1000));
  105 +#else
  106 + /* usleep source code */
  107 + struct timespec request, remaining;
  108 + request.tv_sec = ctx->response_timeout.tv_sec;
  109 + request.tv_nsec = ((long int)ctx->response_timeout.tv_usec % 1000000)
  110 + * 1000;
  111 + while (nanosleep(&request, &remaining) == -1 && errno == EINTR)
  112 + request = remaining;
  113 +#endif
  114 + return modbus_flush(ctx);
  115 +}
  116 +
101 int modbus_flush(modbus_t *ctx) 117 int modbus_flush(modbus_t *ctx)
102 { 118 {
103 - return ctx->backend->flush(ctx); 119 + int rc = ctx->backend->flush(ctx);
  120 + if (rc != -1 && ctx->debug) {
  121 + printf("%d bytes flushed\n", rc);
  122 + }
  123 + return rc;
104 } 124 }
105 125
106 /* Computes the length of the expected response */ 126 /* Computes the length of the expected response */
@@ -157,13 +177,20 @@ static int send_msg(modbus_t *ctx, uint8_t *msg, int msg_length) @@ -157,13 +177,20 @@ static int send_msg(modbus_t *ctx, uint8_t *msg, int msg_length)
157 rc = ctx->backend->send(ctx, msg, msg_length); 177 rc = ctx->backend->send(ctx, msg, msg_length);
158 if (rc == -1) { 178 if (rc == -1) {
159 _error_print(ctx, NULL); 179 _error_print(ctx, NULL);
160 - if (ctx->error_recovery &&  
161 - (errno == EBADF || errno == ECONNRESET || errno == EPIPE)) {  
162 - modbus_close(ctx);  
163 - modbus_connect(ctx); 180 + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) {
  181 + int saved_errno = errno;
  182 +
  183 + if ((errno == EBADF || errno == ECONNRESET || errno == EPIPE)) {
  184 + modbus_close(ctx);
  185 + modbus_connect(ctx);
  186 + } else {
  187 + _sleep_and_flush(ctx);
  188 + }
  189 + errno = saved_errno;
164 } 190 }
165 } 191 }
166 - } while (ctx->error_recovery && rc == -1); 192 + } while ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) &&
  193 + rc == -1);
167 194
168 if (rc > 0 && rc != msg_length) { 195 if (rc > 0 && rc != msg_length) {
169 errno = EMBBADDATA; 196 errno = EMBBADDATA;
@@ -339,6 +366,18 @@ static int receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) @@ -339,6 +366,18 @@ static int receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
339 while (length_to_read != 0) { 366 while (length_to_read != 0) {
340 rc = ctx->backend->select(ctx, &rfds, p_tv, length_to_read); 367 rc = ctx->backend->select(ctx, &rfds, p_tv, length_to_read);
341 if (rc == -1) { 368 if (rc == -1) {
  369 + _error_print(ctx, "select");
  370 + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) {
  371 + int saved_errno = errno;
  372 +
  373 + if (errno == ETIMEDOUT) {
  374 + _sleep_and_flush(ctx);
  375 + } else if (errno == EBADF) {
  376 + modbus_close(ctx);
  377 + modbus_connect(ctx);
  378 + }
  379 + errno = saved_errno;
  380 + }
342 return -1; 381 return -1;
343 } 382 }
344 383
@@ -350,12 +389,14 @@ static int receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) @@ -350,12 +389,14 @@ static int receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
350 389
351 if (rc == -1) { 390 if (rc == -1) {
352 _error_print(ctx, "read"); 391 _error_print(ctx, "read");
353 - if (ctx->error_recovery && (errno == ECONNRESET ||  
354 - errno == ECONNREFUSED)) { 392 + if ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) &&
  393 + (errno == ECONNRESET || errno == ECONNREFUSED ||
  394 + errno == EBADF)) {
  395 + int saved_errno = errno;
355 modbus_close(ctx); 396 modbus_close(ctx);
356 modbus_connect(ctx); 397 modbus_connect(ctx);
357 /* Could be removed by previous calls */ 398 /* Could be removed by previous calls */
358 - errno = ECONNRESET; 399 + errno = saved_errno;
359 } 400 }
360 return -1; 401 return -1;
361 } 402 }
@@ -440,10 +481,12 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, @@ -440,10 +481,12 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req,
440 int rsp_length_computed; 481 int rsp_length_computed;
441 const int offset = ctx->backend->header_length; 482 const int offset = ctx->backend->header_length;
442 483
443 -  
444 if (ctx->backend->pre_check_confirmation) { 484 if (ctx->backend->pre_check_confirmation) {
445 rc = ctx->backend->pre_check_confirmation(ctx, req, rsp, rsp_length); 485 rc = ctx->backend->pre_check_confirmation(ctx, req, rsp, rsp_length);
446 if (rc == -1) { 486 if (rc == -1) {
  487 + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
  488 + _sleep_and_flush(ctx);
  489 + }
447 return -1; 490 return -1;
448 } 491 }
449 } 492 }
@@ -464,6 +507,9 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, @@ -464,6 +507,9 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req,
464 "Received function not corresponding to the request (%d != %d)\n", 507 "Received function not corresponding to the request (%d != %d)\n",
465 function, req[offset]); 508 function, req[offset]);
466 } 509 }
  510 + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
  511 + _sleep_and_flush(ctx);
  512 + }
467 errno = EMBBADDATA; 513 errno = EMBBADDATA;
468 return -1; 514 return -1;
469 } 515 }
@@ -509,6 +555,11 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, @@ -509,6 +555,11 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req,
509 "Quantity not corresponding to the request (%d != %d)\n", 555 "Quantity not corresponding to the request (%d != %d)\n",
510 rsp_nb_value, req_nb_value); 556 rsp_nb_value, req_nb_value);
511 } 557 }
  558 +
  559 + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
  560 + _sleep_and_flush(ctx);
  561 + }
  562 +
512 errno = EMBBADDATA; 563 errno = EMBBADDATA;
513 rc = -1; 564 rc = -1;
514 } 565 }
@@ -530,6 +581,9 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, @@ -530,6 +581,9 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req,
530 "Message length not corresponding to the computed length (%d != %d)\n", 581 "Message length not corresponding to the computed length (%d != %d)\n",
531 rsp_length, rsp_length_computed); 582 rsp_length, rsp_length_computed);
532 } 583 }
  584 + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
  585 + _sleep_and_flush(ctx);
  586 + }
533 errno = EMBBADDATA; 587 errno = EMBBADDATA;
534 rc = -1; 588 rc = -1;
535 } 589 }
@@ -1320,7 +1374,7 @@ void _modbus_init_common(modbus_t *ctx) @@ -1320,7 +1374,7 @@ void _modbus_init_common(modbus_t *ctx)
1320 ctx->s = -1; 1374 ctx->s = -1;
1321 1375
1322 ctx->debug = FALSE; 1376 ctx->debug = FALSE;
1323 - ctx->error_recovery = FALSE; 1377 + ctx->error_recovery = MODBUS_ERROR_RECOVERY_NONE;
1324 1378
1325 ctx->response_timeout.tv_sec = 0; 1379 ctx->response_timeout.tv_sec = 0;
1326 ctx->response_timeout.tv_usec = _RESPONSE_TIMEOUT; 1380 ctx->response_timeout.tv_usec = _RESPONSE_TIMEOUT;
@@ -1335,10 +1389,11 @@ int modbus_set_slave(modbus_t *ctx, int slave) @@ -1335,10 +1389,11 @@ int modbus_set_slave(modbus_t *ctx, int slave)
1335 return ctx->backend->set_slave(ctx, slave); 1389 return ctx->backend->set_slave(ctx, slave);
1336 } 1390 }
1337 1391
1338 -int modbus_set_error_recovery(modbus_t *ctx, int enabled) 1392 +int modbus_set_error_recovery(modbus_t *ctx,
  1393 + modbus_error_recovery_mode error_recovery)
1339 { 1394 {
1340 - if (enabled == TRUE || enabled == FALSE) {  
1341 - ctx->error_recovery = (uint8_t) enabled; 1395 + if (error_recovery >= 0) {
  1396 + ctx->error_recovery = (uint8_t) error_recovery;
1342 } else { 1397 } else {
1343 errno = EINVAL; 1398 errno = EINVAL;
1344 return -1; 1399 return -1;
src/modbus.h
@@ -134,8 +134,15 @@ typedef struct { @@ -134,8 +134,15 @@ typedef struct {
134 uint16_t *tab_registers; 134 uint16_t *tab_registers;
135 } modbus_mapping_t; 135 } modbus_mapping_t;
136 136
  137 +typedef enum
  138 +{
  139 + MODBUS_ERROR_RECOVERY_NONE = 0,
  140 + MODBUS_ERROR_RECOVERY_LINK = (1<<1),
  141 + MODBUS_ERROR_RECOVERY_PROTOCOL = (1<<2),
  142 +} modbus_error_recovery_type;
  143 +
137 int modbus_set_slave(modbus_t* ctx, int slave); 144 int modbus_set_slave(modbus_t* ctx, int slave);
138 -int modbus_set_error_recovery(modbus_t *ctx, int enabled); 145 +int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_type error_recovery);
139 void modbus_set_socket(modbus_t *ctx, int socket); 146 void modbus_set_socket(modbus_t *ctx, int socket);
140 int modbus_get_socket(modbus_t *ctx); 147 int modbus_get_socket(modbus_t *ctx);
141 148
tests/unit-test-client.c
@@ -73,6 +73,9 @@ int main(int argc, char *argv[]) @@ -73,6 +73,9 @@ int main(int argc, char *argv[])
73 return -1; 73 return -1;
74 } 74 }
75 modbus_set_debug(ctx, TRUE); 75 modbus_set_debug(ctx, TRUE);
  76 + modbus_set_error_recovery(ctx,
  77 + MODBUS_ERROR_RECOVERY_LINK |
  78 + MODBUS_ERROR_RECOVERY_PROTOCOL);
76 79
77 if (use_backend == RTU) { 80 if (use_backend == RTU) {
78 modbus_set_slave(ctx, SERVER_ID); 81 modbus_set_slave(ctx, SERVER_ID);
@@ -587,9 +590,8 @@ int main(int argc, char *argv[]) @@ -587,9 +590,8 @@ int main(int argc, char *argv[])
587 /* Restore original timeout */ 590 /* Restore original timeout */
588 modbus_set_response_timeout(ctx, &old_response_timeout); 591 modbus_set_response_timeout(ctx, &old_response_timeout);
589 592
590 - /* Wait for data before flushing */  
591 - usleep(500000);  
592 - modbus_flush(ctx); 593 + /* A wait and flush operation is done by the error recovery code of
  594 + * libmodbus */
593 595
594 /** BAD RESPONSE **/ 596 /** BAD RESPONSE **/
595 printf("\nTEST BAD RESPONSE ERROR:\n"); 597 printf("\nTEST BAD RESPONSE ERROR:\n");
tests/unit-test-server.c
@@ -71,7 +71,6 @@ int main(int argc, char*argv[]) @@ -71,7 +71,6 @@ int main(int argc, char*argv[])
71 header_length = modbus_get_header_length(ctx); 71 header_length = modbus_get_header_length(ctx);
72 72
73 modbus_set_debug(ctx, TRUE); 73 modbus_set_debug(ctx, TRUE);
74 - modbus_set_error_recovery(ctx, TRUE);  
75 74
76 mb_mapping = modbus_mapping_new( 75 mb_mapping = modbus_mapping_new(
77 UT_BITS_ADDRESS + UT_BITS_NB, 76 UT_BITS_ADDRESS + UT_BITS_NB,