瀏覽代碼

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
Stéphane Raimbault 11 年之前
父節點
當前提交
c4f7a24285

+ 3 - 3
doc/modbus_get_byte_timeout.txt

@@ -9,7 +9,7 @@ modbus_get_byte_timeout - get timeout between bytes
 
 
 SYNOPSIS
 SYNOPSIS
 --------
 --------
-*int modbus_get_byte_timeout(modbus_t *'ctx', long *'to_sec', long *'to_usec');*
+*int modbus_get_byte_timeout(modbus_t *'ctx', uint32_t *'to_sec', uint32_t *'to_usec');*
 
 
 
 
 DESCRIPTION
 DESCRIPTION
@@ -29,8 +29,8 @@ EXAMPLE
 -------
 -------
 [source,c]
 [source,c]
 -------------------
 -------------------
-long to_sec;
-long to_usec;
+uint32_t to_sec;
+uint32_t to_usec;
 
 
 /* Save original timeout */
 /* Save original timeout */
 modbus_get_byte_timeout(ctx, &to_sec, &to_usec);
 modbus_get_byte_timeout(ctx, &to_sec, &to_usec);

+ 3 - 3
doc/modbus_get_response_timeout.txt

@@ -9,7 +9,7 @@ modbus_get_response_timeout - get timeout for response
 
 
 SYNOPSIS
 SYNOPSIS
 --------
 --------
-*int modbus_get_response_timeout(modbus_t *'ctx', long *'to_sec', long *'to_usec');*
+*int modbus_get_response_timeout(modbus_t *'ctx', uint32_t *'to_sec', uint32_t *'to_usec');*
 
 
 
 
 DESCRIPTION
 DESCRIPTION
@@ -28,8 +28,8 @@ EXAMPLE
 -------
 -------
 [source,c]
 [source,c]
 -------------------
 -------------------
-long old_response_to_sec;
-long old_response_to_usec;
+uint32_t old_response_to_sec;
+uint32_t old_response_to_usec;
 
 
 /* Save original timeout */
 /* Save original timeout */
 modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec);
 modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec);

+ 9 - 8
doc/modbus_set_byte_timeout.txt

@@ -9,23 +9,24 @@ modbus_set_byte_timeout - set timeout between bytes
 
 
 SYNOPSIS
 SYNOPSIS
 --------
 --------
-*void modbus_set_byte_timeout(modbus_t *'ctx', long 'to_sec', long 'to_usec');*
+*void modbus_set_byte_timeout(modbus_t *'ctx', uint32_t 'to_sec', uint32_t 'to_usec');*
 
 
 
 
 DESCRIPTION
 DESCRIPTION
 -----------
 -----------
 The _modbus_set_byte_timeout()_ function shall set the timeout interval between
 The _modbus_set_byte_timeout()_ function shall set the timeout interval between
-two consecutive bytes of the same message. If the delay between bytes is longer
-than the given timeout, the 'ETIMEDOUT' error will be raised by the the function
-waiting for a response.
+two consecutive bytes of the same message. The timeout is an upper bound on the
+amount of time elapsed before _select()_ returns, if the time elapsed is longer
+than the defined timeout, an 'ETIMEDOUT' error will be raised by the
+function waiting for a response.
 
 
 The value of _to_usec_ argument must be in the range 0 to 999999.
 The value of _to_usec_ argument must be in the range 0 to 999999.
 
 
-If _to_sec_ is set to -1 then this timeout will not be used at all. In this
-case, _modbus_set_response_timeout()_ governs the entire handling of the
+If both _to_sec_ and _to_usec_ are zero, this timeout will not be used at all.
+In this case, _modbus_set_response_timeout()_ governs the entire handling of the
 response, the full confirmation response must be received before expiration of
 response, the full confirmation response must be received before expiration of
 the response timeout. When a byte timeout is set, the response timeout is only
 the response timeout. When a byte timeout is set, the response timeout is only
