Commit 14f42c1896f6d8fdce152e1f9bea82ae67939594

Authored by Stéphane Raimbault
1 parent efbbc3f7

Error handling improvements

- new function modbus_flush
- new names and values for error defines
- finer recovery on error
- merge TOO_MANY_DATA and INVALID_DATA
- stop unit-test-master at the first error
- FLUSH_OR_RECONNECT_ON_ERROR -> FLUSH_OR_CONNECT_ON_ERROR
- more precise tests in unit-test-master
src/modbus.c
... ... @@ -151,24 +151,44 @@ static const int TAB_MAX_ADU_LENGTH[2] = {
151 151 MAX_ADU_LENGTH_TCP,
152 152 };
153 153  
  154 +void modbus_flush(modbus_param_t *mb_param)
  155 +{
  156 + if (mb_param->type_com == RTU) {
  157 + tcflush(mb_param->fd, TCIOFLUSH);
  158 + } else {
  159 + int ret;
  160 + do {
  161 + /* Extract the garbage from the socket */
  162 + char devnull[MAX_ADU_LENGTH_TCP];
  163 + ret = recv(mb_param->fd, devnull, MAX_ADU_LENGTH_TCP, MSG_DONTWAIT);
  164 + if (mb_param->debug && ret > 0) {
  165 + printf("%d bytes flushed\n", ret);
  166 + }
  167 + } while (ret > 0);
  168 + }
  169 +}
  170 +
