Permalink
Browse files

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
  • Loading branch information...
1 parent 81a5209 commit c1e2e0b319b1f1fbbd663b2322162278bd48e4f5 @stephane committed Dec 30, 2011
Showing with 96 additions and 28 deletions.
  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
View
@@ -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);
View
@@ -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_ */
View
@@ -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;
}
View
@@ -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,
View
@@ -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;
View
@@ -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");

0 comments on commit c1e2e0b

Please sign in to comment.