Commit c1e2e0b319b1f1fbbd663b2322162278bd48e4f5

Authored by Stéphane Raimbault
1 parent 81a52093

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
src/modbus-private.h
... ... @@ -68,6 +68,18 @@ typedef enum {
68 68 _MODBUS_BACKEND_TYPE_TCP
69 69 } modbus_bakend_type_t;
70 70  
  71 +/*
  72 + * ---------- Request Indication ----------
  73 + * | Client | ---------------------->| Server |
  74 + * ---------- Confirmation Response ----------
  75 + */
  76 +typedef enum {
  77 + /* Request message on the server side */
  78 + MSG_INDICATION,
  79 + /* Request message on the client side */
  80 + MSG_CONFIRMATION
  81 +} msg_type_t;
  82 +
71 83 /* This structure reduces the number of params in functions and so
72 84 * optimizes the speed of execution (~ 37%). */
73 85 typedef struct _sft {
... ... @@ -88,6 +100,7 @@ typedef struct _modbus_backend {
88 100 int (*prepare_response_tid) (const uint8_t *req, int *req_length);
89 101 int (*send_msg_pre) (uint8_t *req, int req_length);
90 102 ssize_t (*send) (modbus_t *ctx, const uint8_t *req, int req_length);
  103 + int (*receive) (modbus_t *ctx, uint8_t *req);
91 104 ssize_t (*recv) (modbus_t *ctx, uint8_t *rsp, int rsp_length);
92 105 int (*check_integrity) (modbus_t *ctx, uint8_t *msg,
93 106 const int msg_length);
... ... @@ -114,6 +127,7 @@ struct _modbus {
114 127  
115 128 void _modbus_init_common(modbus_t *ctx);
116 129 void _error_print(modbus_t *ctx, const char *context);
  130 +int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type);
117 131  
118 132 #ifndef HAVE_STRLCPY
119 133 size_t strlcpy(char *dest, const char *src, size_t dest_size);
... ...
src/modbus-rtu-private.h
... ... @@ -88,6 +88,8 @@ typedef struct _modbus_rtu {
88 88 #if HAVE_DECL_TIOCM_RTS
89 89 int rts;
90 90 #endif
  91 + /* To handle many slaves on the same link */
  92 + int confirmation_to_ignore;
91 93 } modbus_rtu_t;
92 94  
93 95 #endif /* _MODBUS_RTU_PRIVATE_H_ */
... ...
src/modbus-rtu.c
... ... @@ -309,6 +309,29 @@ ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length)
309 309 #endif
310 310 }
311 311  
  312 +int _modbus_rtu_receive(modbus_t *ctx, uint8_t *req)
  313 +{
  314 + int rc;
  315 + modbus_rtu_t *ctx_rtu = ctx->backend_data;
  316 +
  317 + if (ctx_rtu->confirmation_to_ignore) {
  318 + _modbus_receive_msg(ctx, req, MSG_CONFIRMATION);
  319 + /* Ignore errors and reset the flag */
  320 + ctx_rtu->confirmation_to_ignore = FALSE;
  321 + rc = 0;
  322 + if (ctx->debug) {
  323 + printf("Confirmation to ignore\n");
  324 + }
  325 + } else {
  326 + rc = _modbus_receive_msg(ctx, req, MSG_INDICATION);
  327 + if (rc == 0) {
  328 + /* The next expected message is a confirmation to ignore */
  329 + ctx_rtu->confirmation_to_ignore = TRUE;
  330 + }
  331 + }
  332 + return rc;
  333 +}
  334 +
312 335 ssize_t _modbus_rtu_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length)
313 336 {
314 337 #if defined(_WIN32)
... ... @@ -354,6 +377,7 @@ int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg,
354 377 if (ctx->debug) {
355 378 printf("Request for slave %d ignored (not %d)\n", slave, ctx->slave);
356 379 }
  380 +
357 381 return 0;
358 382 }
359 383  
... ... @@ -368,6 +392,7 @@ int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg,
368 392 fprintf(stderr, "ERROR CRC received %0X != CRC calculated %0X\n",
369 393 crc_received, crc_calculated);
370 394 }
  395 +
