Bladeren bron

New RTU receive() to ignore confirmation from other slaves (closes #18)

- export new symbols to create specific behavior (RTU)
- the flag can't only be set by slaves
- new tests
Stéphane Raimbault 13 jaren geleden
bovenliggende
commit
c1e2e0b319
6 gewijzigde bestanden met toevoegingen van 96 en 28 verwijderingen
  1. 14 0
      src/modbus-private.h
  2. 2 0
      src/modbus-rtu-private.h
  3. 28 0
      src/modbus-rtu.c
  4. 6 0
      src/modbus-tcp.c
  5. 15 21
      src/modbus.c
  6. 31 7
      tests/unit-test-client.c

+ 14 - 0
src/modbus-private.h

@@ -68,6 +68,18 @@ typedef enum {
     _MODBUS_BACKEND_TYPE_TCP
 } modbus_bakend_type_t;
 
+/*
+ *  ---------- Request     Indication ----------
+ *  | Client | ---------------------->| Server |
+ *  ---------- Confirmation  Response ----------
+ */
+typedef enum {
+    /* Request message on the server side */
+    MSG_INDICATION,
+    /* Request message on the client side */
+    MSG_CONFIRMATION
+} msg_type_t;
+
 /* This structure reduces the number of params in functions and so
  * optimizes the speed of execution (~ 37%). */
 typedef struct _sft {
@@ -88,6 +100,7 @@ typedef struct _modbus_backend {
     int (*prepare_response_tid) (const uint8_t *req, int *req_length);
     int (*send_msg_pre) (uint8_t *req, int req_length);
     ssize_t (*send) (modbus_t *ctx, const uint8_t *req, int req_length);
+    int (*receive) (modbus_t *ctx, uint8_t *req);
     ssize_t (*recv) (modbus_t *ctx, uint8_t *rsp, int rsp_length);
     int (*check_integrity) (modbus_t *ctx, uint8_t *msg,
                             const int msg_length);
@@ -114,6 +127,7 @@ struct _modbus {
 
 void _modbus_init_common(modbus_t *ctx);
 void _error_print(modbus_t *ctx, const char *context);
+int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type);
 
 #ifndef HAVE_STRLCPY
 size_t strlcpy(char *dest, const char *src, size_t dest_size);

+ 2 - 0
src/modbus-rtu-private.h

@@ -88,6 +88,8 @@ typedef struct _modbus_rtu {
 #if HAVE_DECL_TIOCM_RTS
     int rts;
 #endif
+    /* To handle many slaves on the same link */
+    int confirmation_to_ignore;
 } modbus_rtu_t;
 
 #endif /* _MODBUS_RTU_PRIVATE_H_ */

+ 28 - 0
src/modbus-rtu.c

@@ -309,6 +309,29 @@ ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length)
 #endif
 }
 
+int _modbus_rtu_receive(modbus_t *ctx, uint8_t *req)
+{
+    int rc;
+    modbus_rtu_t *ctx_rtu = ctx->backend_data;
+
+    if (ctx_rtu->confirmation_to_ignore) {
+        _modbus_receive_msg(ctx, req, MSG_CONFIRMATION);
+        /* Ignore errors and reset the flag */
+        ctx_rtu->confirmation_to_ignore = FALSE;
+        rc = 0;
+        if (ctx->debug) {
+            printf("Confirmation to ignore\n");
+        }
+    } else {
+        rc = _modbus_receive_msg(ctx, req, MSG_INDICATION);
+        if (rc == 0) {
+            /* The next expected message is a confirmation to ignore */
+            ctx_rtu->confirmation_to_ignore = TRUE;
+        }
+    }
+    return rc;
+}
+
 ssize_t _modbus_rtu_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length)
 {
 #if defined(_WIN32)
@@ -354,6 +377,7 @@ int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg,
         if (ctx->debug) {
             printf("Request for slave %d ignored (not %d)\n", slave, ctx->slave);
         }
+
         return 0;
     }
 
@@ -368,6 +392,7 @@ int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg,
             fprintf(stderr, "ERROR CRC received %0X != CRC calculated %0X\n",
                     crc_received, crc_calculated);
         }
+
         if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
             _modbus_rtu_flush(ctx);
         }
