diff --git a/src/modbus-private.h b/src/modbus-private.h index 33b4579..0c1fc6a 100644 --- a/src/modbus-private.h +++ b/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); diff --git a/src/modbus-rtu-private.h b/src/modbus-rtu-private.h index f314971..072195e 100644 --- a/src/modbus-rtu-private.h +++ b/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_ */ diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index f37fad4..06958b8 100644 --- a/src/modbus-rtu.c +++ b/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; } diff --git a/src/modbus-tcp.c b/src/modbus-tcp.c index a5b1924..5948111 100644 --- a/src/modbus-tcp.c +++ b/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, diff --git a/src/modbus.c b/src/modbus.c index 0771490..ca98032 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -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; diff --git a/tests/unit-test-client.c b/tests/unit-test-client.c index 86964f6..b0c60d2 100644 --- a/tests/unit-test-client.c +++ b/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");