Commit ea80f74094e3b313e348897a0f972fb3aa5df434

Authored by Stéphane Raimbault
1 parent 8941a84c

New API for set/get response and byte timeouts

- update documentation
- 5 new tests
- updated NEWS file
- avoid include of time.h
  1 +libmodbus 3.1.2 (2013-XX-XX)
  2 +============================
  3 +
  4 +This release introduces API changes on modbus_get_byte_timeout,
  5 +modbus_get_response_timeout, modbus_set_byte_timeout,
  6 +modbus_set_response_timeout to ease writing of language bindings.
  7 +
  8 +- New API to set/get response and byte timeouts.
  9 + New unit tests and updated documentation.
  10 +- Export Modbus function codes supported by libmodbus
  11 +- Fix bandwidth-server-one (closes #152)
  12 +- Check debug flag in RTU code
  13 +- Sync packaging with official from Debian (closes #134)
  14 +- Remove warnings caused by shadowed 'index' variable.
  15 + Thanks to Åke Forslund.
  16 +- Use accept4 in TCP PI if available
  17 +- Add documentation for tcp[_pi]_accept (closes #31)
  18 +- Fix mistake in modbus_tcp_listen documentation
  19 +- Add documentation for modbus_tcp_pi_listen
  20 +
1 21 libmodbus 3.1.1 (2013-10-06)
2 22 ============================
3 23  
... ...
doc/modbus_get_byte_timeout.txt
... ... @@ -9,28 +9,31 @@ modbus_get_byte_timeout - get timeout between bytes
9 9  
10 10 SYNOPSIS
11 11 --------
12   -*int modbus_get_byte_timeout(modbus_t *'ctx', struct timeval *'timeout');*
  12 +*int modbus_get_byte_timeout(modbus_t *'ctx', long *'to_sec', long *'to_usec');*
13 13  
14 14  
15 15 DESCRIPTION
16 16 -----------
17 17 The _modbus_get_byte_timeout()_ function shall store the timeout interval
18   -between two consecutive bytes of the same message in the 'timeout' argument.
  18 +between two consecutive bytes of the same message in the _to_sec_ and _to_usec_
  19 +arguments.
19 20  
20 21  
21 22 RETURN VALUE
22 23 ------------
23   -The function shall return 0 if successful. Otherwise it shall return -1 and set errno.
  24 +The function shall return 0 if successful. Otherwise it shall return -1 and set
  25 +errno.
24 26  
25 27  
26 28 EXAMPLE
27 29 -------
28 30 [source,c]
29 31 -------------------
30   -struct timeval byte_timeout;
  32 +long to_sec;
  33 +long to_usec;
31 34  
32 35 /* Save original timeout */
33   -modbus_get_byte_timeout(ctx, &byte_timeout);
  36 +modbus_get_byte_timeout(ctx, &to_sec, &to_usec);
34 37 -------------------
35 38  
36 39  
... ...
doc/modbus_get_response_timeout.txt
... ... @@ -9,34 +9,33 @@ modbus_get_response_timeout - get timeout for response
9 9  
10 10 SYNOPSIS
11 11 --------
12   -*int modbus_get_response_timeout(modbus_t *'ctx', struct timeval *'timeout');*
  12 +*int modbus_get_response_timeout(modbus_t *'ctx', long *'to_sec', long *'to_usec');*
13 13  
14 14  
15 15 DESCRIPTION
16 16 -----------
17   -The _modbus_get_response_timeout()_ function shall store the timeout interval
18   -used to wait for a response in the 'timeout' argument.
  17 +The _modbus_get_response_timeout()_ function shall return the timeout interval
  18 +used to wait for a response in the _to_sec_ and _to_usec_ arguments.
19 19  
20 20  
21 21 RETURN VALUE
22 22 ------------
23   -The function shall return 0 if successful. Otherwise it shall return -1 and set errno.
  23 +The function shall return 0 if successful. Otherwise it shall return -1 and set
  24 +errno.
24 25  
25 26  
26 27 EXAMPLE
27 28 -------
28 29 [source,c]
29 30 -------------------
30   -struct timeval old_response_timeout;
31   -struct timeval response_timeout;
  31 +long old_response_to_sec;
  32 +long old_response_to_usec;
32 33  
33 34 /* Save original timeout */
34   -modbus_get_response_timeout(ctx, &old_response_timeout);
  35 +modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec);
35 36  
36 37 /* Define a new and too short timeout! */
37   -response_timeout.tv_sec = 0;
38   -response_timeout.tv_usec = 0;
39   -modbus_set_response_timeout(ctx, &response_timeout);
  38 +modbus_set_response_timeout(ctx, 0, 0);
40 39 -------------------
41 40  
42 41  
... ...
doc/modbus_set_byte_timeout.txt
... ... @@ -9,23 +9,35 @@ modbus_set_byte_timeout - set timeout between bytes
9 9  
10 10 SYNOPSIS
11 11 --------
12   -*void modbus_set_byte_timeout(modbus_t *'ctx', struct timeval *'timeout');*
  12 +*void modbus_set_byte_timeout(modbus_t *'ctx', long 'to_sec', long 'to_usec');*
13 13  
14 14  
15 15 DESCRIPTION
16 16 -----------
17 17 The _modbus_set_byte_timeout()_ function shall set the timeout interval between
18   -two consecutive bytes of the same message. If the delay between is longer than
19   -the given timeout, an error will be raised.
  18 +two consecutive bytes of the same message. If the delay between bytes is longer
  19 +than the given timeout, the 'ETIMEDOUT' error will be raised by the the function
  20 +waiting for a response.
20 21  
21   -If the timeout value has a tv_sec of -1 then this timeout will not be used at
22   -all. This results in modbus_set_response_timeout governing the entire timeout
23   -duration of an operation.
  22 +The value of _to_usec_ argument must be in the range 0 to 999999.
  23 +
  24 +If _to_sec_ is set to -1 then this timeout will not be used at all. In this
  25 +case, _modbus_set_response_timeout()_ governs the entire handling of the
  26 +response, the full confirmation response must be received before expiration of
  27 +the response timeout. When a byte timeout is set, the response timeout is only
  28 +used to wait for the first byte of the respone.
24 29  
25 30  
26 31 RETURN VALUE
27 32 ------------
28   -The function shall return 0 if successful. Otherwise it shall return -1 and set errno.
  33 +The function shall return 0 if successful. Otherwise it shall return -1 and set
  34 +errno.
  35 +
  36 +
  37 +ERRORS
  38 +------
  39 +*EINVAL*::
  40 +The argument _ctx_ is NULL or _to_usec_ is not smaller than 1000000.
29 41  
30 42  
31 43 SEE ALSO
... ...
doc/modbus_set_response_timeout.txt
... ... @@ -9,35 +9,47 @@ modbus_set_response_timeout - set timeout for response
9 9  
10 10 SYNOPSIS
11 11 --------
12   -*int modbus_set_response_timeout(modbus_t *'ctx', struct timeval *'timeout');*
  12 +*int modbus_set_response_timeout(modbus_t *'ctx', long 'to_sec', long 'to_usec');*
13 13  
14 14  
15 15 DESCRIPTION
16 16 -----------
  17 +
17 18 The _modbus_set_response_timeout()_ function shall set the timeout interval used
18   -to wait for a response. If the waiting before receiving the response is longer than
19   -the given timeout, an error will be raised.
  19 +to wait for a response. When a byte timeout is set, if the waiting before
  20 +receiving the first byte of response is longer than the given timeout, the
  21 +'ETIMEDOUT' error will be raised by the function waiting for a response. When
  22 +byte timeout is disabled, the full confirmation response must be received before
  23 +expiration of the response timeout.
  24 +
  25 +The value of to_usec argument must be in the range 0 to 999999.
20 26  
21 27  
22 28 RETURN VALUE
23 29 ------------
24   -The function shall return 0 if successful. Otherwise it shall return -1 and set errno.
  30 +The function shall return 0 if successful. Otherwise it shall return -1 and set
  31 +errno.
  32 +
  33 +
  34 +ERRORS
  35 +------
  36 +*EINVAL*::
  37 +The argument _ctx_ is NULL or _to_sec_/_to_usec_ aren't equal or greater than 0 or
  38 +_to_usec_ is not smaller than 1000000.
25 39  
26 40  
27 41 EXAMPLE
28 42 -------
29 43 [source,c]
30 44 -------------------
31   -struct timeval old_response_timeout;
32   -struct timeval response_timeout;
  45 +long old_response_to_sec;
  46 +long old_response_to_usec;
33 47  
34 48 /* Save original timeout */
35   -modbus_get_response_timeout(ctx, &old_response_timeout);
  49 +modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec);
36 50  
37 51 /* Define a new and too short timeout! */
38   -response_timeout.tv_sec = 0;
39   -response_timeout.tv_usec = 0;
40   -modbus_set_response_timeout(ctx, &response_timeout);
  52 +modbus_set_response_timeout(ctx, 0, 0);
41 53 -------------------
42 54  
43 55  
... ...
src/modbus.c
... ... @@ -104,6 +104,7 @@ void _error_print(modbus_t *ctx, const char *context)
104 104  
105 105 static void _sleep_response_timeout(modbus_t *ctx)
106 106 {
  107 + /* Response timeout is always positive */
107 108 #ifdef _WIN32
108 109 /* usleep doesn't exist on Windows */
109 110 Sleep((ctx->response_timeout.tv_sec * 1000) +
... ... @@ -112,10 +113,10 @@ static void _sleep_response_timeout(modbus_t *ctx)
112 113 /* usleep source code */
113 114 struct timespec request, remaining;
114 115 request.tv_sec = ctx->response_timeout.tv_sec;
115   - request.tv_nsec = ((long int)ctx->response_timeout.tv_usec % 1000000)
116   - * 1000;
117   - while (nanosleep(&request, &remaining) == -1 && errno == EINTR)
  116 + request.tv_nsec = ((long int)ctx->response_timeout.tv_usec) * 1000;
  117 + while (nanosleep(&request, &remaining) == -1 && errno == EINTR) {
118 118 request = remaining;
  119 + }
119 120 #endif
120 121 }
121 122  
... ... @@ -459,7 +460,7 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
459 460 }
460 461 }
461 462  
462   - if (length_to_read > 0 && ctx->byte_timeout.tv_sec != -1) {
  463 + if (length_to_read > 0 && ctx->byte_timeout.tv_sec >= 0 && ctx->byte_timeout.tv_usec >= 0) {
463 464 /* If there is no character in the buffer, the allowed timeout
464 465 interval between two consecutive bytes is defined by
465 466 byte_timeout */
... ... @@ -467,6 +468,8 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
467 468 tv.tv_usec = ctx->byte_timeout.tv_usec;
468 469 p_tv = &tv;
469 470 }
  471 + /* else timeout isn't set again, the full response must be read before
  472 + expiration of response timeout (for CONFIRMATION only) */
470 473 }
471 474  
472 475 if (ctx->debug)
... ... @@ -1639,48 +1642,54 @@ int modbus_get_socket(modbus_t *ctx)
1639 1642 }
1640 1643  
1641 1644 /* Get the timeout interval used to wait for a response */
1642   -int modbus_get_response_timeout(modbus_t *ctx, struct timeval *timeout)
  1645 +int modbus_get_response_timeout(modbus_t *ctx, long *to_sec, long *to_usec)
