Commit 87293e459ec35576b0a8c502a523f7f2d2d7d7f1

Authored by Stéphane Raimbault
1 parent cfe875e7

MAJOR Rewrite of the message reading

The goal of this rewriting is to avoid the timeouts on the receiving
of exceptions and to be more robust on bad requests. Some devices
use the exception MODBUS_EXCEPTION_ACKNOWLEDGE to response to some
valid requests, so in this case, you'll really appreciate this
change!

- Slower! More system calls are used.
- The code is cleaner and easier to understand.
- Really faster when an exception occurs.
- Fix unit test of bad request in RTU
src/modbus-private.h
... ... @@ -88,7 +88,7 @@ typedef struct _modbus_backend {
88 88 int (*connect) (modbus_t *ctx);
89 89 void (*close) (modbus_t *ctx);
90 90 int (*flush) (modbus_t *ctx);
91   - int (*select) (modbus_t *ctx, fd_set *rfds, struct timeval *tv, int msg_length_computed, int msg_length);
  91 + int (*select) (modbus_t *ctx, fd_set *rfds, struct timeval *tv, int msg_length);
92 92 int (*filter_request) (modbus_t *ctx, int slave);
93 93 } modbus_backend_t;
94 94  
... ...
src/modbus-rtu.c
... ... @@ -179,12 +179,13 @@ static void win32_ser_init(struct win32_ser *ws) {
179 179 ws->fd = INVALID_HANDLE_VALUE;
180 180 }
181 181  
  182 +/* FIXME Try to remove length_to_read -> max_len argument, only used by win32 */
