Commit c1e2e0b319b1f1fbbd663b2322162278bd48e4f5
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
Showing
6 changed files
with
96 additions
and
28 deletions
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
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"); | ... | ... |