1643 1646 {
1644 1647 if (ctx == NULL) {
1645 1648 errno = EINVAL;
1646 1649 return -1;
1647 1650 }
1648 1651  
1649   - *timeout = ctx->response_timeout;
  1652 + *to_sec = ctx->response_timeout.tv_sec;
  1653 + *to_usec = ctx->response_timeout.tv_usec;
1650 1654 return 0;
1651 1655 }
1652 1656  
1653   -int modbus_set_response_timeout(modbus_t *ctx, const struct timeval *timeout)
  1657 +int modbus_set_response_timeout(modbus_t *ctx, long to_sec, long to_usec)
1654 1658 {
1655   - if (ctx == NULL) {
  1659 + if (ctx == NULL ||
  1660 + to_sec < 0 || to_usec < 0 || to_usec > 999999) {
1656 1661 errno = EINVAL;
1657 1662 return -1;
1658 1663 }
1659 1664  
1660   - ctx->response_timeout = *timeout;
  1665 + ctx->response_timeout.tv_sec = to_sec;
  1666 + ctx->response_timeout.tv_usec = to_usec;
1661 1667 return 0;
1662 1668 }
1663 1669  
1664 1670 /* Get the timeout interval between two consecutive bytes of a message */
1665   -int modbus_get_byte_timeout(modbus_t *ctx, struct timeval *timeout)
  1671 +int modbus_get_byte_timeout(modbus_t *ctx, long *to_sec, long *to_usec)
1666 1672 {
1667 1673 if (ctx == NULL) {
1668 1674 errno = EINVAL;
1669 1675 return -1;
1670 1676 }
1671 1677  
1672   - *timeout = ctx->byte_timeout;
  1678 + *to_sec = ctx->byte_timeout.tv_sec;
  1679 + *to_usec = ctx->byte_timeout.tv_usec;
1673 1680 return 0;
1674 1681 }
1675 1682  
1676   -int modbus_set_byte_timeout(modbus_t *ctx, const struct timeval *timeout)
  1683 +int modbus_set_byte_timeout(modbus_t *ctx, long to_sec, long to_usec)
1677 1684 {
1678   - if (ctx == NULL) {
  1685 + /* Byte timeout can be disabled with negative values */
  1686 + if (ctx == NULL || to_usec > 999999) {
1679 1687 errno = EINVAL;
1680 1688 return -1;
1681 1689 }
1682 1690  
1683   - ctx->byte_timeout = *timeout;
  1691 + ctx->byte_timeout.tv_sec = to_sec;
  1692 + ctx->byte_timeout.tv_usec = to_usec;
1684 1693 return 0;
1685 1694 }
1686 1695  
... ...
src/modbus.h
... ... @@ -26,10 +26,8 @@
26 26  
27 27 #ifndef _MSC_VER
28 28 #include <stdint.h>
29   -#include <sys/time.h>
30 29 #else
31 30 #include "stdint.h"
32   -#include <time.h>
33 31 #endif
34 32  
35 33 #include "modbus-version.h"
... ... @@ -174,11 +172,11 @@ MODBUS_API int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mo
174 172 MODBUS_API int modbus_set_socket(modbus_t *ctx, int s);
175 173 MODBUS_API int modbus_get_socket(modbus_t *ctx);
176 174  
177   -MODBUS_API int modbus_get_response_timeout(modbus_t *ctx, struct timeval *timeout);
178   -MODBUS_API int modbus_set_response_timeout(modbus_t *ctx, const struct timeval *timeout);
  175 +MODBUS_API int modbus_get_response_timeout(modbus_t *ctx, long *to_sec, long *to_usec);
  176 +MODBUS_API int modbus_set_response_timeout(modbus_t *ctx, long to_sec, long to_usec);
179 177  
180   -MODBUS_API int modbus_get_byte_timeout(modbus_t *ctx, struct timeval *timeout);
181   -MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, const struct timeval *timeout);
  178 +MODBUS_API int modbus_get_byte_timeout(modbus_t *ctx, long *to_sec, long *to_usec);
  179 +MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, long to_sec, long to_usec);