182 183 static int win32_ser_select(struct win32_ser *ws, int max_len, struct timeval *tv) {
183 184 COMMTIMEOUTS comm_to;
184 185 unsigned int msec = 0;
185 186  
186 187 /* Check if some data still in the buffer to be consumed */
187   - if (ws->n_bytes> 0) {
  188 + if (ws->n_bytes > 0) {
188 189 return 1;
189 190 }
190 191  
... ... @@ -713,11 +714,11 @@ int _modbus_rtu_flush(modbus_t *ctx)
713 714 #endif
714 715 }
715 716  
716   -int _modbus_rtu_select(modbus_t *ctx, fd_set *rfds, struct timeval *tv, int msg_length_computed, int msg_length)
  717 +int _modbus_rtu_select(modbus_t *ctx, fd_set *rfds, struct timeval *tv, int length_to_read)
717 718 {
718 719 int s_rc;
719 720 #if defined(_WIN32)
720   - s_rc = win32_ser_select(&(((modbus_rtu_t*)ctx->backend_data)->w_ser), msg_length_computed, tv);
  721 + s_rc = win32_ser_select(&(((modbus_rtu_t*)ctx->backend_data)->w_ser), length_to_read, tv);
721 722 if (s_rc == 0) {
722 723 errno = ETIMEDOUT;
723 724 return -1;
... ... @@ -758,17 +759,8 @@ int _modbus_rtu_select(modbus_t *ctx, fd_set *rfds, struct timeval *tv, int msg_
758 759  
759 760 if (s_rc == 0) {
760 761 /* Timeout */
761   - if (msg_length == (ctx->backend->header_length + 2 +
762   - ctx->backend->checksum_length)) {
763   - /* Optimization allowed because exception response is
764   - the smallest trame in modbus protocol (3) so always
765   - raise a timeout error.
766   - Temporary error before exception analyze. */
767   - errno = EMBUNKEXC;
768   - } else {
769   - errno = ETIMEDOUT;
770   - _error_print(ctx, "select");
771   - }
  762 + errno = ETIMEDOUT;
  763 + _error_print(ctx, "select");
772 764 return -1;
773 765 }
774 766 #endif
... ...
src/modbus-tcp.c
... ... @@ -350,7 +350,7 @@ int modbus_tcp_accept(modbus_t *ctx, int *socket)
350 350 return ctx->s;
351 351 }
352 352  
353   -int _modbus_tcp_select(modbus_t *ctx, fd_set *rfds, struct timeval *tv, int msg_length_computed, int msg_length)
  353 +int _modbus_tcp_select(modbus_t *ctx, fd_set *rfds, struct timeval *tv, int length_to_read)
354 354 {
355 355 int s_rc;
356 356 while ((s_rc = select(ctx->s+1, rfds, NULL, NULL, tv)) == -1) {
... ... @@ -375,18 +375,8 @@ int _modbus_tcp_select(modbus_t *ctx, fd_set *rfds, struct timeval *tv, int msg_
375 375 }
376 376  
377 377 if (s_rc == 0) {
378   - /* Timeout */
379   - if (msg_length == (ctx->backend->header_length + 2 +
380   - ctx->backend->checksum_length)) {
381   - /* Optimization allowed because exception response is
382   - the smallest trame in modbus protocol (3) so always
383   - raise a timeout error.
384   - Temporary error before exception analyze. */
385   - errno = EMBUNKEXC;
386   - } else {
387   - errno = ETIMEDOUT;
388   - _error_print(ctx, "select");
389   - }
  378 + errno = ETIMEDOUT;
  379 + _error_print(ctx, "select");
390 380 return -1;
391 381 }
392 382  
... ...
src/modbus.c
... ... @@ -42,6 +42,13 @@ const unsigned int libmodbus_version_micro = LIBMODBUS_VERSION_MICRO;
42 42 /* Max between RTU and TCP max adu length (so TCP) */
43 43 #define MAX_MESSAGE_LENGTH 260
44 44  
  45 +/* 3 steps are used to parse the query */
  46 +typedef enum {
  47 + _STEP_FUNCTION,
  48 + _STEP_META,
  49 + _STEP_DATA
  50 +} _step_t;
  51 +
45 52 const char *modbus_strerror(int errnum) {
46 53 switch (errnum) {
47 54 case EMBXILFUN:
... ... @@ -95,7 +102,7 @@ int modbus_flush(modbus_t *ctx)
95 102 }
96 103  
97 104 /* Computes the length of the expected response */
98   -static unsigned int compute_response_length(modbus_t *ctx, uint8_t *req)
  105 +static unsigned int compute_response_length_from_request(modbus_t *ctx, uint8_t *req)
99 106 {
100 107 int length;
101 108 int offset;
... ... @@ -127,7 +134,7 @@ static unsigned int compute_response_length(modbus_t *ctx, uint8_t *req)
127 134 length = 5;
128 135 }
129 136  
130   - return length + offset + ctx->backend->checksum_length;
  137 + return offset + length + ctx->backend->checksum_length;
131 138 }
132 139  
133 140 /* Sends a request/response */
... ... @@ -179,21 +186,14 @@ typedef enum {
179 186 MSG_CONFIRMATION
180 187 } msg_type_t;
181 188  
182   -/* Computes the header length (to reach the real data) */
183   -static uint8_t compute_header_length(int function, msg_type_t msg_type)
  189 +/* Computes the length to read after the function received */
  190 +static uint8_t compute_meta_length_after_function(int function,
  191 + msg_type_t msg_type)
184 192 {
185 193 int length;
186 194  
187   - if (msg_type == MSG_CONFIRMATION) {
188   - if (function == _FC_REPORT_SLAVE_ID) {
189   - length = 1;
190   - } else {
191   - /* Should never happen, the other header lengths are precomputed */
192   - abort();
193   - }
194   - } else /* MSG_INDICATION */ {
195   - if (function <= _FC_WRITE_SINGLE_COIL ||
196   - function == _FC_WRITE_SINGLE_REGISTER) {
  195 + if (msg_type == MSG_INDICATION) {
  196 + if (function <= _FC_WRITE_SINGLE_REGISTER) {
197 197 length = 4;
198 198 } else if (function == _FC_WRITE_MULTIPLE_COILS ||
199 199 function == _FC_WRITE_MULTIPLE_REGISTERS) {
... ... @@ -201,27 +201,54 @@ static uint8_t compute_header_length(int function, msg_type_t msg_type)
201 201 } else if (function == _FC_READ_AND_WRITE_REGISTERS) {
202 202 length = 9;
203 203 } else {
  204 + /* _FC_READ_EXCEPTION_STATUS, _FC_REPORT_SLAVE_ID */
204 205 length = 0;
205 206 }
  207 + } else {
  208 + /* MSG_CONFIRMATION */
  209 + switch (function) {
  210 + case _FC_WRITE_SINGLE_COIL:
  211 + case _FC_WRITE_SINGLE_REGISTER:
  212 + case _FC_WRITE_MULTIPLE_COILS:
  213 + case _FC_WRITE_MULTIPLE_REGISTERS:
  214 + length = 4;
  215 + break;
  216 + default:
  217 + length = 1;
  218 + }
206 219 }
  220 +
207 221 return length;
208 222 }
209 223  
210   -/* Computes the length of the data to write in the request */
211   -static int compute_data_length(modbus_t *ctx, uint8_t *msg)
  224 +/* Computes the length to read after the meta information (address, count, etc) */
  225 +static int compute_data_length_after_meta(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
212 226 {
213 227 int function = msg[ctx->backend->header_length];
214 228 int length;
215 229  
216   - if (function == _FC_WRITE_MULTIPLE_COILS ||
217   - function == _FC_WRITE_MULTIPLE_REGISTERS) {
218   - length = msg[ctx->backend->header_length + 5];
219   - } else if (function == _FC_REPORT_SLAVE_ID) {
220   - length = msg[ctx->backend->header_length + 1];
221   - } else if (function == _FC_READ_AND_WRITE_REGISTERS) {
222   - length = msg[ctx->backend->header_length + 9];
223   - } else
224   - length = 0;
  230 + if (msg_type == MSG_INDICATION) {
  231 + switch (function) {
  232 + case _FC_WRITE_MULTIPLE_COILS:
  233 + case _FC_WRITE_MULTIPLE_REGISTERS:
  234 + length = msg[ctx->backend->header_length + 5];
  235 + break;
  236 + case _FC_READ_AND_WRITE_REGISTERS:
  237 + length = msg[ctx->backend->header_length + 9];
  238 + break;
  239 + default:
  240 + length = 0;
  241 + }
  242 + } else {
  243 + /* MSG_CONFIRMATION */
  244 + if (function <= _FC_READ_INPUT_REGISTERS ||
  245 + function == _FC_REPORT_SLAVE_ID ||
  246 + function == _FC_READ_AND_WRITE_REGISTERS) {
  247 + length = msg[ctx->backend->header_length + 1];
  248 + } else {
  249 + length = 0;
  250 + }
  251 + }
225 252  
226 253 length += ctx->backend->checksum_length;
227 254  
... ... @@ -232,9 +259,6 @@ static int compute_data_length(modbus_t *ctx, uint8_t *msg)
232 259 /* Waits a response from a modbus server or a request from a modbus client.
233 260 This function blocks if there is no replies (3 timeouts).
234 261  
235   - The argument msg_length_computed must be set to MSG_LENGTH_UNDEFINED if
236   - undefined.
237   -
238 262 The function shall return the number of received characters and the received
239 263 message in an array of uint8_t if successful. Otherwise it shall return -1
240 264 and errno is set to one of the values defined below:
... ... @@ -245,69 +269,59 @@ static int compute_data_length(modbus_t *ctx, uint8_t *msg)
245 269 - read() or recv() error codes
246 270 */
247 271  
248   -static int receive_msg(modbus_t *ctx, int msg_length_computed,
249   - uint8_t *msg, msg_type_t msg_type)
  272 +static int receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
250 273 {
251   - int s_rc;
252   - int read_rc;
  274 + int rc;
253 275 fd_set rfds;
254 276 struct timeval tv;
255 277 int length_to_read;
256 278 uint8_t *p_msg;
257   - enum { FUNCTION, DATA, COMPLETE };
258   - int state;
259 279 int msg_length = 0;
  280 + _step_t step;
260 281  
261 282 if (ctx->debug) {
262 283 if (msg_type == MSG_INDICATION) {
263   - printf("Waiting for a indication");
  284 + printf("Waiting for a indication...\n");
264 285 } else {
265   - printf("Waiting for a confirmation");
  286 + printf("Waiting for a confirmation...\n");
266 287 }
267   -
268   - if (msg_length_computed == MSG_LENGTH_UNDEFINED)
269   - printf("...\n");
270   - else
271   - printf(" (%d bytes)...\n", msg_length_computed);
272 288 }
273 289  
274 290 /* Add a file descriptor to the set */
275 291 FD_ZERO(&rfds);
276 292 FD_SET(ctx->s, &rfds);
277 293  
278   - if (msg_length_computed == MSG_LENGTH_UNDEFINED) {
279   - /* Wait for a message */
  294 + /* We need to analyse the message step by step. At the first step, we want
  295 + * to reach the function code because all packets contain this
  296 + * information. */
  297 + step = _STEP_FUNCTION;
  298 + length_to_read = ctx->backend->header_length + 1;
  299 +
  300 + if (msg_type == MSG_INDICATION) {
  301 + /* Wait for a message, we don't know when the message will be
  302 + * received */
  303 + /* FIXME Not infinite */
280 304 tv.tv_sec = 60;
281 305 tv.tv_usec = 0;
282   -
283   - /* The message length is undefined (request receiving) so we need to
284   - * analyse the message step by step. At the first step, we want to
285   - * reach the function code because all packets contains this
286   - * information. */
287   - state = FUNCTION;
288   - msg_length_computed = ctx->backend->header_length + 1;
289 306 } else {
290 307 tv.tv_sec = ctx->timeout_begin.tv_sec;
291 308 tv.tv_usec = ctx->timeout_begin.tv_usec;
292   - state = COMPLETE;
293   - }
294   -
295   - length_to_read = msg_length_computed;
296   -
297   - s_rc = ctx->backend->select(ctx, &rfds, &tv, msg_length_computed, msg_length);
298   - if (s_rc == -1) {
299   - return -1;
300 309 }
301 310  
302 311 p_msg = msg;
303   - while (s_rc) {
304   - read_rc = ctx->backend->recv(ctx, p_msg, length_to_read);
305   - if (read_rc == 0) {
  312 + while (length_to_read != 0) {
  313 + rc = ctx->backend->select(ctx, &rfds, &tv, length_to_read);
  314 + if (rc == -1) {
  315 + return -1;
  316 + }
  317 +
  318 + rc = ctx->backend->recv(ctx, p_msg, length_to_read);
  319 + if (rc == 0) {
306 320 errno = ECONNRESET;
307   - read_rc = -1;
  321 + rc = -1;
308 322 }
309 323  
310   - if (read_rc == -1) {
  324 + if (rc == -1) {
311 325 _error_print(ctx, "read");
312 326 if (ctx->error_recovery && (errno == ECONNRESET ||
313 327 errno == ECONNREFUSED)) {
... ... @@ -315,71 +329,55 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed,
315 329 modbus_connect(ctx);
316 330 /* Could be removed by previous calls */
317 331 errno = ECONNRESET;
318   - return -1;
319 332 }
320 333 return -1;
321 334 }
322 335  
323   - /* Sums bytes received */
324   - msg_length += read_rc;
325   -
326 336 /* Display the hex code of each character received */
327 337 if (ctx->debug) {
328 338 int i;
329   - for (i=0; i < read_rc; i++)
  339 + for (i=0; i < rc; i++)
330 340 printf("<%.2X>", p_msg[i]);
331 341 }
332 342  
333   - if (msg_length < msg_length_computed) {
334   - /* Message incomplete */
335   - length_to_read = msg_length_computed - msg_length;
336   - } else {
337   - switch (state) {
338   - case FUNCTION:
  343 + /* Moves the pointer to receive other data */
  344 + p_msg = &(p_msg[rc]);
  345 + /* Sums bytes received */
  346 + msg_length += rc;
  347 + /* Computes remaining bytes */
  348 + length_to_read -= rc;
  349 +
  350 + if (length_to_read == 0) {
  351 + switch (step) {
  352 + case _STEP_FUNCTION:
339 353 /* Function code position */
340   - length_to_read = compute_header_length(
  354 + length_to_read = compute_meta_length_after_function(
341 355 msg[ctx->backend->header_length],
342 356 msg_type);
343   - msg_length_computed += length_to_read;
344   - /* It's useless to check the value of
345   - msg_length_computed in this case (only
346   - defined values are used). */
347 357 if (length_to_read != 0) {
348   - state = DATA;
  358 + step = _STEP_META;
349 359 break;
350   - } /* else switch straight to DATA */
351   - case DATA:
352   - length_to_read = compute_data_length(ctx, msg);
353   - msg_length_computed += length_to_read;
354   - if (msg_length_computed > ctx->backend->max_adu_length) {
  360 + } /* else switches straight to the next step */
  361 + case _STEP_META:
  362 + length_to_read = compute_data_length_after_meta(
  363 + ctx, msg, msg_type);
  364 + if ((msg_length + length_to_read) > ctx->backend->max_adu_length) {
355 365 errno = EMBBADDATA;
356 366 _error_print(ctx, "too many data");
357 367 return -1;
358 368 }
359   - state = COMPLETE;
  369 + step = _STEP_DATA;
360 370 break;
361   - case COMPLETE:
362   - length_to_read = 0;
  371 + default:
363 372 break;
364 373 }
365 374 }
366 375  
367   - /* Moves the pointer to receive other data */
368   - p_msg = &(p_msg[read_rc]);
369   -
370 376 if (length_to_read > 0) {
371 377 /* If no character at the buffer wait
372   - TIME_OUT_END_OF_TRAME before to generate an error. */
  378 + TIME_OUT_END_OF_TRAME before raising an error. */
373 379 tv.tv_sec = ctx->timeout_end.tv_sec;
374 380 tv.tv_usec = ctx->timeout_end.tv_usec;
375   -
376   - s_rc = ctx->backend->select(ctx, &rfds, &tv, msg_length_computed, msg_length);
377   - if (s_rc == -1) {
378   - return -1;
379   - }
380   - } else {
381   - /* All chars are received */
382   - s_rc = FALSE;
383 381 }
384 382 }
385 383  
... ... @@ -401,8 +399,7 @@ int modbus_receive(modbus_t *ctx, int sockfd, uint8_t *req)
401 399 ctx->s = sockfd;
402 400 }
403 401  
404   - /* The length of the request to receive isn't known. */
405   - return receive_msg(ctx, MSG_LENGTH_UNDEFINED, req, MSG_INDICATION);
  402 + return receive_msg(ctx, req, MSG_INDICATION);
406 403 }
407 404  
408 405 /* Receives the response and checks values.
... ... @@ -418,14 +415,21 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp)
418 415 int rsp_length_computed;
419 416 int offset = ctx->backend->header_length;
420 417  
421   - rsp_length_computed = compute_response_length(ctx, req);
422   - rc = receive_msg(ctx, rsp_length_computed, rsp, MSG_CONFIRMATION);
423   - if (rc != -1) {
424   - /* GOOD RESPONSE */
  418 + rc = receive_msg(ctx, rsp, MSG_CONFIRMATION);
  419 + if (rc == -1) {
  420 + return -1;
  421 + }
  422 +
  423 + rsp_length_computed = compute_response_length_from_request(ctx, req);
  424 +
  425 + /* Check length */
  426 + if (rc == rsp_length_computed ||
  427 + rsp_length_computed == MSG_LENGTH_UNDEFINED) {
425 428 int req_nb_value;
426 429 int rsp_nb_value;
427 430 int function = rsp[offset];
428 431  
  432 + /* Check function code */
429 433 if (function != req[offset]) {
430 434 if (ctx->debug) {
431 435 fprintf(stderr,
... ... @@ -436,8 +440,7 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp)
436 440 return -1;
437 441 }
438 442  
439   - /* The number of values is returned if it's corresponding
440   - * to the request */
  443 + /* Check the number of values is corresponding to the request */
441 444 switch (function) {
442 445 case _FC_READ_COILS:
443 446 case _FC_READ_DISCRETE_INPUTS:
... ... @@ -481,28 +484,21 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp)
481 484 errno = EMBBADDATA;
482 485 rc = -1;
483 486 }
484   - } else if (errno == EMBUNKEXC) {
  487 + } else if (rc == (offset + 2 + ctx->backend->checksum_length) &&
  488 + req[offset] == (rsp[offset] - 0x80)) {
485 489 /* EXCEPTION CODE RECEIVED */
486 490  
487   - /* CRC must be checked here (not done in receive_msg) */
488   - rc = ctx->backend->check_integrity(ctx, rsp,
489   - _MODBUS_EXCEPTION_RSP_LENGTH);
490   - if (rc == -1)
491   - return -1;
492   -
493   - /* Check for exception response.
494   - 0x80 + function is stored in the exception
495   - response. */
496   - if (0x80 + req[offset] == rsp[offset]) {
497   - int exception_code = rsp[offset + 1];
498   - if (exception_code < MODBUS_EXCEPTION_MAX) {
499   - errno = MODBUS_ENOBASE + exception_code;
500   - } else {
501   - errno = EMBBADEXC;
502   - }
503   - _error_print(ctx, NULL);
504   - return -1;
  491 + int exception_code = rsp[offset + 1];
  492 + if (exception_code < MODBUS_EXCEPTION_MAX) {
  493 + errno = MODBUS_ENOBASE + exception_code;
  494 + } else {
  495 + errno = EMBBADEXC;
505 496 }
  497 + _error_print(ctx, NULL);
  498 + rc = -1;
  499 + } else {
  500 + errno = EMBBADDATA;
  501 + rc = -1;
506 502 }
507 503  
508 504 return rc;
... ...
tests/unit-test-client.c
... ... @@ -270,7 +270,7 @@ int main(int argc, char *argv[])
270 270 memset(tab_rp_registers, 0, nb_points * sizeof(uint16_t));
271 271  
272 272 /* Write registers to zero from tab_rp_registers and read registers to
273   - tab_rp_registers. They should be same as UT_REGISTERS_TAB. */
  273 + tab_rp_registers. They should be same as UT_REGISTERS_TAB. */
274 274 rc = modbus_read_and_write_registers(ctx,
275 275 UT_REGISTERS_ADDRESS,
276 276 UT_REGISTERS_NB_POINTS,
... ... @@ -280,7 +280,7 @@ int main(int argc, char *argv[])
280 280 tab_rp_registers);
281 281 printf("4/5 modbus_read_and_write_registers, read part: ");
282 282 if (rc != UT_REGISTERS_NB_POINTS) {
283   - printf("FAILED (nb points %d)\n", rc);
  283 + printf("FAILED (nb points %d != %d)\n", rc, UT_REGISTERS_NB_POINTS);
284 284 goto close;
285 285 }
286 286  
... ... @@ -299,7 +299,7 @@ int main(int argc, char *argv[])
299 299 tab_rp_registers);
300 300 printf("5/5 modbus_read_and_write_registers, write part: ");
301 301 if (rc != UT_REGISTERS_NB_POINTS) {
302   - printf("FAILED (nb points %d)\n", rc);
  302 + printf("FAILED (nb points %d != %d)\n", rc, UT_REGISTERS_NB_POINTS);
303 303 goto close;
304 304 }
305 305  
... ... @@ -362,8 +362,8 @@ int main(int argc, char *argv[])
362 362 /** ILLEGAL DATA ADDRESS **/
363 363 printf("\nTEST ILLEGAL DATA ADDRESS:\n");
364 364  
365   - /* The mapping begins at 0 and ending at address + nb_points so
366   - * the addresses below are not valid. */
  365 + /* The mapping begins at 0 and ends at address + nb_points so
  366 + * the addresses are not valid. */
367 367  
368 368 rc = modbus_read_bits(ctx, UT_BITS_ADDRESS,
369 369 UT_BITS_NB_POINTS + 1,
... ...
tests/unit-test.h
1 1 /*
2   - * Copyright © 2008 Stéphane Raimbault <stephane.raimbault@gmail.com>
  2 + * Copyright © 2008-2010 Stéphane Raimbault <stephane.raimbault@gmail.com>
3 3 *
4 4 * This program is free software: you can redistribute it and/or modify
5 5 * it under the terms of the GNU General Public License as published by
... ...