@@ -970,6 +995,7 @@ const modbus_backend_t _modbus_rtu_backend = {
     _modbus_rtu_prepare_response_tid,
     _modbus_rtu_send_msg_pre,
     _modbus_rtu_send,
+    _modbus_rtu_receive,
     _modbus_rtu_recv,
     _modbus_rtu_check_integrity,
     _modbus_rtu_pre_check_confirmation,
@@ -1032,5 +1058,7 @@ modbus_t* modbus_new_rtu(const char *device,
     ctx_rtu->rts = MODBUS_RTU_RTS_NONE;
 #endif
 
+    ctx_rtu->confirmation_to_ignore = FALSE;
+
     return ctx;
 }

+ 6 - 0
src/modbus-tcp.c

@@ -178,6 +178,10 @@ ssize_t _modbus_tcp_send(modbus_t *ctx, const uint8_t *req, int req_length)
     return send(ctx->s, (const char*)req, req_length, MSG_NOSIGNAL);
 }
 
+int _modbus_tcp_receive(modbus_t *ctx, uint8_t *req) {
+    return _modbus_receive_msg(ctx, req, MSG_INDICATION);
+}
+
 ssize_t _modbus_tcp_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length) {
     return recv(ctx->s, (char *)rsp, rsp_length, 0);
 }