154 171 /* Treats errors and flush or close connection if necessary */
155 172 static void error_treat(modbus_param_t *mb_param, int code, const char *string)
156 173 {
157   - printf("\nERROR %s (%d)\n", string, code);
  174 + printf("\nERROR %s (%0X)\n", string, -code);
158 175  
159   - if (mb_param->error_handling == FLUSH_OR_RECONNECT_ON_ERROR) {
  176 + if (mb_param->error_handling == FLUSH_OR_CONNECT_ON_ERROR) {
160 177 switch (code) {
161   - case ILLEGAL_DATA_VALUE:
162   - case ILLEGAL_DATA_ADDRESS:
163   - case ILLEGAL_FUNCTION:
  178 + case INVALID_DATA:
  179 + case INVALID_CRC:
  180 + case INVALID_EXCEPTION_CODE:
  181 + modbus_flush(mb_param);
  182 + break;
  183 + case SELECT_FAILURE:
  184 + case SOCKET_FAILURE:
  185 + case CONNECTION_CLOSED:
  186 + modbus_close(mb_param);
  187 + modbus_connect(mb_param);
164 188 break;
165 189 default:
166   - if (mb_param->type_com == RTU) {
167   - tcflush(mb_param->fd, TCIOFLUSH);
168   - } else {
169   - modbus_close(mb_param);
170   - modbus_connect(mb_param);
171   - }
  190 + /* NOP */
  191 + break;
172 192 }
173 193 }
174 194 }
... ... @@ -394,10 +414,10 @@ static int modbus_send(modbus_param_t *mb_param, uint8_t *query,
394 414 ret = send(mb_param->fd, query, query_length, 0);
395 415  
396 416 /* Return the number of bytes written (0 to n)
397   - or PORT_SOCKET_FAILURE on error */
  417 + or SOCKET_FAILURE on error */
398 418 if ((ret == -1) || (ret != query_length)) {
399   - ret = PORT_SOCKET_FAILURE;
400   - error_treat(mb_param, ret, "Write port/socket failure");
  419 + ret = SOCKET_FAILURE;
  420 + error_treat(mb_param, ret, "Write socket failure");
401 421 }
402 422  
403 423 return ret;
... ... @@ -455,7 +475,7 @@ static int compute_query_length_data(modbus_param_t *mb_param, uint8_t *msg)
455 475 \
456 476 if (select_ret == 0) { \
457 477 /* Call to error_treat is done later to manage exceptions */ \
458   - return COMM_TIME_OUT; \
  478 + return SELECT_TIMEOUT; \
459 479 } \
460 480 }
461 481  
... ... @@ -534,9 +554,9 @@ static int receive_msg(modbus_param_t *mb_param,
534 554 return CONNECTION_CLOSED;
535 555 } else if (read_ret < 0) {
536 556 /* The only negative possible value is -1 */
537   - error_treat(mb_param, PORT_SOCKET_FAILURE,
538   - "Read port/socket failure");
539   - return PORT_SOCKET_FAILURE;
  557 + error_treat(mb_param, SOCKET_FAILURE,
  558 + "Read socket failure");
  559 + return SOCKET_FAILURE;
540 560 }
541 561  
542 562 /* Sums bytes received */
... ... @@ -568,8 +588,8 @@ static int receive_msg(modbus_param_t *mb_param,
568 588 length_to_read = compute_query_length_data(mb_param, msg);
569 589 msg_length_computed += length_to_read;
570 590 if (msg_length_computed > TAB_MAX_ADU_LENGTH[mb_param->type_com]) {
571   - error_treat(mb_param, TOO_MANY_DATA, "Too many data");
572   - return TOO_MANY_DATA;
  591 + error_treat(mb_param, INVALID_DATA, "Too many data");
  592 + return INVALID_DATA;
573 593 }
574 594 state = COMPLETE;
575 595 break;
... ... @@ -692,13 +712,13 @@ static int modbus_receive(modbus_param_t *mb_param,
692 712 ret = response_nb_value;
693 713 } else {
694 714 char *s_error = malloc(64 * sizeof(char));
695   - sprintf(s_error, "Quantity (%d) not corresponding to the query (%d)",
  715 + sprintf(s_error, "Quantity not corresponding to the query (%d != %d)",
696 716 response_nb_value, query_nb_value);
697   - ret = ILLEGAL_DATA_VALUE;
698   - error_treat(mb_param, ILLEGAL_DATA_VALUE, s_error);
  717 + ret = INVALID_DATA;
  718 + error_treat(mb_param, ret, s_error);
699 719 free(s_error);
700 720 }
701   - } else if (ret == COMM_TIME_OUT) {
  721 + } else if (ret == SELECT_TIMEOUT) {
702 722  
703 723 if (response_length == (offset + 2 + TAB_CHECKSUM_LENGTH[mb_param->type_com])) {
704 724 /* EXCEPTION CODE RECEIVED */
... ... @@ -745,8 +765,7 @@ static int modbus_receive(modbus_param_t *mb_param,
745 765 TIME OUT here */
746 766 }
747 767  
748   - /* COMMUNICATION TIME OUT */
749   - error_treat(mb_param, ret, "Communication time out");
  768 + error_treat(mb_param, ret, "Select timeout");
750 769 return ret;
751 770 }
752 771  
... ... @@ -1046,7 +1065,7 @@ int read_coil_status(modbus_param_t *mb_param, int start_addr,
1046 1065 if (nb > MAX_STATUS) {
1047 1066 printf("ERROR Too many coils status requested (%d > %d)\n",
1048 1067 nb, MAX_STATUS);
1049   - return TOO_MANY_DATA;
  1068 + return INVALID_DATA;
1050 1069 }
1051 1070  
1052 1071 status = read_io_status(mb_param, FC_READ_COIL_STATUS,
... ... @@ -1068,7 +1087,7 @@ int read_input_status(modbus_param_t *mb_param, int start_addr,
1068 1087 if (nb > MAX_STATUS) {
1069 1088 printf("ERROR Too many input status requested (%d > %d)\n",
1070 1089 nb, MAX_STATUS);
1071   - return TOO_MANY_DATA;
  1090 + return INVALID_DATA;
1072 1091 }
1073 1092  
1074 1093 status = read_io_status(mb_param, FC_READ_INPUT_STATUS,
... ... @@ -1092,7 +1111,7 @@ static int read_registers(modbus_param_t *mb_param, int function,
1092 1111 if (nb > MAX_REGISTERS) {
1093 1112 printf("EROOR Too many holding registers requested (%d > %d)\n",
1094 1113 nb, MAX_REGISTERS);
1095   - return TOO_MANY_DATA;
  1114 + return INVALID_DATA;
1096 1115 }
1097 1116  
1098 1117 query_length = build_query_basis(mb_param, function,
... ... @@ -1128,7 +1147,7 @@ int read_holding_registers(modbus_param_t *mb_param,
1128 1147 if (nb > MAX_REGISTERS) {
1129 1148 printf("ERROR Too many holding registers requested (%d > %d)\n",
1130 1149 nb, MAX_REGISTERS);
1131   - return TOO_MANY_DATA;
  1150 + return INVALID_DATA;
1132 1151 }
1133 1152  
1134 1153 status = read_registers(mb_param, FC_READ_HOLDING_REGISTERS,
... ... @@ -1146,7 +1165,7 @@ int read_input_registers(modbus_param_t *mb_param, int start_addr, int nb,
1146 1165 if (nb > MAX_REGISTERS) {
1147 1166 printf("ERROR Too many input registers requested (%d > %d)\n",
1148 1167 nb, MAX_REGISTERS);
1149   - return TOO_MANY_DATA;
  1168 + return INVALID_DATA;
1150 1169 }
1151 1170  
1152 1171 status = read_registers(mb_param, FC_READ_INPUT_REGISTERS,
... ... @@ -1219,7 +1238,7 @@ int force_multiple_coils(modbus_param_t *mb_param, int start_addr, int nb,
1219 1238 if (nb > MAX_STATUS) {
1220 1239 printf("ERROR Writing to too many coils (%d > %d)\n",
1221 1240 nb, MAX_STATUS);
1222   - return TOO_MANY_DATA;
  1241 + return INVALID_DATA;
1223 1242 }
1224 1243  
1225 1244 query_length = build_query_basis(mb_param, FC_FORCE_MULTIPLE_COILS,
... ... @@ -1268,7 +1287,7 @@ int preset_multiple_registers(modbus_param_t *mb_param, int start_addr, int nb,
1268 1287 if (nb > MAX_REGISTERS) {
1269 1288 printf("ERROR Trying to write to too many registers (%d > %d)\n",
1270 1289 nb, MAX_REGISTERS);
1271   - return TOO_MANY_DATA;
  1290 + return INVALID_DATA;
1272 1291 }
1273 1292  
1274 1293 query_length = build_query_basis(mb_param, FC_PRESET_MULTIPLE_REGISTERS,
... ... @@ -1344,7 +1363,7 @@ void modbus_init_rtu(modbus_param_t *mb_param, const char *device,
1344 1363 mb_param->data_bit = data_bit;
1345 1364 mb_param->stop_bit = stop_bit;
1346 1365 mb_param->type_com = RTU;
1347   - mb_param->error_handling = FLUSH_OR_RECONNECT_ON_ERROR;
  1366 + mb_param->error_handling = FLUSH_OR_CONNECT_ON_ERROR;
1348 1367 mb_param->slave = slave;
1349 1368 }
1350 1369  
... ... @@ -1363,16 +1382,15 @@ void modbus_init_tcp(modbus_param_t *mb_param, const char *ip, int port, int sla
1363 1382 strncpy(mb_param->ip, ip, sizeof(char)*16);
1364 1383 mb_param->port = port;
1365 1384 mb_param->type_com = TCP;
1366   - mb_param->error_handling = FLUSH_OR_RECONNECT_ON_ERROR;
  1385 + mb_param->error_handling = FLUSH_OR_CONNECT_ON_ERROR;
1367 1386 mb_param->slave = slave;
1368 1387 }
1369 1388  
1370   -/* By default, the error handling mode used is FLUSH_OR_RECONNECT_ON_ERROR.
  1389 +/* By default, the error handling mode used is FLUSH_OR_CONNECT_ON_ERROR.
1371 1390  
1372   - With FLUSH_OR_RECONNECT_ON_ERROR, the library will flush to I/O
1373   - port in RTU mode or attempt an immediate reconnection which may
1374   - hang for several seconds if the network to the remote target unit
1375   - is down in TCP mode.
  1391 + With FLUSH_OR_CONNECT_ON_ERROR, the library will attempt an immediate
  1392 + reconnection which may hang for several seconds if the network to
  1393 + the remote target unit is down.
1376 1394  
1377 1395 With NOP_ON_ERROR, it is expected that the application will
1378 1396 check for error returns and deal with them as necessary.
... ... @@ -1380,7 +1398,7 @@ void modbus_init_tcp(modbus_param_t *mb_param, const char *ip, int port, int sla
1380 1398 void modbus_set_error_handling(modbus_param_t *mb_param,
1381 1399 error_handling_t error_handling)
1382 1400 {
1383   - if (error_handling == FLUSH_OR_RECONNECT_ON_ERROR ||
  1401 + if (error_handling == FLUSH_OR_CONNECT_ON_ERROR ||
1384 1402 error_handling == NOP_ON_ERROR) {
1385 1403 mb_param->error_handling = error_handling;
1386 1404 } else {
... ...
src/modbus.h
... ... @@ -120,19 +120,20 @@ extern &quot;C&quot; {
120 120 #define GATEWAY_PROBLEM_TARGET -0x0B
121 121  
122 122 /* Local */
123   -#define COMM_TIME_OUT -0x0C
124   -#define PORT_SOCKET_FAILURE -0x0D
125   -#define SELECT_FAILURE -0x0E
126   -#define TOO_MANY_DATA -0x0F
127   -#define INVALID_CRC -0x10
128   -#define INVALID_EXCEPTION_CODE -0x11
129   -#define CONNECTION_CLOSED -0x12
  123 +#define INVALID_DATA -0x10
  124 +#define INVALID_CRC -0x11
  125 +#define INVALID_EXCEPTION_CODE -0x12
  126 +
  127 +#define SELECT_TIMEOUT -0x13
  128 +#define SELECT_FAILURE -0x14
  129 +#define SOCKET_FAILURE -0x15
  130 +#define CONNECTION_CLOSED -0x16
130 131  
131 132 /* Internal using */
132 133 #define MSG_LENGTH_UNDEFINED -1
133 134  
134 135 typedef enum { RTU=0, TCP } type_com_t;
135   -typedef enum { FLUSH_OR_RECONNECT_ON_ERROR, NOP_ON_ERROR } error_handling_t;
  136 +typedef enum { FLUSH_OR_CONNECT_ON_ERROR, NOP_ON_ERROR } error_handling_t;
136 137  
137 138 /* This structure is byte-aligned */
138 139 typedef struct {
... ... @@ -250,9 +251,9 @@ void modbus_init_rtu(modbus_param_t *mb_param, const char *device,
250 251 void modbus_init_tcp(modbus_param_t *mb_param, const char *ip_address, int port,
251 252 int slave);
252 253  
253   -/* By default, the error handling mode used is RECONNECT_ON_ERROR.
  254 +/* By default, the error handling mode used is CONNECT_ON_ERROR.
254 255  
255   - With RECONNECT_ON_ERROR, the library will attempt an immediate
  256 + With FLUSH_OR_CONNECT_ON_ERROR, the library will attempt an immediate
256 257 reconnection which may hang for several seconds if the network to
257 258 the remote target unit is down.
258 259  
... ... @@ -270,6 +271,9 @@ int modbus_connect(modbus_param_t *mb_param);
270 271 /* Closes a modbus connection */
271 272 void modbus_close(modbus_param_t *mb_param);
272 273  
  274 +/* Flush the pending request */
  275 +void modbus_flush(modbus_param_t *mb_param);
  276 +
273 277 /* Activates the debug messages */
274 278 void modbus_set_debug(modbus_param_t *mb_param, int boolean);
275 279  
... ...
tests/unit-test-master.c
... ... @@ -42,8 +42,7 @@ int main(void)
42 42  
43 43 /* TCP */
44 44 modbus_init_tcp(&mb_param, "127.0.0.1", 1502, SLAVE);
45   -/* modbus_set_debug(&mb_param, TRUE);*/
46   - modbus_set_error_handling(&mb_param, NOP_ON_ERROR);
  45 +/* modbus_set_debug(&mb_param, TRUE); */
47 46  
48 47 if (modbus_connect(&mb_param) == -1) {
49 48 printf("ERROR Connection failed\n");
... ... @@ -267,8 +266,10 @@ int main(void)
267 266 printf("* read_coil_status: ");
268 267 if (ret == ILLEGAL_DATA_ADDRESS)
269 268 printf("OK\n");
270   - else
  269 + else {
271 270 printf("FAILED\n");
  271 + goto close;
  272 + }
272 273  
273 274 ret = read_input_status(&mb_param,
274 275 UT_INPUT_STATUS_ADDRESS,
... ... @@ -277,8 +278,10 @@ int main(void)
277 278 printf("* read_input_status: ");
278 279 if (ret == ILLEGAL_DATA_ADDRESS)
279 280 printf("OK\n");
280   - else
  281 + else {
281 282 printf("FAILED\n");
  283 + goto close;
  284 + }
282 285  
283 286 ret = read_holding_registers(&mb_param,
284 287 UT_HOLDING_REGISTERS_ADDRESS,
... ... @@ -287,8 +290,10 @@ int main(void)
287 290 printf("* read_holding_registers: ");
288 291 if (ret == ILLEGAL_DATA_ADDRESS)
289 292 printf("OK\n");
290   - else
  293 + else {
291 294 printf("FAILED\n");
  295 + goto close;
  296 + }
292 297  
293 298 ret = read_input_registers(&mb_param,
294 299 UT_INPUT_REGISTERS_ADDRESS,
... ... @@ -297,8 +302,10 @@ int main(void)
297 302 printf("* read_input_registers: ");
298 303 if (ret == ILLEGAL_DATA_ADDRESS)
299 304 printf("OK\n");
300   - else
  305 + else {
301 306 printf("FAILED\n");
  307 + goto close;
  308 + }
302 309  
303 310 ret = force_single_coil(&mb_param,
304 311 UT_COIL_STATUS_ADDRESS + UT_COIL_STATUS_NB_POINTS,
... ... @@ -308,6 +315,7 @@ int main(void)
308 315 printf("OK\n");
309 316 } else {
310 317 printf("FAILED\n");
  318 + goto close;
311 319 }
312 320  
313 321 ret = force_multiple_coils(&mb_param,
... ... @@ -319,6 +327,7 @@ int main(void)
319 327 printf("OK\n");
320 328 } else {
321 329 printf("FAILED\n");
  330 + goto close;
322 331 }
323 332  
324 333 ret = preset_multiple_registers(&mb_param,
... ... @@ -330,6 +339,7 @@ int main(void)
330 339 printf("OK\n");
331 340 } else {
332 341 printf("FAILED\n");
  342 + goto close;
333 343 }
334 344  
335 345  
... ... @@ -341,49 +351,58 @@ int main(void)
341 351 MAX_STATUS + 1,
342 352 tab_rp_status);
343 353 printf("* read_coil_status: ");
344   - if (ret == TOO_MANY_DATA)
  354 + if (ret == INVALID_DATA) {
345 355 printf("OK\n");
346   - else
  356 + } else {
347 357 printf("FAILED\n");
  358 + goto close;
  359 + }
348 360  
349 361 ret = read_input_status(&mb_param,
350 362 UT_INPUT_STATUS_ADDRESS,
351 363 MAX_STATUS + 1,
352 364 tab_rp_status);
353 365 printf("* read_input_status: ");
354   - if (ret == TOO_MANY_DATA)
  366 + if (ret == INVALID_DATA) {
355 367 printf("OK\n");
356   - else
  368 + } else {
357 369 printf("FAILED\n");
  370 + goto close;
  371 + }
358 372  
359 373 ret = read_holding_registers(&mb_param,
360 374 UT_HOLDING_REGISTERS_ADDRESS,
361 375 MAX_REGISTERS + 1,
362 376 tab_rp_registers);
363 377 printf("* read_holding_registers: ");
364   - if (ret == TOO_MANY_DATA)
  378 + if (ret == INVALID_DATA) {
365 379 printf("OK\n");
366   - else
  380 + } else {
367 381 printf("FAILED\n");
  382 + goto close;
  383 + }
368 384  
369 385 ret = read_input_registers(&mb_param,
370 386 UT_INPUT_REGISTERS_ADDRESS,
371 387 MAX_REGISTERS + 1,
372 388 tab_rp_registers);
373 389 printf("* read_input_registers: ");
374   - if (ret == TOO_MANY_DATA)
  390 + if (ret == INVALID_DATA) {
375 391 printf("OK\n");
376   - else
  392 + } else {
377 393 printf("FAILED\n");
  394 + goto close;
  395 + }
378 396  
379 397 ret = force_multiple_coils(&mb_param,
380 398 UT_COIL_STATUS_ADDRESS,
381 399 MAX_STATUS + 1,
382 400 tab_rp_status);
383 401 printf("* force_multiple_coils: ");
384   - if (ret == TOO_MANY_DATA) {
  402 + if (ret == INVALID_DATA) {
385 403 printf("OK\n");
386 404 } else {
  405 + goto close;
387 406 printf("FAILED\n");
388 407 }
389 408  
... ... @@ -392,10 +411,11 @@ int main(void)
392 411 MAX_REGISTERS + 1,
393 412 tab_rp_registers);
394 413 printf("* preset_multiple_registers: ");
395   - if (ret == TOO_MANY_DATA) {
  414 + if (ret == INVALID_DATA) {
396 415 printf("OK\n");
397 416 } else {
398 417 printf("FAILED\n");
  418 + goto close;
399 419 }
400 420  
401 421 /** SLAVE REPLY **/
... ... @@ -407,10 +427,11 @@ int main(void)
407 427 UT_HOLDING_REGISTERS_NB_POINTS,
408 428 tab_rp_registers);
409 429 printf("1/2 No reply from slave %d: ", mb_param.slave);
410   - if (ret != UT_HOLDING_REGISTERS_NB_POINTS) {
  430 + if (ret == SELECT_TIMEOUT) {
411 431 printf("OK\n", ret);
412 432 } else {
413 433 printf("FAILED\n");
  434 + goto close;
414 435 }
415 436  
416 437 mb_param.slave = MODBUS_BROADCAST_ADDRESS;
... ... @@ -422,6 +443,7 @@ int main(void)
422 443 if (ret == UT_HOLDING_REGISTERS_NB_POINTS) {
423 444 printf("OK\n", ret);
424 445 } else {
  446 + goto close;
425 447 printf("FAILED\n");
426 448 }
427 449  
... ... @@ -436,11 +458,11 @@ int main(void)
436 458 UT_HOLDING_REGISTERS_NB_POINTS_SPECIAL,
437 459 tab_rp_registers_bad);
438 460 printf("* read_holding_registers: ");
439   - if (ret > 0) {
440   - /* Error not detected */
441   - printf("FAILED\n");
442   - } else {
  461 + if (ret == INVALID_DATA) {
443 462 printf("OK\n");
  463 + } else {
  464 + printf("FAILED\n");
  465 + goto close;
444 466 }
445 467 free(tab_rp_registers_bad);
446 468  
... ...