371 396 if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
372 397 _modbus_rtu_flush(ctx);
373 398 }
... ... @@ -970,6 +995,7 @@ const modbus_backend_t _modbus_rtu_backend = {
970 995 _modbus_rtu_prepare_response_tid,
971 996 _modbus_rtu_send_msg_pre,
972 997 _modbus_rtu_send,
  998 + _modbus_rtu_receive,
973 999 _modbus_rtu_recv,
974 1000 _modbus_rtu_check_integrity,
975 1001 _modbus_rtu_pre_check_confirmation,
... ... @@ -1032,5 +1058,7 @@ modbus_t* modbus_new_rtu(const char *device,
1032 1058 ctx_rtu->rts = MODBUS_RTU_RTS_NONE;
1033 1059 #endif
1034 1060  
  1061 + ctx_rtu->confirmation_to_ignore = FALSE;
  1062 +
1035 1063 return ctx;
1036 1064 }
... ...
src/modbus-tcp.c
... ... @@ -178,6 +178,10 @@ ssize_t _modbus_tcp_send(modbus_t *ctx, const uint8_t *req, int req_length)
178 178 return send(ctx->s, (const char*)req, req_length, MSG_NOSIGNAL);
179 179 }
180 180  
  181 +int _modbus_tcp_receive(modbus_t *ctx, uint8_t *req) {
  182 + return _modbus_receive_msg(ctx, req, MSG_INDICATION);
  183 +}
  184 +
