Commit c4f7a2428521765dac88977eb726f544f2e3b040
1 parent
8dc4e2e5
Change timeout to uint32 and add 3 byte timeout tests
- add byte timeout tests (TCP backend only) - update and improve documentation - long timeout values are now uint32_t so it changes the API to disable byte timeout
Showing
9 changed files
with
123 additions
and
50 deletions
doc/modbus_get_byte_timeout.txt
| ... | ... | @@ -9,7 +9,7 @@ modbus_get_byte_timeout - get timeout between bytes |
| 9 | 9 | |
| 10 | 10 | SYNOPSIS |
| 11 | 11 | -------- |
| 12 | -*int modbus_get_byte_timeout(modbus_t *'ctx', long *'to_sec', long *'to_usec');* | |
| 12 | +*int modbus_get_byte_timeout(modbus_t *'ctx', uint32_t *'to_sec', uint32_t *'to_usec');* | |
| 13 | 13 | |
| 14 | 14 | |
| 15 | 15 | DESCRIPTION |
| ... | ... | @@ -29,8 +29,8 @@ EXAMPLE |
| 29 | 29 | ------- |
| 30 | 30 | [source,c] |
| 31 | 31 | ------------------- |
| 32 | -long to_sec; | |
| 33 | -long to_usec; | |
| 32 | +uint32_t to_sec; | |
| 33 | +uint32_t to_usec; | |
| 34 | 34 | |
| 35 | 35 | /* Save original timeout */ |
| 36 | 36 | modbus_get_byte_timeout(ctx, &to_sec, &to_usec); | ... | ... |
doc/modbus_get_response_timeout.txt
| ... | ... | @@ -9,7 +9,7 @@ modbus_get_response_timeout - get timeout for response |
| 9 | 9 | |
| 10 | 10 | SYNOPSIS |
| 11 | 11 | -------- |
| 12 | -*int modbus_get_response_timeout(modbus_t *'ctx', long *'to_sec', long *'to_usec');* | |
| 12 | +*int modbus_get_response_timeout(modbus_t *'ctx', uint32_t *'to_sec', uint32_t *'to_usec');* | |
| 13 | 13 | |
| 14 | 14 | |
| 15 | 15 | DESCRIPTION |
| ... | ... | @@ -28,8 +28,8 @@ EXAMPLE |
| 28 | 28 | ------- |
| 29 | 29 | [source,c] |
| 30 | 30 | ------------------- |
| 31 | -long old_response_to_sec; | |
| 32 | -long old_response_to_usec; | |
| 31 | +uint32_t old_response_to_sec; | |
| 32 | +uint32_t old_response_to_usec; | |
| 33 | 33 | |
| 34 | 34 | /* Save original timeout */ |
| 35 | 35 | modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec); | ... | ... |
doc/modbus_set_byte_timeout.txt
| ... | ... | @@ -9,23 +9,24 @@ modbus_set_byte_timeout - set timeout between bytes |
| 9 | 9 | |
| 10 | 10 | SYNOPSIS |
| 11 | 11 | -------- |
| 12 | -*void modbus_set_byte_timeout(modbus_t *'ctx', long 'to_sec', long 'to_usec');* | |
| 12 | +*void modbus_set_byte_timeout(modbus_t *'ctx', uint32_t 'to_sec', uint32_t '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 bytes is longer | |
| 19 | -than the given timeout, the 'ETIMEDOUT' error will be raised by the the function | |
| 20 | -waiting for a response. | |
| 18 | +two consecutive bytes of the same message. The timeout is an upper bound on the | |
| 19 | +amount of time elapsed before _select()_ returns, if the time elapsed is longer | |
| 20 | +than the defined timeout, an 'ETIMEDOUT' error will be raised by the | |
| 21 | +function waiting for a response. | |
| 21 | 22 | |
| 22 | 23 | The value of _to_usec_ argument must be in the range 0 to 999999. |
| 23 | 24 | |
| 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 | |
| 25 | +If both _to_sec_ and _to_usec_ are zero, this timeout will not be used at all. | |
| 26 | +In this case, _modbus_set_response_timeout()_ governs the entire handling of the | |
| 26 | 27 | response, the full confirmation response must be received before expiration of |
| 27 | 28 | the response timeout. When a byte timeout is set, the response timeout is only |
| 28 | -used to wait for the first byte of the response. | |
| 29 | +used to wait for until the first byte of the response. | |
| 29 | 30 | |
| 30 | 31 | |
| 31 | 32 | RETURN VALUE |
| ... | ... | @@ -37,7 +38,7 @@ errno. |
| 37 | 38 | ERRORS |
| 38 | 39 | ------ |
| 39 | 40 | *EINVAL*:: |
| 40 | -The argument _ctx_ is NULL or _to_usec_ is not smaller than 1000000. | |
| 41 | +The argument _ctx_ is NULL or _to_usec_ is larger than 1000000. | |
| 41 | 42 | |
| 42 | 43 | |
| 43 | 44 | SEE ALSO | ... | ... |
doc/modbus_set_response_timeout.txt
| ... | ... | @@ -9,18 +9,18 @@ modbus_set_response_timeout - set timeout for response |
| 9 | 9 | |
| 10 | 10 | SYNOPSIS |
| 11 | 11 | -------- |
| 12 | -*int modbus_set_response_timeout(modbus_t *'ctx', long 'to_sec', long 'to_usec');* | |
| 12 | +*int modbus_set_response_timeout(modbus_t *'ctx', uint32_t 'to_sec', uint32_t 'to_usec');* | |
| 13 | 13 | |
| 14 | 14 | |
| 15 | 15 | DESCRIPTION |
| 16 | 16 | ----------- |
| 17 | 17 | |
| 18 | 18 | The _modbus_set_response_timeout()_ function shall set the timeout interval used |
| 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. | |
| 19 | +to wait for a response. When a byte timeout is set, if elapsed time for the | |
| 20 | +first byte of response is longer than the given timeout, an 'ETIMEDOUT' error | |
| 21 | +will be raised by the function waiting for a response. When byte timeout is | |
| 22 | +disabled, the full confirmation response must be received before expiration of | |
| 23 | +the response timeout. | |
| 24 | 24 | |
| 25 | 25 | The value of to_usec argument must be in the range 0 to 999999. |
| 26 | 26 | |
| ... | ... | @@ -34,22 +34,22 @@ errno. |
| 34 | 34 | ERRORS |
| 35 | 35 | ------ |
| 36 | 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. | |
| 37 | +The argument _ctx_ is NULL, or both _to_sec_ and _to_usec_ are zero, or _to_usec_ | |
| 38 | +is larger than 1000000. | |
| 39 | 39 | |
| 40 | 40 | |
| 41 | 41 | EXAMPLE |
| 42 | 42 | ------- |
| 43 | 43 | [source,c] |
| 44 | 44 | ------------------- |
| 45 | -long old_response_to_sec; | |
| 46 | -long old_response_to_usec; | |
| 45 | +uint32_t old_response_to_sec; | |
| 46 | +uint32_t old_response_to_usec; | |
| 47 | 47 | |
| 48 | 48 | /* Save original timeout */ |
| 49 | 49 | modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec); |
| 50 | 50 | |
| 51 | -/* Define a new and too short timeout! */ | |
| 52 | -modbus_set_response_timeout(ctx, 0, 0); | |
| 51 | +/* Define a new timeout of 200ms */ | |
| 52 | +modbus_set_response_timeout(ctx, 0, 200000); | |
| 53 | 53 | ------------------- |
| 54 | 54 | |
| 55 | 55 | ... | ... |
src/modbus.c
| ... | ... | @@ -462,7 +462,8 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) |
| 462 | 462 | } |
| 463 | 463 | } |
| 464 | 464 | |
| 465 | - if (length_to_read > 0 && ctx->byte_timeout.tv_sec >= 0 && ctx->byte_timeout.tv_usec >= 0) { | |
| 465 | + if (length_to_read > 0 && | |
| 466 | + (ctx->byte_timeout.tv_sec > 0 || ctx->byte_timeout.tv_usec > 0)) { | |
| 466 | 467 | /* If there is no character in the buffer, the allowed timeout |
| 467 | 468 | interval between two consecutive bytes is defined by |
| 468 | 469 | byte_timeout */ |
| ... | ... | @@ -1644,7 +1645,7 @@ int modbus_get_socket(modbus_t *ctx) |
| 1644 | 1645 | } |
| 1645 | 1646 | |
| 1646 | 1647 | /* Get the timeout interval used to wait for a response */ |
| 1647 | -int modbus_get_response_timeout(modbus_t *ctx, long *to_sec, long *to_usec) | |
| 1648 | +int modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec) | |
| 1648 | 1649 | { |
| 1649 | 1650 | if (ctx == NULL) { |
| 1650 | 1651 | errno = EINVAL; |
| ... | ... | @@ -1656,10 +1657,10 @@ int modbus_get_response_timeout(modbus_t *ctx, long *to_sec, long *to_usec) |
| 1656 | 1657 | return 0; |
| 1657 | 1658 | } |
| 1658 | 1659 | |
| 1659 | -int modbus_set_response_timeout(modbus_t *ctx, long to_sec, long to_usec) | |
| 1660 | +int modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec) | |
| 1660 | 1661 | { |
| 1661 | 1662 | if (ctx == NULL || |
| 1662 | - to_sec < 0 || to_usec < 0 || to_usec > 999999) { | |
| 1663 | + (to_sec == 0 && to_usec == 0) || to_usec > 999999) { | |
| 1663 | 1664 | errno = EINVAL; |
| 1664 | 1665 | return -1; |
| 1665 | 1666 | } |
| ... | ... | @@ -1670,7 +1671,7 @@ int modbus_set_response_timeout(modbus_t *ctx, long to_sec, long to_usec) |
| 1670 | 1671 | } |
| 1671 | 1672 | |
| 1672 | 1673 | /* Get the timeout interval between two consecutive bytes of a message */ |
| 1673 | -int modbus_get_byte_timeout(modbus_t *ctx, long *to_sec, long *to_usec) | |
| 1674 | +int modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec) | |
| 1674 | 1675 | { |
| 1675 | 1676 | if (ctx == NULL) { |
| 1676 | 1677 | errno = EINVAL; |
| ... | ... | @@ -1682,9 +1683,9 @@ int modbus_get_byte_timeout(modbus_t *ctx, long *to_sec, long *to_usec) |
| 1682 | 1683 | return 0; |
| 1683 | 1684 | } |
| 1684 | 1685 | |
| 1685 | -int modbus_set_byte_timeout(modbus_t *ctx, long to_sec, long to_usec) | |
| 1686 | +int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec) | |
| 1686 | 1687 | { |
| 1687 | - /* Byte timeout can be disabled with negative values */ | |
| 1688 | + /* Byte timeout can be disabled when both values are zero */ | |
| 1688 | 1689 | if (ctx == NULL || to_usec > 999999) { |
| 1689 | 1690 | errno = EINVAL; |
| 1690 | 1691 | return -1; | ... | ... |
src/modbus.h
| ... | ... | @@ -172,11 +172,11 @@ MODBUS_API int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mo |
| 172 | 172 | MODBUS_API int modbus_set_socket(modbus_t *ctx, int s); |
| 173 | 173 | MODBUS_API int modbus_get_socket(modbus_t *ctx); |
| 174 | 174 | |
| 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); | |
| 175 | +MODBUS_API int modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); | |
| 176 | +MODBUS_API int modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); | |
| 177 | 177 | |
| 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); | |
| 178 | +MODBUS_API int modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); | |
| 179 | +MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); | |
| 180 | 180 | |
| 181 | 181 | MODBUS_API int modbus_get_header_length(modbus_t *ctx); |
| 182 | 182 | ... | ... |
tests/unit-test-client.c
| ... | ... | @@ -44,8 +44,10 @@ int main(int argc, char *argv[]) |
| 44 | 44 | int rc; |
| 45 | 45 | float real; |
| 46 | 46 | uint32_t ireal; |
| 47 | - long old_response_timeout_sec; | |
| 48 | - long old_response_timeout_usec; | |
| 47 | + uint32_t old_response_to_sec; | |
| 48 | + uint32_t old_response_to_usec; | |
| 49 | + uint32_t old_byte_to_sec; | |
| 50 | + uint32_t old_byte_to_usec; | |
| 49 | 51 | int use_backend; |
| 50 | 52 | |
| 51 | 53 | if (argc > 1) { |
| ... | ... | @@ -647,10 +649,11 @@ int main(int argc, char *argv[]) |
| 647 | 649 | } |
| 648 | 650 | |
| 649 | 651 | /* Save original timeout */ |
| 650 | - modbus_get_response_timeout(ctx, &old_response_timeout_sec, &old_response_timeout_usec); | |
| 652 | + modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec); | |
| 653 | + modbus_get_byte_timeout(ctx, &old_byte_to_sec, &old_byte_to_usec); | |
| 651 | 654 | |
| 652 | - rc = modbus_set_response_timeout(ctx, -1, 0); | |
| 653 | - printf("1/6 Invalid response timeout (negative): "); | |
| 655 | + rc = modbus_set_response_timeout(ctx, 0, 0); | |
| 656 | + printf("1/6 Invalid response timeout (zero): "); | |
| 654 | 657 | if (rc == -1 && errno == EINVAL) { |
| 655 | 658 | printf("OK\n"); |
| 656 | 659 | } else { |
| ... | ... | @@ -659,7 +662,7 @@ int main(int argc, char *argv[]) |
| 659 | 662 | } |
| 660 | 663 | |
| 661 | 664 | rc = modbus_set_response_timeout(ctx, 0, 1000000); |
| 662 | - printf("2/6 Invalid response timeout (too large): "); | |
| 665 | + printf("2/6 Invalid response timeout (too large us): "); | |
| 663 | 666 | if (rc == -1 && errno == EINVAL) { |
| 664 | 667 | printf("OK\n"); |
| 665 | 668 | } else { |
| ... | ... | @@ -668,7 +671,7 @@ int main(int argc, char *argv[]) |
| 668 | 671 | } |
| 669 | 672 | |
| 670 | 673 | rc = modbus_set_byte_timeout(ctx, 0, 1000000); |
| 671 | - printf("3/6 Invalid byte timeout (too large): "); | |
| 674 | + printf("3/6 Invalid byte timeout (too large us): "); | |
| 672 | 675 | if (rc == -1 && errno == EINVAL) { |
| 673 | 676 | printf("OK\n"); |
| 674 | 677 | } else { |
| ... | ... | @@ -676,21 +679,21 @@ int main(int argc, char *argv[]) |
| 676 | 679 | goto close; |
| 677 | 680 | } |
| 678 | 681 | |
| 679 | - modbus_set_response_timeout(ctx, 0, 0); | |
| 682 | + modbus_set_response_timeout(ctx, 0, 1); | |
| 680 | 683 | rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, |
| 681 | 684 | UT_REGISTERS_NB, tab_rp_registers); |
| 682 | - printf("4/6 Zero response timeout: "); | |
| 685 | + printf("4/6 1us response timeout: "); | |
| 683 | 686 | if (rc == -1 && errno == ETIMEDOUT) { |
| 684 | 687 | printf("OK\n"); |
| 685 | 688 | } else { |
| 686 | - printf("FAILED (can fail on slow systems or Windows)\n"); | |
| 689 | + printf("FAILED (can fail on some platforms)\n"); | |
| 687 | 690 | } |
| 688 | 691 | |
| 689 | 692 | /* A wait and flush operation is done by the error recovery code of |
| 690 | 693 | * libmodbus but after a sleep of current response timeout |
| 691 | 694 | * so 0 can't be too short! |
| 692 | 695 | */ |
| 693 | - usleep(old_response_timeout_sec * 1000000 + old_response_timeout_usec); | |
| 696 | + usleep(old_response_to_sec * 1000000 + old_response_to_usec); | |
| 694 | 697 | modbus_flush(ctx); |
| 695 | 698 | |
| 696 | 699 | /* Trigger a special behaviour on server to wait for 0.5 second before |
| ... | ... | @@ -721,8 +724,57 @@ int main(int argc, char *argv[]) |
| 721 | 724 | goto close; |
| 722 | 725 | } |
| 723 | 726 | |
| 724 | - /* Restore original timeout */ | |
| 725 | - modbus_set_response_timeout(ctx, old_response_timeout_sec, old_response_timeout_usec); | |
| 727 | + /* Disable the byte timeout. | |
| 728 | + The full response must be available in the 600ms interval */ | |
| 729 | + modbus_set_byte_timeout(ctx, 0, 0); | |
| 730 | + rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_SLEEP_500_MS, | |
| 731 | + 1, tab_rp_registers); | |
| 732 | + printf("7/7 Disable byte timeout: "); | |
| 733 | + if (rc == 1) { | |
| 734 | + printf("OK\n"); | |
| 735 | + } else { | |
| 736 | + printf("FAILED\n"); | |
| 737 | + goto close; | |
| 738 | + } | |
| 739 | + | |
| 740 | + /* Restore original response timeout */ | |
| 741 | + modbus_set_response_timeout(ctx, old_response_to_sec, | |
| 742 | + old_response_to_usec); | |
| 743 | + | |
| 744 | + if (use_backend == TCP) { | |
| 745 | + /* Test server is only able to test byte timeout with the TCP backend */ | |
| 746 | + | |
| 747 | + /* Timeout of 3ms between bytes */ | |
| 748 | + modbus_set_byte_timeout(ctx, 0, 3000); | |
| 749 | + rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS, | |
| 750 | + 1, tab_rp_registers); | |
| 751 | + printf("1/2 Too small byte timeout (3ms < 5ms): "); | |
| 752 | + if (rc == -1 && errno == ETIMEDOUT) { | |
| 753 | + printf("OK\n"); | |
| 754 | + } else { | |
| 755 | + printf("FAILED\n"); | |
| 756 | + goto close; | |
| 757 | + } | |
| 758 | + | |
| 759 | + /* Wait remaing bytes before flushing */ | |
| 760 | + usleep(11 * 5000); | |
| 761 | + modbus_flush(ctx); | |
| 762 | + | |
| 763 | + /* Timeout of 10ms between bytes */ | |
| 764 | + modbus_set_byte_timeout(ctx, 0, 7000); | |
| 765 | + rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS, | |
| 766 | + 1, tab_rp_registers); | |
| 767 | + printf("2/2 Adapted byte timeout (7ms > 5ms): "); | |
| 768 | + if (rc == 1) { | |
| 769 | + printf("OK\n"); | |
| 770 | + } else { | |
| 771 | + printf("FAILED\n"); | |
| 772 | + goto close; | |
| 773 | + } | |
| 774 | + } | |
| 775 | + | |
| 776 | + /* Restore original byte timeout */ | |
| 777 | + modbus_set_byte_timeout(ctx, old_byte_to_sec, old_byte_to_usec); | |
| 726 | 778 | |
| 727 | 779 | /** BAD RESPONSE **/ |
| 728 | 780 | printf("\nTEST BAD RESPONSE ERROR:\n"); | ... | ... |
tests/unit-test-server.c
| ... | ... | @@ -21,6 +21,7 @@ |
| 21 | 21 | #include <stdlib.h> |
| 22 | 22 | #include <errno.h> |
| 23 | 23 | #include <modbus.h> |
| 24 | +#include <sys/socket.h> | |
| 24 | 25 | |
| 25 | 26 | #include "unit-test.h" |
| 26 | 27 | |
| ... | ... | @@ -179,6 +180,22 @@ int main(int argc, char*argv[]) |
| 179 | 180 | == UT_REGISTERS_ADDRESS_SLEEP_500_MS) { |
| 180 | 181 | printf("Sleep 0.5 s before replying\n"); |
| 181 | 182 | usleep(500000); |
| 183 | + } else if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 1) | |
| 184 | + == UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS) { | |
| 185 | + /* Test low level only available in TCP mode */ | |
| 186 | + /* Catch the reply and send reply byte a byte */ | |
| 187 | + uint8_t req[] = "\x00\x1C\x00\x00\x00\x05\xFF\x03\x02\x00\x00"; | |
| 188 | + int req_length = 11; | |
| 189 | + int w_s = modbus_get_socket(ctx); | |
| 190 | + | |
| 191 | + /* Copy TID */ | |
| 192 | + req[1] = query[1]; | |
| 193 | + for (i=0; i < req_length; i++) { | |
| 194 | + printf("(%.2X)", req[i]); | |
| 195 | + usleep(500); | |
| 196 | + send(w_s, req + i, 1, MSG_NOSIGNAL); | |
| 197 | + } | |
| 198 | + continue; | |
| 182 | 199 | } |
| 183 | 200 | } |
| 184 | 201 | ... | ... |
tests/unit-test.h.in
| ... | ... | @@ -52,6 +52,8 @@ const uint16_t UT_REGISTERS_ADDRESS_SPECIAL = 0x6C; |
| 52 | 52 | const uint16_t UT_REGISTERS_ADDRESS_INVALID_TID_OR_SLAVE = 0x6D; |
| 53 | 53 | /* The server will wait for 1 second before replying to test timeout */ |
| 54 | 54 | const uint16_t UT_REGISTERS_ADDRESS_SLEEP_500_MS = 0x6E; |
| 55 | +/* The server will wait for 5 ms before sending each byte */ | |
| 56 | +const uint16_t UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS = 0x6F; | |
| 55 | 57 | |
| 56 | 58 | const uint16_t UT_REGISTERS_NB = 0x3; |
| 57 | 59 | const uint16_t UT_REGISTERS_TAB[] = { 0x022B, 0x0001, 0x0064 }; | ... | ... |