182 180  
183 181 MODBUS_API int modbus_get_header_length(modbus_t *ctx);
184 182  
... ... @@ -202,8 +200,8 @@ MODBUS_API int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t
202 200 MODBUS_API int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data);
203 201 MODBUS_API int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask);
204 202 MODBUS_API int modbus_write_and_read_registers(modbus_t *ctx, int write_addr, int write_nb,
205   - const uint16_t *src, int read_addr, int read_nb,
206   - uint16_t *dest);
  203 + const uint16_t *src, int read_addr, int read_nb,
  204 + uint16_t *dest);
207 205 MODBUS_API int modbus_report_slave_id(modbus_t *ctx, uint8_t *dest);
208 206  
209 207 MODBUS_API modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits,
... ... @@ -218,9 +216,9 @@ MODBUS_API int modbus_receive_from(modbus_t *ctx, int sockfd, uint8_t *req);
218 216 MODBUS_API int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp);
219 217  
220 218 MODBUS_API int modbus_reply(modbus_t *ctx, const uint8_t *req,
221   - int req_length, modbus_mapping_t *mb_mapping);
  219 + int req_length, modbus_mapping_t *mb_mapping);
222 220 MODBUS_API int modbus_reply_exception(modbus_t *ctx, const uint8_t *req,
223   - unsigned int exception_code);
  221 + unsigned int exception_code);