-used to wait for the first byte of the response.
+used to wait for until the first byte of the response.
 
 
 
 
 RETURN VALUE
 RETURN VALUE
@@ -37,7 +38,7 @@ errno.
 ERRORS
 ERRORS
 ------
 ------
 *EINVAL*::
 *EINVAL*::
-The argument _ctx_ is NULL or _to_usec_ is not smaller than 1000000.
+The argument _ctx_ is NULL or _to_usec_ is larger than 1000000.
 
 
 
 
 SEE ALSO
 SEE ALSO

+ 12 - 12
doc/modbus_set_response_timeout.txt

@@ -9,18 +9,18 @@ modbus_set_response_timeout - set timeout for response
 
 
 SYNOPSIS
 SYNOPSIS
 --------
 --------
-*int modbus_set_response_timeout(modbus_t *'ctx', long 'to_sec', long 'to_usec');*
+*int modbus_set_response_timeout(modbus_t *'ctx', uint32_t 'to_sec', uint32_t 'to_usec');*
 
 
 
 
 DESCRIPTION
 DESCRIPTION
 -----------
 -----------
 
 
 The _modbus_set_response_timeout()_ function shall set the timeout interval used
 The _modbus_set_response_timeout()_ function shall set the timeout interval used
-to wait for a response. When a byte timeout is set, if the waiting before
-receiving the first byte of response is longer than the given timeout, the
-'ETIMEDOUT' error will be raised by the function waiting for a response. When
-byte timeout is disabled, the full confirmation response must be received before
-expiration of the response timeout.
+to wait for a response. When a byte timeout is set, if elapsed time for the
+first byte of response is longer than the given timeout, an 'ETIMEDOUT' error
+will be raised by the function waiting for a response. When byte timeout is
+disabled, the full confirmation response must be received before expiration of
+the response timeout.
 
 
 The value of to_usec argument must be in the range 0 to 999999.
 The value of to_usec argument must be in the range 0 to 999999.
 
 
@@ -34,22 +34,22 @@ errno.
 ERRORS
 ERRORS
 ------
 ------
 *EINVAL*::
 *EINVAL*::
-The argument _ctx_ is NULL or _to_sec_/_to_usec_ aren't equal or greater than 0 or
-_to_usec_ is not smaller than 1000000.
+The argument _ctx_ is NULL, or both _to_sec_ and _to_usec_ are zero, or _to_usec_
+is larger than 1000000.
 
 
 
 
 EXAMPLE
 EXAMPLE
 -------
 -------
 [source,c]
 [source,c]
 -------------------
 -------------------
-long old_response_to_sec;
-long old_response_to_usec;
+uint32_t old_response_to_sec;
+uint32_t old_response_to_usec;
 
 
 /* Save original timeout */
 /* Save original timeout */
 modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec);
 modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec);
 
 
-/* Define a new and too short timeout! */
-modbus_set_response_timeout(ctx, 0, 0);
+/* Define a new timeout of 200ms */
+modbus_set_response_timeout(ctx, 0, 200000);
 -------------------
 -------------------
 
 
 
 

+ 8 - 7
src/modbus.c

@@ -462,7 +462,8 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
             }
             }
         }
         }
 
 
-        if (length_to_read > 0 && ctx->byte_timeout.tv_sec >= 0 && ctx->byte_timeout.tv_usec >= 0) {
+        if (length_to_read > 0 &&
+            (ctx->byte_timeout.tv_sec > 0 || ctx->byte_timeout.tv_usec > 0)) {
             /* If there is no character in the buffer, the allowed timeout
             /* If there is no character in the buffer, the allowed timeout
                interval between two consecutive bytes is defined by
                interval between two consecutive bytes is defined by
                byte_timeout */
                byte_timeout */
@@ -1644,7 +1645,7 @@ int modbus_get_socket(modbus_t *ctx)
 }
 }
 
 
 /* Get the timeout interval used to wait for a response */
 /* Get the timeout interval used to wait for a response */