@@ -597,6 +601,7 @@ const modbus_backend_t _modbus_tcp_backend = {
     _modbus_tcp_prepare_response_tid,
     _modbus_tcp_send_msg_pre,
     _modbus_tcp_send,
+    _modbus_tcp_receive,
     _modbus_tcp_recv,
     _modbus_tcp_check_integrity,
     _modbus_tcp_pre_check_confirmation,
@@ -618,6 +623,7 @@ const modbus_backend_t _modbus_tcp_pi_backend = {
     _modbus_tcp_prepare_response_tid,
     _modbus_tcp_send_msg_pre,
     _modbus_tcp_send,
+    _modbus_tcp_receive,
     _modbus_tcp_recv,
     _modbus_tcp_check_integrity,
     _modbus_tcp_pre_check_confirmation,

+ 15 - 21
src/modbus.c

@@ -26,6 +26,7 @@
 #include <errno.h>
 #include <limits.h>
 #include <time.h>
+#include <unistd.h>
 
 #include <config.h>
 
@@ -233,17 +234,10 @@ int modbus_send_raw_request(modbus_t *ctx, uint8_t *raw_req, int raw_req_length)
 }
 
 /*
-    ---------- Request     Indication ----------
-    | Client | ---------------------->| Server |
-    ---------- Confirmation  Response ----------
-*/
-
-typedef enum {
-    /* Request message on the server side */
-    MSG_INDICATION,
-    /* Request message on the client side */
-    MSG_CONFIRMATION
-} msg_type_t;
+ *  ---------- Request     Indication ----------
+ *  | Client | ---------------------->| Server |
+ *  ---------- Confirmation  Response ----------
+ */
 
 /* Computes the length to read after the function received */
 static uint8_t compute_meta_length_after_function(int function,
@@ -329,7 +323,7 @@ static int compute_data_length_after_meta(modbus_t *ctx, uint8_t *msg,
    - read() or recv() error codes
 */
 
-static int receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
+int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
 {
     int rc;
     fd_set rfds;
@@ -462,7 +456,7 @@ static int receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
 /* Receive the request from a modbus master */
 int modbus_receive(modbus_t *ctx, uint8_t *req)
 {
-    return receive_msg(ctx, req, MSG_INDICATION);
+    return ctx->backend->receive(ctx, req);
 }
 
 /* Receives the confirmation.
@@ -475,7 +469,7 @@ int modbus_receive(modbus_t *ctx, uint8_t *req)
 */
 int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp)
 {
-    return receive_msg(ctx, rsp, MSG_CONFIRMATION);
+    return _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
 }
 
 static int check_confirmation(modbus_t *ctx, uint8_t *req,
@@ -965,7 +959,7 @@ static int read_io_status(modbus_t *ctx, int function,
         int offset;
         int offset_end;
 
-        rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
+        rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
         if (rc == -1)
             return -1;
 
@@ -1064,7 +1058,7 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb,
         int offset;
         int i;
 
-        rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
+        rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
         if (rc == -1)
             return -1;
 
@@ -1140,7 +1134,7 @@ static int write_single(modbus_t *ctx, int function, int addr, int value)
         /* Used by write_bit and write_register */
         uint8_t rsp[_MIN_REQ_LENGTH];
 
-        rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
+        rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
         if (rc == -1)
             return -1;
 
@@ -1211,7 +1205,7 @@ int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src)
     if (rc > 0) {
         uint8_t rsp[MAX_MESSAGE_LENGTH];
 
-        rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
+        rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
         if (rc == -1)
             return -1;
 
@@ -1257,7 +1251,7 @@ int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src)
     if (rc > 0) {
         uint8_t rsp[MAX_MESSAGE_LENGTH];
 
-        rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
+        rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
         if (rc == -1)
             return -1;
 
@@ -1320,7 +1314,7 @@ int modbus_write_and_read_registers(modbus_t *ctx,
     if (rc > 0) {
         int offset;
 
-        rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
+        rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
         if (rc == -1)
             return -1;
 
@@ -1361,7 +1355,7 @@ int modbus_report_slave_id(modbus_t *ctx, uint8_t *dest)
         int offset;
         uint8_t rsp[MAX_MESSAGE_LENGTH];
 
-        rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
+        rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
         if (rc == -1)
             return -1;
 

+ 31 - 7
tests/unit-test-client.c

@@ -491,8 +491,12 @@ int main(int argc, char *argv[])
                                UT_REGISTERS_NB, tab_rp_registers);
     if (use_backend == RTU) {
         const int RAW_REQ_LENGTH = 6;
-        uint8_t raw_req[] = { INVALID_SERVER_ID, 0x03, 0x00, 0x01, 0xFF, 0xFF };
-        uint8_t rsp[MODBUS_TCP_MAX_ADU_LENGTH];
+        uint8_t raw_req[] = { INVALID_SERVER_ID, 0x03, 0x00, 0x01, 0x01, 0x01 };
+        /* Too many points */
+        uint8_t raw_invalid_req[] = { INVALID_SERVER_ID, 0x03, 0x00, 0x01, 0xFF, 0xFF };
+        const int RAW_REP_LENGTH = 7;
+        uint8_t raw_rep[] = { INVALID_SERVER_ID, 0x03, 0x04, 0, 0, 0, 0 };
+        uint8_t rsp[MODBUS_RTU_MAX_ADU_LENGTH];
 
         /* No response in RTU mode */
         printf("1/5-A No response from slave %d: ", INVALID_SERVER_ID);
@@ -504,12 +508,19 @@ int main(int argc, char *argv[])
             goto close;
         }
 
-        /* Send an invalid query with a wrong slave ID */
-        modbus_send_raw_request(ctx, raw_req,
-                                RAW_REQ_LENGTH * sizeof(uint8_t));
+        /* The slave raises a timeout on a confirmation to ignore because if an
+         * indication for another slave is received, a confirmation must follow */
+
+
+        /* Send a pair of indication/confimration to the slave with a different
+         * slave ID to simulate a communication on a RS485 bus. At first, the
+         * slave will see the indication message then the confirmation, and it must
+         * ignore both. */
+        modbus_send_raw_request(ctx, raw_req, RAW_REQ_LENGTH * sizeof(uint8_t));
+        modbus_send_raw_request(ctx, raw_rep, RAW_REP_LENGTH * sizeof(uint8_t));
         rc = modbus_receive_confirmation(ctx, rsp);
 
-        printf("1/5-B No response from slave %d with invalid request: ",
+        printf("1/5-B No response from slave %d on indication/confirmation messages: ",
                INVALID_SERVER_ID);
 
         if (rc == -1 && errno == ETIMEDOUT) {
@@ -519,9 +530,22 @@ int main(int argc, char *argv[])
             goto close;
         }
 
+        /* Send an INVALID request for another slave */
+        modbus_send_raw_request(ctx, raw_invalid_req, RAW_REQ_LENGTH * sizeof(uint8_t));
+        rc = modbus_receive_confirmation(ctx, rsp);
+
+        printf("1/5-C No response from slave %d with invalid request: ",
+               INVALID_SERVER_ID);
+
+        if (rc == -1 && errno == ETIMEDOUT) {
+            printf("OK\n");
+        } else {
+            printf("FAILED (%d)\n", rc);
+            goto close;
+        }
     } else {
         /* Response in TCP mode */
-        printf("1/4 Response from slave %d: ", 18);
+        printf("1/4 Response from slave %d: ", INVALID_SERVER_ID);
 
         if (rc == UT_REGISTERS_NB) {
             printf("OK\n");