181 185 ssize_t _modbus_tcp_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length) {
182 186 return recv(ctx->s, (char *)rsp, rsp_length, 0);
183 187 }
... ... @@ -597,6 +601,7 @@ const modbus_backend_t _modbus_tcp_backend = {
597 601 _modbus_tcp_prepare_response_tid,
598 602 _modbus_tcp_send_msg_pre,
599 603 _modbus_tcp_send,
  604 + _modbus_tcp_receive,
600 605 _modbus_tcp_recv,
601 606 _modbus_tcp_check_integrity,
602 607 _modbus_tcp_pre_check_confirmation,
... ... @@ -618,6 +623,7 @@ const modbus_backend_t _modbus_tcp_pi_backend = {
618 623 _modbus_tcp_prepare_response_tid,
619 624 _modbus_tcp_send_msg_pre,
620 625 _modbus_tcp_send,
  626 + _modbus_tcp_receive,
621 627 _modbus_tcp_recv,
622 628 _modbus_tcp_check_integrity,
623 629 _modbus_tcp_pre_check_confirmation,
... ...
src/modbus.c
... ... @@ -26,6 +26,7 @@
26 26 #include <errno.h>
27 27 #include <limits.h>
28 28 #include <time.h>
  29 +#include <unistd.h>
29 30  
30 31 #include <config.h>
31 32  
... ... @@ -233,17 +234,10 @@ int modbus_send_raw_request(modbus_t *ctx, uint8_t *raw_req, int raw_req_length)
233 234 }
234 235  
235 236 /*
236   - ---------- Request Indication ----------
237   - | Client | ---------------------->| Server |
238   - ---------- Confirmation Response ----------
239   -*/
240   -
241   -typedef enum {
242   - /* Request message on the server side */
243   - MSG_INDICATION,
244   - /* Request message on the client side */
245   - MSG_CONFIRMATION
246   -} msg_type_t;
  237 + * ---------- Request Indication ----------
  238 + * | Client | ---------------------->| Server |
  239 + * ---------- Confirmation Response ----------
  240 + */
247 241  
248 242 /* Computes the length to read after the function received */
249 243 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,
329 323 - read() or recv() error codes
330 324 */
331 325  
332   -static int receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
  326 +int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
333 327 {
334 328 int rc;
335 329 fd_set rfds;
... ... @@ -462,7 +456,7 @@ static int receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
462 456 /* Receive the request from a modbus master */
463 457 int modbus_receive(modbus_t *ctx, uint8_t *req)
464 458 {
465   - return receive_msg(ctx, req, MSG_INDICATION);
  459 + return ctx->backend->receive(ctx, req);
466 460 }
467 461  
468 462 /* Receives the confirmation.
... ... @@ -475,7 +469,7 @@ int modbus_receive(modbus_t *ctx, uint8_t *req)
475 469 */
476 470 int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp)
477 471 {
478   - return receive_msg(ctx, rsp, MSG_CONFIRMATION);
  472 + return _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
479 473 }
480 474  
481 475 static int check_confirmation(modbus_t *ctx, uint8_t *req,
... ... @@ -965,7 +959,7 @@ static int read_io_status(modbus_t *ctx, int function,
965 959 int offset;
966 960 int offset_end;
967 961  
968   - rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
  962 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
969 963 if (rc == -1)
970 964 return -1;
971 965  
... ... @@ -1064,7 +1058,7 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb,
1064 1058 int offset;
1065 1059 int i;
1066 1060  
1067   - rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
  1061 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
1068 1062 if (rc == -1)
1069 1063 return -1;
1070 1064  
... ... @@ -1140,7 +1134,7 @@ static int write_single(modbus_t *ctx, int function, int addr, int value)
1140 1134 /* Used by write_bit and write_register */
1141 1135 uint8_t rsp[_MIN_REQ_LENGTH];
1142 1136  
1143   - rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
  1137 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
1144 1138 if (rc == -1)
1145 1139 return -1;
1146 1140  
... ... @@ -1211,7 +1205,7 @@ int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src)
1211 1205 if (rc > 0) {
1212 1206 uint8_t rsp[MAX_MESSAGE_LENGTH];
1213 1207  
1214   - rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
  1208 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
1215 1209 if (rc == -1)
1216 1210 return -1;
1217 1211  
... ... @@ -1257,7 +1251,7 @@ int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src)
1257 1251 if (rc > 0) {
1258 1252 uint8_t rsp[MAX_MESSAGE_LENGTH];
1259 1253  
1260   - rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
  1254 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
1261 1255 if (rc == -1)
1262 1256 return -1;
1263 1257  
... ... @@ -1320,7 +1314,7 @@ int modbus_write_and_read_registers(modbus_t *ctx,
1320 1314 if (rc > 0) {
1321 1315 int offset;
1322 1316  
1323   - rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
  1317 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
1324 1318 if (rc == -1)
1325 1319 return -1;
1326 1320  
... ... @@ -1361,7 +1355,7 @@ int modbus_report_slave_id(modbus_t *ctx, uint8_t *dest)
1361 1355 int offset;
1362 1356 uint8_t rsp[MAX_MESSAGE_LENGTH];
1363 1357  
1364   - rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
  1358 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
1365 1359 if (rc == -1)
1366 1360 return -1;
1367 1361  
... ...
tests/unit-test-client.c
... ... @@ -491,8 +491,12 @@ int main(int argc, char *argv[])
491 491 UT_REGISTERS_NB, tab_rp_registers);
492 492 if (use_backend == RTU) {
493 493 const int RAW_REQ_LENGTH = 6;
494   - uint8_t raw_req[] = { INVALID_SERVER_ID, 0x03, 0x00, 0x01, 0xFF, 0xFF };
495   - uint8_t rsp[MODBUS_TCP_MAX_ADU_LENGTH];
  494 + uint8_t raw_req[] = { INVALID_SERVER_ID, 0x03, 0x00, 0x01, 0x01, 0x01 };
  495 + /* Too many points */
  496 + uint8_t raw_invalid_req[] = { INVALID_SERVER_ID, 0x03, 0x00, 0x01, 0xFF, 0xFF };
  497 + const int RAW_REP_LENGTH = 7;
  498 + uint8_t raw_rep[] = { INVALID_SERVER_ID, 0x03, 0x04, 0, 0, 0, 0 };
  499 + uint8_t rsp[MODBUS_RTU_MAX_ADU_LENGTH];
496 500  
497 501 /* No response in RTU mode */
498 502 printf("1/5-A No response from slave %d: ", INVALID_SERVER_ID);
... ... @@ -504,12 +508,19 @@ int main(int argc, char *argv[])
504 508 goto close;
505 509 }
506 510  
507   - /* Send an invalid query with a wrong slave ID */
508   - modbus_send_raw_request(ctx, raw_req,
509   - RAW_REQ_LENGTH * sizeof(uint8_t));
  511 + /* The slave raises a timeout on a confirmation to ignore because if an
  512 + * indication for another slave is received, a confirmation must follow */
  513 +
  514 +
  515 + /* Send a pair of indication/confimration to the slave with a different
  516 + * slave ID to simulate a communication on a RS485 bus. At first, the
  517 + * slave will see the indication message then the confirmation, and it must
  518 + * ignore both. */
  519 + modbus_send_raw_request(ctx, raw_req, RAW_REQ_LENGTH * sizeof(uint8_t));
  520 + modbus_send_raw_request(ctx, raw_rep, RAW_REP_LENGTH * sizeof(uint8_t));
510 521 rc = modbus_receive_confirmation(ctx, rsp);
511 522  
512   - printf("1/5-B No response from slave %d with invalid request: ",
  523 + printf("1/5-B No response from slave %d on indication/confirmation messages: ",
513 524 INVALID_SERVER_ID);
514 525  
515 526 if (rc == -1 && errno == ETIMEDOUT) {
... ... @@ -519,9 +530,22 @@ int main(int argc, char *argv[])
519 530 goto close;
520 531 }
521 532  
  533 + /* Send an INVALID request for another slave */
  534 + modbus_send_raw_request(ctx, raw_invalid_req, RAW_REQ_LENGTH * sizeof(uint8_t));
  535 + rc = modbus_receive_confirmation(ctx, rsp);
  536 +
  537 + printf("1/5-C No response from slave %d with invalid request: ",
  538 + INVALID_SERVER_ID);
  539 +
  540 + if (rc == -1 && errno == ETIMEDOUT) {
  541 + printf("OK\n");
  542 + } else {
  543 + printf("FAILED (%d)\n", rc);
  544 + goto close;
  545 + }
522 546 } else {
523 547 /* Response in TCP mode */
524   - printf("1/4 Response from slave %d: ", 18);
  548 + printf("1/4 Response from slave %d: ", INVALID_SERVER_ID);
525 549  
526 550 if (rc == UT_REGISTERS_NB) {
527 551 printf("OK\n");
... ...