224 222  
225 223 /**
226 224 * UTILS FUNCTIONS
... ...
tests/unit-test-client.c
... ... @@ -44,8 +44,8 @@ int main(int argc, char *argv[])
44 44 int rc;
45 45 float real;
46 46 uint32_t ireal;
47   - struct timeval old_response_timeout;
48   - struct timeval response_timeout;
  47 + long old_response_timeout_sec;
  48 + long old_response_timeout_usec;
49 49 int use_backend;
50 50  
51 51 if (argc > 1) {
... ... @@ -647,27 +647,82 @@ int main(int argc, char *argv[])
647 647 }
648 648  
649 649 /* Save original timeout */
650   - modbus_get_response_timeout(ctx, &old_response_timeout);
  650 + modbus_get_response_timeout(ctx, &old_response_timeout_sec, &old_response_timeout_usec);
651 651  
652   - /* Define a new and too short timeout */
653   - response_timeout.tv_sec = 0;
654   - response_timeout.tv_usec = 0;
655   - modbus_set_response_timeout(ctx, &response_timeout);
  652 + rc = modbus_set_response_timeout(ctx, -1, 0);
  653 + printf("1/6 Invalid response timeout (negative): ");
  654 + if (rc == -1 && errno == EINVAL) {
  655 + printf("OK\n");
  656 + } else {
  657 + printf("FAILED\n");
  658 + goto close;
  659 + }
  660 +
  661 + rc = modbus_set_response_timeout(ctx, 0, 1000000);
  662 + printf("2/6 Invalid response timeout (too large): ");
  663 + if (rc == -1 && errno == EINVAL) {
  664 + printf("OK\n");
  665 + } else {
  666 + printf("FAILED\n");
  667 + goto close;
  668 + }
