Эх сурвалжийг харах

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 жил өмнө
parent
commit
c4f7a24285

+ 3 - 3
doc/modbus_get_byte_timeout.txt

@@ -9,7 +9,7 @@ modbus_get_byte_timeout - get timeout between bytes
 
 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
@@ -29,8 +29,8 @@ EXAMPLE
 -------
 [source,c]
 -------------------
-long to_sec;
-long to_usec;
+uint32_t to_sec;
+uint32_t to_usec;
 
 /* Save original timeout */
 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
 --------
-*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
@@ -28,8 +28,8 @@ EXAMPLE
 -------
 [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 */
 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
 --------
-*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
 -----------
 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.
 
-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
 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
@@ -37,7 +38,7 @@ errno.
 ERRORS
 ------
 *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

+ 12 - 12
doc/modbus_set_response_timeout.txt

@@ -9,18 +9,18 @@ modbus_set_response_timeout - set timeout for response
 
 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
 -----------
 
 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.
 
@@ -34,22 +34,22 @@ errno.
 ERRORS
 ------
 *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
 -------
 [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 */
 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
                interval between two consecutive bytes is defined by
                byte_timeout */
@@ -1644,7 +1645,7 @@ int modbus_get_socket(modbus_t *ctx)
 }
 
 /* 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) {
         errno = EINVAL;
@@ -1656,10 +1657,10 @@ int modbus_get_response_timeout(modbus_t *ctx, long *to_sec, long *to_usec)
     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 ||
-        to_sec < 0 || to_usec < 0 || to_usec > 999999) {
+        (to_sec == 0 && to_usec == 0) || to_usec > 999999) {
         errno = EINVAL;
         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 */
-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) {
         errno = EINVAL;
@@ -1682,9 +1683,9 @@ int modbus_get_byte_timeout(modbus_t *ctx, long *to_sec, long *to_usec)
     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) {
         errno = EINVAL;
         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_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);
 

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

@@ -44,8 +44,10 @@ int main(int argc, char *argv[])
     int rc;
     float real;
     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;
 
     if (argc > 1) {
@@ -647,10 +649,11 @@ int main(int argc, char *argv[])
     }
 
     /* 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) {
         printf("OK\n");
     } else {
@@ -659,7 +662,7 @@ int main(int argc, char *argv[])
     }
 
     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) {
         printf("OK\n");
     } else {
@@ -668,7 +671,7 @@ int main(int argc, char *argv[])
     }
 
     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) {
         printf("OK\n");
     } else {
@@ -676,21 +679,21 @@ int main(int argc, char *argv[])
         goto close;
     }
 
-    modbus_set_response_timeout(ctx, 0, 0);
+    modbus_set_response_timeout(ctx, 0, 1);
     rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS,
                                UT_REGISTERS_NB, tab_rp_registers);
-    printf("4/6 Zero response timeout: ");
+    printf("4/6 1us response timeout: ");
     if (rc == -1 && errno == ETIMEDOUT) {
         printf("OK\n");
     } 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
      * libmodbus but after a sleep of current response timeout
      * 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);
 
     /* 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;
     }
 
-    /* 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 **/
     printf("\nTEST BAD RESPONSE ERROR:\n");

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

@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <errno.h>
 #include <modbus.h>
+#include <sys/socket.h>
 
 #include "unit-test.h"
 
@@ -179,6 +180,22 @@ int main(int argc, char*argv[])
                        == UT_REGISTERS_ADDRESS_SLEEP_500_MS) {
                 printf("Sleep 0.5 s before replying\n");
                 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;
 /* The server will wait for 1 second before replying to test timeout */
 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_TAB[] = { 0x022B, 0x0001, 0x0064 };