-int modbus_get_response_timeout(modbus_t *ctx, long *to_sec, long *to_usec)
+int modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec)
 {
 {
     if (ctx == NULL) {
     if (ctx == NULL) {
         errno = EINVAL;
         errno = EINVAL;
@@ -1656,10 +1657,10 @@ int modbus_get_response_timeout(modbus_t *ctx, long *to_sec, long *to_usec)
     return 0;
     return 0;
 }
 }
 
 
-int modbus_set_response_timeout(modbus_t *ctx, long to_sec, long to_usec)
+int modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec)
 {
 {
     if (ctx == NULL ||
     if (ctx == NULL ||
-        to_sec < 0 || to_usec < 0 || to_usec > 999999) {
+        (to_sec == 0 && to_usec == 0) || to_usec > 999999) {
         errno = EINVAL;
         errno = EINVAL;
         return -1;
         return -1;
     }
     }
@@ -1670,7 +1671,7 @@ int modbus_set_response_timeout(modbus_t *ctx, long to_sec, long to_usec)
 }
 }
 
 
 /* Get the timeout interval between two consecutive bytes of a message */
 /* Get the timeout interval between two consecutive bytes of a message */
-int modbus_get_byte_timeout(modbus_t *ctx, long *to_sec, long *to_usec)
+int modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec)
 {
 {
     if (ctx == NULL) {
     if (ctx == NULL) {
         errno = EINVAL;
         errno = EINVAL;
@@ -1682,9 +1683,9 @@ int modbus_get_byte_timeout(modbus_t *ctx, long *to_sec, long *to_usec)
     return 0;
     return 0;
 }
 }
 
 
-int modbus_set_byte_timeout(modbus_t *ctx, long to_sec, long to_usec)
+int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec)
 {
 {
-    /* Byte timeout can be disabled with negative values */
+    /* Byte timeout can be disabled when both values are zero */
     if (ctx == NULL || to_usec > 999999) {
     if (ctx == NULL || to_usec > 999999) {
         errno = EINVAL;
         errno = EINVAL;
         return -1;
         return -1;

+ 4 - 4
src/modbus.h

@@ -172,11 +172,11 @@ MODBUS_API int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mo
 MODBUS_API int modbus_set_socket(modbus_t *ctx, int s);
 MODBUS_API int modbus_set_socket(modbus_t *ctx, int s);
 MODBUS_API int modbus_get_socket(modbus_t *ctx);
 MODBUS_API int modbus_get_socket(modbus_t *ctx);
 
 
-MODBUS_API int modbus_get_response_timeout(modbus_t *ctx, long *to_sec, long *to_usec);
-MODBUS_API int modbus_set_response_timeout(modbus_t *ctx, long to_sec, long to_usec);
+MODBUS_API int modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
+MODBUS_API int modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
 
 
-MODBUS_API int modbus_get_byte_timeout(modbus_t *ctx, long *to_sec, long *to_usec);
-MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, long to_sec, long to_usec);
+MODBUS_API int modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
+MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
 
 
 MODBUS_API int modbus_get_header_length(modbus_t *ctx);
 MODBUS_API int modbus_get_header_length(modbus_t *ctx);
 
 

+ 65 - 13
tests/unit-test-client.c

@@ -44,8 +44,10 @@ int main(int argc, char *argv[])
     int rc;
     int rc;
     float real;
     float real;
     uint32_t ireal;
     uint32_t ireal;
-    long old_response_timeout_sec;
-    long old_response_timeout_usec;
+    uint32_t old_response_to_sec;
+    uint32_t old_response_to_usec;
+    uint32_t old_byte_to_sec;
+    uint32_t old_byte_to_usec;
     int use_backend;
     int use_backend;
 
 
     if (argc > 1) {
     if (argc > 1) {
@@ -647,10 +649,11 @@ int main(int argc, char *argv[])
     }
     }
 
 
     /* Save original timeout */
     /* Save original timeout */
-    modbus_get_response_timeout(ctx, &old_response_timeout_sec, &old_response_timeout_usec);
+    modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec);
+    modbus_get_byte_timeout(ctx, &old_byte_to_sec, &old_byte_to_usec);
 
 
-    rc = modbus_set_response_timeout(ctx, -1, 0);
-    printf("1/6 Invalid response timeout (negative): ");
+    rc = modbus_set_response_timeout(ctx, 0, 0);
+    printf("1/6 Invalid response timeout (zero): ");
     if (rc == -1 && errno == EINVAL) {
     if (rc == -1 && errno == EINVAL) {
         printf("OK\n");
         printf("OK\n");
     } else {
     } else {
@@ -659,7 +662,7 @@ int main(int argc, char *argv[])
     }
     }
 
 
     rc = modbus_set_response_timeout(ctx, 0, 1000000);
     rc = modbus_set_response_timeout(ctx, 0, 1000000);
-    printf("2/6 Invalid response timeout (too large): ");
+    printf("2/6 Invalid response timeout (too large us): ");
     if (rc == -1 && errno == EINVAL) {
     if (rc == -1 && errno == EINVAL) {
         printf("OK\n");
         printf("OK\n");
     } else {
     } else {
@@ -668,7 +671,7 @@ int main(int argc, char *argv[])
     }
     }
 
 
     rc = modbus_set_byte_timeout(ctx, 0, 1000000);
     rc = modbus_set_byte_timeout(ctx, 0, 1000000);
-    printf("3/6 Invalid byte timeout (too large): ");
+    printf("3/6 Invalid byte timeout (too large us): ");
     if (rc == -1 && errno == EINVAL) {
     if (rc == -1 && errno == EINVAL) {
         printf("OK\n");
         printf("OK\n");
     } else {
     } else {
@@ -676,21 +679,21 @@ int main(int argc, char *argv[])
         goto close;
         goto close;
     }
     }
 
 
-    modbus_set_response_timeout(ctx, 0, 0);
+    modbus_set_response_timeout(ctx, 0, 1);
     rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS,
     rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS,
                                UT_REGISTERS_NB, tab_rp_registers);
                                UT_REGISTERS_NB, tab_rp_registers);
-    printf("4/6 Zero response timeout: ");
+    printf("4/6 1us response timeout: ");
     if (rc == -1 && errno == ETIMEDOUT) {
     if (rc == -1 && errno == ETIMEDOUT) {
         printf("OK\n");
         printf("OK\n");
     } else {
     } else {
-        printf("FAILED (can fail on slow systems or Windows)\n");
+        printf("FAILED (can fail on some platforms)\n");
     }
     }
 
 
     /* A wait and flush operation is done by the error recovery code of
     /* A wait and flush operation is done by the error recovery code of
      * libmodbus but after a sleep of current response timeout
      * libmodbus but after a sleep of current response timeout
      * so 0 can't be too short!
      * so 0 can't be too short!
      */
      */
-    usleep(old_response_timeout_sec * 1000000 + old_response_timeout_usec);
+    usleep(old_response_to_sec * 1000000 + old_response_to_usec);
     modbus_flush(ctx);
     modbus_flush(ctx);
 
 
     /* Trigger a special behaviour on server to wait for 0.5 second before
     /* Trigger a special behaviour on server to wait for 0.5 second before
@@ -721,8 +724,57 @@ int main(int argc, char *argv[])
         goto close;
         goto close;
     }
     }
 
 
-    /* Restore original timeout */
-    modbus_set_response_timeout(ctx, old_response_timeout_sec, old_response_timeout_usec);
+    /* Disable the byte timeout.
+       The full response must be available in the 600ms interval */
+    modbus_set_byte_timeout(ctx, 0, 0);
+    rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_SLEEP_500_MS,
+                               1, tab_rp_registers);
+    printf("7/7 Disable byte timeout: ");
+    if (rc == 1) {
+        printf("OK\n");
+    } else {
+        printf("FAILED\n");
+        goto close;
+    }
+
+    /* Restore original response timeout */
+    modbus_set_response_timeout(ctx, old_response_to_sec,
+                                old_response_to_usec);
+
+    if (use_backend == TCP) {
+        /* Test server is only able to test byte timeout with the TCP backend */
+
+        /* Timeout of 3ms between bytes */
+        modbus_set_byte_timeout(ctx, 0, 3000);
+        rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS,
+                                   1, tab_rp_registers);
+        printf("1/2 Too small byte timeout (3ms < 5ms): ");
+        if (rc == -1 && errno == ETIMEDOUT) {
+            printf("OK\n");
+        } else {
+            printf("FAILED\n");
+            goto close;
+        }
+
+        /* Wait remaing bytes before flushing */
+        usleep(11 * 5000);
+        modbus_flush(ctx);
+
+        /* Timeout of 10ms between bytes */
+        modbus_set_byte_timeout(ctx, 0, 7000);
+        rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS,
+                                   1, tab_rp_registers);
+        printf("2/2 Adapted byte timeout (7ms > 5ms): ");
+        if (rc == 1) {
+            printf("OK\n");
+        } else {
+            printf("FAILED\n");
+            goto close;
+        }
+    }
+
+    /* Restore original byte timeout */
+    modbus_set_byte_timeout(ctx, old_byte_to_sec, old_byte_to_usec);
 
 
     /** BAD RESPONSE **/
     /** BAD RESPONSE **/
     printf("\nTEST BAD RESPONSE ERROR:\n");
     printf("\nTEST BAD RESPONSE ERROR:\n");