656 669  
  670 + rc = modbus_set_byte_timeout(ctx, 0, 1000000);
  671 + printf("3/6 Invalid byte timeout (too large): ");
  672 + if (rc == -1 && errno == EINVAL) {
  673 + printf("OK\n");
  674 + } else {
  675 + printf("FAILED\n");
  676 + goto close;
  677 + }
  678 +
  679 + modbus_set_response_timeout(ctx, 0, 0);
657 680 rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS,
658 681 UT_REGISTERS_NB, tab_rp_registers);
659   - printf("4/4 Too short timeout: ");
  682 + printf("4/6 Zero response timeout: ");
660 683 if (rc == -1 && errno == ETIMEDOUT) {
661 684 printf("OK\n");
662 685 } else {
663 686 printf("FAILED (can fail on slow systems or Windows)\n");
664 687 }
665 688  
666   - /* Restore original timeout */
667   - modbus_set_response_timeout(ctx, &old_response_timeout);
668   -
669 689 /* A wait and flush operation is done by the error recovery code of
670   - * libmodbus */
  690 + * libmodbus but after a sleep of current response timeout
  691 + * so 0 can't be too short!
  692 + */
  693 + usleep(old_response_timeout_sec * 1000000 + old_response_timeout_usec);
  694 + modbus_flush(ctx);
  695 +
  696 + /* Trigger a special behaviour on server to wait for 0.5 second before
  697 + * replying whereas allowed timeout is 0.2 second */
  698 + modbus_set_response_timeout(ctx, 0, 200000);
  699 + rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_SLEEP_500_MS,
  700 + 1, tab_rp_registers);
  701 + printf("5/6 Too short response timeout (0.2s < 0.5s): ");
  702 + if (rc == -1 && errno == ETIMEDOUT) {
  703 + printf("OK\n");
  704 + } else {
  705 + printf("FAILED\n");
  706 + goto close;
  707 + }
  708 +
  709 + /* Wait for reply (0.2 + 0.4 > 0.5 s) and flush before continue */
  710 + usleep(400000);
  711 + modbus_flush(ctx);
  712 +
  713 + modbus_set_response_timeout(ctx, 0, 600000);
  714 + rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_SLEEP_500_MS,
  715 + 1, tab_rp_registers);
  716 + printf("6/6 Adequate response timeout (0.6s > 0.5s): ");
  717 + if (rc == 1) {
  718 + printf("OK\n");
  719 + } else {
  720 + printf("FAILED\n");
  721 + goto close;
  722 + }
  723 +
  724 + /* Restore original timeout */
  725 + modbus_set_response_timeout(ctx, old_response_timeout_sec, old_response_timeout_usec);
