Commit 4d3bf7beeaa8c196957c8daa76fa7dc51941b859
1 parent
86418cf3
Implement report slave ID on server side
- return only useful data client side - available in TCP when a gateway to RTU is used - need to add isolated handling of indication/confirmation messages
Showing
4 changed files
with
97 additions
and
38 deletions
NEWS
| 1 | 1 | libmodbus 2.1.1 (2010-XX-XX) |
| 2 | 2 | ============================ |
| 3 | 3 | |
| 4 | +- New API | |
| 4 | 5 | - Remove the internal function set_message_length_tcp |
| 5 | 6 | - Restore slave ID (server ID) argument in functions |
| 6 | 7 | - Error conventions of POSIX systems and error recover |
| ... | ... | @@ -13,6 +14,7 @@ libmodbus 2.1.1 (2010-XX-XX) |
| 13 | 14 | - Fix #591142 - Slave id check should be disabled in TCP connection |
| 14 | 15 | Reported by aladdinwu. |
| 15 | 16 | - Parity setting is now a single char ('N', 'E' or 'O') |
| 17 | +- Report slave ID server side | |
| 16 | 18 | |
| 17 | 19 | libmodbus 2.1.0 (2010-03-24) |
| 18 | 20 | ============================ | ... | ... |
src/modbus.c
| ... | ... | @@ -479,7 +479,7 @@ static int send_msg(modbus_t *ctx, uint8_t *req, int req_length) |
| 479 | 479 | } |
| 480 | 480 | |
| 481 | 481 | /* Computes the length of the header following the function code */ |
| 482 | -static uint8_t compute_request_length_header(int function) | |
| 482 | +static uint8_t compute_indication_length_header(int function) | |
| 483 | 483 | { |
| 484 | 484 | int length; |
| 485 | 485 | |
| ... | ... | @@ -492,6 +492,28 @@ static uint8_t compute_request_length_header(int function) |
| 492 | 492 | /* Multiple write */ |
| 493 | 493 | length = 5; |
| 494 | 494 | else if (function == FC_REPORT_SLAVE_ID) |
| 495 | + length = 0; | |
| 496 | + else | |
| 497 | + length = 0; | |
| 498 | + | |
| 499 | + return length; | |
| 500 | +} | |
| 501 | + | |
| 502 | +/* Computes the length of the header following the function code */ | |
| 503 | +static uint8_t compute_confirmation_length_header(int function) | |
| 504 | +{ | |
| 505 | + int length; | |
| 506 | + | |
| 507 | + if (function <= FC_WRITE_SINGLE_COIL || | |
| 508 | + function == FC_WRITE_SINGLE_REGISTER) | |
| 509 | + /* Read and single write */ | |
| 510 | + length = 4; | |
| 511 | + else if (function == FC_WRITE_MULTIPLE_COILS || | |
| 512 | + function == FC_WRITE_MULTIPLE_REGISTERS) | |
| 513 | + /* Multiple write */ | |
| 514 | + length = 5; | |
| 515 | + else if (function == FC_REPORT_SLAVE_ID) | |
| 516 | + /* Report slave_ID */ | |
| 495 | 517 | length = 1; |
| 496 | 518 | else |
| 497 | 519 | length = 0; |
| ... | ... | @@ -500,17 +522,17 @@ static uint8_t compute_request_length_header(int function) |
| 500 | 522 | } |
| 501 | 523 | |
| 502 | 524 | /* Computes the length of the data to write in the request */ |
| 503 | -static int compute_request_length_data(modbus_t *ctx, uint8_t *msg) | |
| 525 | +static int compute_msg_length_data(modbus_t *ctx, uint8_t *msg) | |
| 504 | 526 | { |
| 505 | 527 | int function = msg[TAB_HEADER_LENGTH[ctx->type_com]]; |
| 506 | 528 | int length; |
| 507 | 529 | |
| 508 | 530 | if (function == FC_WRITE_MULTIPLE_COILS || |
| 509 | - function == FC_WRITE_MULTIPLE_REGISTERS) | |
| 531 | + function == FC_WRITE_MULTIPLE_REGISTERS) { | |
| 510 | 532 | length = msg[TAB_HEADER_LENGTH[ctx->type_com] + 5]; |
| 511 | - else if (function == FC_REPORT_SLAVE_ID) | |
| 533 | + } else if (function == FC_REPORT_SLAVE_ID) { | |
| 512 | 534 | length = msg[TAB_HEADER_LENGTH[ctx->type_com] + 1]; |
| 513 | - else | |
| 535 | + } else | |
| 514 | 536 | length = 0; |
| 515 | 537 | |
| 516 | 538 | length += TAB_CHECKSUM_LENGTH[ctx->type_com]; |
| ... | ... | @@ -573,7 +595,14 @@ static int compute_request_length_data(modbus_t *ctx, uint8_t *msg) |
| 573 | 595 | - ETIMEDOUT |
| 574 | 596 | - read() or recv() error codes |
| 575 | 597 | */ |
| 576 | -static int receive_msg(modbus_t *ctx, int msg_length_computed, uint8_t *msg) | |
| 598 | +enum { | |
| 599 | + /* Request message on the server side */ | |
| 600 | + MSG_INDICATION, | |
| 601 | + /* Request message on the client side */ | |
| 602 | + MSG_CONFIRMATION | |
| 603 | +}; | |
| 604 | + | |
| 605 | +static int receive_msg(modbus_t *ctx, int msg_length_computed, uint8_t *msg, int type) | |
| 577 | 606 | { |
| 578 | 607 | int s_rc; |
| 579 | 608 | int read_rc; |
| ... | ... | @@ -586,11 +615,16 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed, uint8_t *msg) |
| 586 | 615 | int msg_length = 0; |
| 587 | 616 | |
| 588 | 617 | if (ctx->debug) { |
| 618 | + if (type == MSG_INDICATION) { | |
| 619 | + printf("Waiting for a indication"); | |
| 620 | + } else { | |
| 621 | + printf("Waiting for a confirmation"); | |
| 622 | + } | |
| 623 | + | |
| 589 | 624 | if (msg_length_computed == MSG_LENGTH_UNDEFINED) |
| 590 | - printf("Waiting for a message...\n"); | |
| 625 | + printf("...\n"); | |
| 591 | 626 | else |
| 592 | - printf("Waiting for a message (%d bytes)...\n", | |
| 593 | - msg_length_computed); | |
| 627 | + printf("(%d bytes)...\n", msg_length_computed); | |
| 594 | 628 | } |
| 595 | 629 | |
| 596 | 630 | /* Add a file descriptor to the set */ |
| ... | ... | @@ -661,8 +695,13 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed, uint8_t *msg) |
| 661 | 695 | switch (state) { |
| 662 | 696 | case FUNCTION: |
| 663 | 697 | /* Function code position */ |
| 664 | - length_to_read = compute_request_length_header( | |
| 665 | - msg[TAB_HEADER_LENGTH[ctx->type_com]]); | |
| 698 | + if (type == MSG_INDICATION) { | |
| 699 | + length_to_read = compute_indication_length_header( | |
| 700 | + msg[TAB_HEADER_LENGTH[ctx->type_com]]); | |
| 701 | + } else { | |
| 702 | + length_to_read = compute_confirmation_length_header( | |
| 703 | + msg[TAB_HEADER_LENGTH[ctx->type_com]]); | |
| 704 | + } | |
| 666 | 705 | msg_length_computed += length_to_read; |
| 667 | 706 | /* It's useless to check the value of |
| 668 | 707 | msg_length_computed in this case (only |
| ... | ... | @@ -670,7 +709,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed, uint8_t *msg) |
| 670 | 709 | state = DATA; |
| 671 | 710 | break; |
| 672 | 711 | case DATA: |
| 673 | - length_to_read = compute_request_length_data(ctx, msg); | |
| 712 | + length_to_read = compute_msg_length_data(ctx, msg); | |
| 674 | 713 | msg_length_computed += length_to_read; |
| 675 | 714 | if (msg_length_computed > TAB_MAX_ADU_LENGTH[ctx->type_com]) { |
| 676 | 715 | errno = EMBBADDATA; |
| ... | ... | @@ -727,7 +766,7 @@ int modbus_receive(modbus_t *ctx, int sockfd, uint8_t *req) |
| 727 | 766 | } |
| 728 | 767 | |
| 729 | 768 | /* The length of the request to receive isn't known. */ |
| 730 | - return receive_msg(ctx, MSG_LENGTH_UNDEFINED, req); | |
| 769 | + return receive_msg(ctx, MSG_LENGTH_UNDEFINED, req, MSG_INDICATION); | |
| 731 | 770 | } |
| 732 | 771 | |
| 733 | 772 | /* Receives the response and checks values (and checksum in RTU). |
| ... | ... | @@ -744,7 +783,7 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp) |
| 744 | 783 | int offset = TAB_HEADER_LENGTH[ctx->type_com]; |
| 745 | 784 | |
| 746 | 785 | rsp_length_computed = compute_response_length(ctx, req); |
| 747 | - rc = receive_msg(ctx, rsp_length_computed, rsp); | |
| 786 | + rc = receive_msg(ctx, rsp_length_computed, rsp, MSG_CONFIRMATION); | |
| 748 | 787 | if (rc != -1) { |
| 749 | 788 | /* GOOD RESPONSE */ |
| 750 | 789 | int req_nb_value; |
| ... | ... | @@ -776,7 +815,7 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp) |
| 776 | 815 | break; |
| 777 | 816 | case FC_REPORT_SLAVE_ID: |
| 778 | 817 | /* Report slave ID (bytes received) */ |
| 779 | - req_nb_value = rsp_nb_value = rc; | |
| 818 | + req_nb_value = rsp_nb_value = rsp[offset + 1]; | |
| 780 | 819 | break; |
| 781 | 820 | default: |
| 782 | 821 | /* 1 Write functions & others */ |
| ... | ... | @@ -1088,8 +1127,15 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, |
| 1088 | 1127 | } |
| 1089 | 1128 | } |
| 1090 | 1129 | break; |
| 1091 | - case FC_READ_EXCEPTION_STATUS: | |
| 1092 | 1130 | case FC_REPORT_SLAVE_ID: |
| 1131 | + resp_length = build_response_basis(ctx, &sft, rsp); | |
| 1132 | + /* 2 bytes */ | |
| 1133 | + rsp[resp_length++] = 2; | |
| 1134 | + rsp[resp_length++] = ctx->slave; | |
| 1135 | + /* Slave is ON */ | |
| 1136 | + rsp[resp_length++] = 0xFF; | |
| 1137 | + break; | |
| 1138 | + case FC_READ_EXCEPTION_STATUS: | |
| 1093 | 1139 | if (ctx->debug) { |
| 1094 | 1140 | fprintf(stderr, "FIXME Not implemented\n"); |
| 1095 | 1141 | } |
| ... | ... | @@ -1127,11 +1173,11 @@ static int read_io_status(modbus_t *ctx, int function, |
| 1127 | 1173 | if (rc == -1) |
| 1128 | 1174 | return -1; |
| 1129 | 1175 | |
| 1130 | - offset = TAB_HEADER_LENGTH[ctx->type_com]; | |
| 1176 | + offset = TAB_HEADER_LENGTH[ctx->type_com] + 2; | |
| 1131 | 1177 | offset_end = offset + rc; |
| 1132 | 1178 | for (i = offset; i < offset_end; i++) { |
| 1133 | 1179 | /* Shift reg hi_byte to temp */ |
| 1134 | - temp = rsp[i + 2]; | |
| 1180 | + temp = rsp[i]; | |
| 1135 | 1181 | |
| 1136 | 1182 | for (bit = 0x01; (bit & 0xff) && (pos < nb);) { |
| 1137 | 1183 | data_dest[pos++] = (temp & bit) ? TRUE : FALSE; |
| ... | ... | @@ -1410,19 +1456,13 @@ int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data |
| 1410 | 1456 | } |
| 1411 | 1457 | |
| 1412 | 1458 | /* Send a request to get the slave ID of the device (only available in serial |
| 1413 | - * communication) */ | |
| 1459 | + * communication). */ | |
| 1414 | 1460 | int modbus_report_slave_id(modbus_t *ctx, uint8_t *data_dest) |
| 1415 | 1461 | { |
| 1416 | 1462 | int rc; |
| 1417 | 1463 | int req_length; |
| 1418 | 1464 | uint8_t req[MIN_REQ_LENGTH]; |
| 1419 | 1465 | |
| 1420 | - if (ctx->type_com != RTU) { | |
| 1421 | - /* Only for serial communications */ | |
| 1422 | - errno = EINVAL; | |
| 1423 | - return -1; | |
| 1424 | - } | |
| 1425 | - | |
| 1426 | 1466 | req_length = build_request_basis(ctx, FC_REPORT_SLAVE_ID, 0, 0, req); |
| 1427 | 1467 | |
| 1428 | 1468 | /* HACKISH, addr and count are not used */ |
| ... | ... | @@ -1432,7 +1472,6 @@ int modbus_report_slave_id(modbus_t *ctx, uint8_t *data_dest) |
| 1432 | 1472 | if (rc > 0) { |
| 1433 | 1473 | int i; |
| 1434 | 1474 | int offset; |
| 1435 | - int offset_end; | |
| 1436 | 1475 | uint8_t rsp[MAX_MESSAGE_LENGTH]; |
| 1437 | 1476 | |
| 1438 | 1477 | /* Byte count, slave id, run indicator status, |
| ... | ... | @@ -1441,11 +1480,11 @@ int modbus_report_slave_id(modbus_t *ctx, uint8_t *data_dest) |
| 1441 | 1480 | if (rc == -1) |
| 1442 | 1481 | return -1; |
| 1443 | 1482 | |
| 1444 | - offset = TAB_HEADER_LENGTH[ctx->type_com] - 1; | |
| 1445 | - offset_end = offset + rc; | |
| 1483 | + offset = TAB_HEADER_LENGTH[ctx->type_com] + 2; | |
| 1446 | 1484 | |
| 1447 | - for (i = offset; i < offset_end; i++) | |
| 1448 | - data_dest[i] = rsp[i]; | |
| 1485 | + for (i=0; i < rc; i++) { | |
| 1486 | + data_dest[i] = rsp[offset + i]; | |
| 1487 | + } | |
| 1449 | 1488 | } |
| 1450 | 1489 | |
| 1451 | 1490 | return rc; | ... | ... |
tests/unit-test-client.c
| ... | ... | @@ -443,7 +443,7 @@ int main(void) |
| 443 | 443 | rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, |
| 444 | 444 | UT_REGISTERS_NB_POINTS, |
| 445 | 445 | tab_rp_registers); |
| 446 | - printf("1/3 No or response from slave %d: ", 18); | |
| 446 | + printf("1/4 No or response from slave %d: ", 18); | |
| 447 | 447 | if (is_mode_rtu) { |
| 448 | 448 | /* No response in RTU mode */ |
| 449 | 449 | if (rc == -1 && errno == ETIMEDOUT) { |
| ... | ... | @@ -466,7 +466,7 @@ int main(void) |
| 466 | 466 | rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, |
| 467 | 467 | UT_REGISTERS_NB_POINTS, |
| 468 | 468 | tab_rp_registers); |
| 469 | - printf("2/3 Reply after a broadcast query: "); | |
| 469 | + printf("2/4 Reply after a broadcast query: "); | |
| 470 | 470 | if (rc == UT_REGISTERS_NB_POINTS) { |
| 471 | 471 | printf("OK\n"); |
| 472 | 472 | } else { |
| ... | ... | @@ -481,6 +481,22 @@ int main(void) |
| 481 | 481 | modbus_set_slave(ctx, MODBUS_TCP_SLAVE); |
| 482 | 482 | } |
| 483 | 483 | |
| 484 | + printf("3/4 Report slave ID: \n"); | |
| 485 | + /* tab_rp_bits is used to store bytes */ | |
| 486 | + rc = modbus_report_slave_id(ctx, tab_rp_bits); | |
| 487 | + if (rc == -1) { | |
| 488 | + printf("FAILED\n"); | |
| 489 | + goto close; | |
| 490 | + } | |
| 491 | + | |
| 492 | + if ((is_mode_rtu && tab_rp_bits[0] == SERVER_ID) | |
| 493 | + || tab_rp_bits[0] == 0xFF) { | |
| 494 | + printf("OK\n"); | |
| 495 | + } else { | |
| 496 | + printf("FAILED\n"); | |
| 497 | + goto close; | |
| 498 | + } | |
| 499 | + | |
| 484 | 500 | /* Save original timeout */ |
| 485 | 501 | modbus_get_timeout_begin(ctx, &timeout_begin_old); |
| 486 | 502 | |
| ... | ... | @@ -492,7 +508,7 @@ int main(void) |
| 492 | 508 | rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, |
| 493 | 509 | UT_REGISTERS_NB_POINTS, |
| 494 | 510 | tab_rp_registers); |
| 495 | - printf("3/3 Too short timeout: "); | |
| 511 | + printf("4/4 Too short timeout: "); | |
| 496 | 512 | if (rc == -1 && errno == ETIMEDOUT) { |
| 497 | 513 | printf("OK\n"); |
| 498 | 514 | } else { | ... | ... |
tests/unit-test-server.c
| ... | ... | @@ -39,10 +39,11 @@ int main(void) |
| 39 | 39 | modbus_set_debug(ctx, TRUE); |
| 40 | 40 | modbus_set_error_recovery(ctx, TRUE); |
| 41 | 41 | |
| 42 | - mb_mapping = modbus_mapping_new(UT_BITS_ADDRESS + UT_BITS_NB_POINTS, | |
| 43 | - UT_INPUT_BITS_ADDRESS + UT_INPUT_BITS_NB_POINTS, | |
| 44 | - UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_POINTS, | |
| 45 | - UT_INPUT_REGISTERS_ADDRESS + UT_INPUT_REGISTERS_NB_POINTS); | |
| 42 | + mb_mapping = modbus_mapping_new( | |
| 43 | + UT_BITS_ADDRESS + UT_BITS_NB_POINTS, | |
| 44 | + UT_INPUT_BITS_ADDRESS + UT_INPUT_BITS_NB_POINTS, | |
| 45 | + UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_POINTS, | |
| 46 | + UT_INPUT_REGISTERS_ADDRESS + UT_INPUT_REGISTERS_NB_POINTS); | |
| 46 | 47 | if (mb_mapping == NULL) { |
| 47 | 48 | fprintf(stderr, "Failed to allocate the mapping: %s\n", |
| 48 | 49 | modbus_strerror(errno)); |
| ... | ... | @@ -72,7 +73,8 @@ int main(void) |
| 72 | 73 | |
| 73 | 74 | rc = modbus_receive(ctx, -1, query); |
| 74 | 75 | if (rc > 0) { |
| 75 | - if (((query[HEADER_LENGTH_TCP + 3] << 8) + query[HEADER_LENGTH_TCP + 4]) | |
| 76 | + if (((query[HEADER_LENGTH_TCP + 3] << 8) + | |
| 77 | + query[HEADER_LENGTH_TCP + 4]) | |
| 76 | 78 | == UT_REGISTERS_NB_POINTS_SPECIAL) { |
| 77 | 79 | /* Change the number of values (offset |
| 78 | 80 | TCP = 6) */ | ... | ... |