+ 17 - 0
tests/unit-test-server.c

@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <errno.h>
 #include <modbus.h>
 #include <modbus.h>
+#include <sys/socket.h>
 
 
 #include "unit-test.h"
 #include "unit-test.h"
 
 
@@ -179,6 +180,22 @@ int main(int argc, char*argv[])
                        == UT_REGISTERS_ADDRESS_SLEEP_500_MS) {
                        == UT_REGISTERS_ADDRESS_SLEEP_500_MS) {
                 printf("Sleep 0.5 s before replying\n");
                 printf("Sleep 0.5 s before replying\n");
                 usleep(500000);
                 usleep(500000);
+            } else if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 1)
+                       == UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS) {
+                /* Test low level only available in TCP mode */
+                /* Catch the reply and send reply byte a byte */
+                uint8_t req[] = "\x00\x1C\x00\x00\x00\x05\xFF\x03\x02\x00\x00";
+                int req_length = 11;
+                int w_s = modbus_get_socket(ctx);
+
+                /* Copy TID */
+                req[1] = query[1];
+                for (i=0; i < req_length; i++) {
+                    printf("(%.2X)", req[i]);
+                    usleep(500);
+                    send(w_s, req + i, 1, MSG_NOSIGNAL);
+                }
+                continue;
             }
             }
         }
         }
 
 

+ 2 - 0
tests/unit-test.h.in

@@ -52,6 +52,8 @@ const uint16_t UT_REGISTERS_ADDRESS_SPECIAL = 0x6C;
 const uint16_t UT_REGISTERS_ADDRESS_INVALID_TID_OR_SLAVE = 0x6D;
 const uint16_t UT_REGISTERS_ADDRESS_INVALID_TID_OR_SLAVE = 0x6D;
 /* The server will wait for 1 second before replying to test timeout */
 /* The server will wait for 1 second before replying to test timeout */
 const uint16_t UT_REGISTERS_ADDRESS_SLEEP_500_MS = 0x6E;
 const uint16_t UT_REGISTERS_ADDRESS_SLEEP_500_MS = 0x6E;
+/* The server will wait for 5 ms before sending each byte */
+const uint16_t UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS = 0x6F;
 
 
 const uint16_t UT_REGISTERS_NB = 0x3;
 const uint16_t UT_REGISTERS_NB = 0x3;
 const uint16_t UT_REGISTERS_TAB[] = { 0x022B, 0x0001, 0x0064 };
 const uint16_t UT_REGISTERS_TAB[] = { 0x022B, 0x0001, 0x0064 };