671 726  
672 727 /** BAD RESPONSE **/
673 728 printf("\nTEST BAD RESPONSE ERROR:\n");
... ... @@ -675,6 +730,7 @@ int main(int argc, char *argv[])
675 730 /* Allocate only the required space */
676 731 tab_rp_registers_bad = (uint16_t *) malloc(
677 732 UT_REGISTERS_NB_SPECIAL * sizeof(uint16_t));
  733 +
678 734 rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS,
679 735 UT_REGISTERS_NB_SPECIAL, tab_rp_registers_bad);
680 736 printf("* modbus_read_registers: ");
... ... @@ -684,14 +740,13 @@ int main(int argc, char *argv[])
684 740 printf("FAILED\n");
685 741 goto close;
686 742 }
687   -
688 743 free(tab_rp_registers_bad);
689 744  
690 745 /** MANUAL EXCEPTION **/
691 746 printf("\nTEST MANUAL EXCEPTION:\n");
692   -
693 747 rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_SPECIAL,
694 748 UT_REGISTERS_NB, tab_rp_registers);
  749 +
695 750 printf("* modbus_read_registers at special address: ");
696 751 if (rc == -1 && errno == EMBXSBUSY) {
697 752 printf("OK\n");
... ...
tests/unit-test-server.c
... ... @@ -175,6 +175,10 @@ int main(int argc, char*argv[])
175 175 printf("Reply with an invalid TID or slave\n");
176 176 modbus_send_raw_request(ctx, raw_req, RAW_REQ_LENGTH * sizeof(uint8_t));
177 177 continue;
  178 + } else if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 1)
  179 + == UT_REGISTERS_ADDRESS_SLEEP_500_MS) {
  180 + printf("Sleep 0.5 s before replying\n");
  181 + usleep(500000);
178 182 }
179 183 }
180 184  
... ...
tests/unit-test.h.in
... ... @@ -50,6 +50,8 @@ const uint16_t UT_REGISTERS_ADDRESS = 0x16B;
50 50 const uint16_t UT_REGISTERS_ADDRESS_SPECIAL = 0x6C;
51 51 /* The response of the server will contains an invalid TID or slave */
52 52 const uint16_t UT_REGISTERS_ADDRESS_INVALID_TID_OR_SLAVE = 0x6D;
  53 +/* The server will wait for 1 second before replying to test timeout */
  54 +const uint16_t UT_REGISTERS_ADDRESS_SLEEP_500_MS = 0x6E;
53 55  
54 56 const uint16_t UT_REGISTERS_NB = 0x3;
55 57 const uint16_t UT_REGISTERS_TAB[] = { 0x022B, 0x0001, 0x0064 };
... ...