Commit c44dcfd5a2a5f56c8885b89f41d1519859a1815c
1 parent
15608980
Make the TCP implementation "protocol independent", i.e. IPv6 capable.
Inspired by the branch ff/ipv6 of Florian Forster but this version uses a specific PI backend to isolate the IPv4 and IPv6 implementations. The existing TCP/IPv4 implementation has been kept intact (not rebased on the PI one) because its data backend requires fewer bytes than the PI. New functions modbus_new_tcp_pi, modbus_tcp_pi_listen and modbus_tcp_pi_accept and new backend called modbus_tcp_pi_t.
Showing
6 changed files
with
336 additions
and
27 deletions
AUTHORS
src/modbus-tcp-private.h
| ... | ... | @@ -31,4 +31,16 @@ typedef struct _modbus_tcp { |
| 31 | 31 | char ip[16]; |
| 32 | 32 | } modbus_tcp_t; |
| 33 | 33 | |
| 34 | +#define _MODBUS_TCP_PI_NODE_LENGTH 1025 | |
| 35 | +#define _MODBUS_TCP_PI_SERVICE_LENGTH 32 | |
| 36 | + | |
| 37 | +typedef struct _modbus_tcp_pi { | |
| 38 | + /* TCP port */ | |
| 39 | + int port; | |
| 40 | + /* Node */ | |
| 41 | + char node[_MODBUS_TCP_PI_NODE_LENGTH]; | |
| 42 | + /* Service */ | |
| 43 | + char service[_MODBUS_TCP_PI_SERVICE_LENGTH]; | |
| 44 | +} modbus_tcp_pi_t; | |
| 45 | + | |
| 34 | 46 | #endif /* _MODBUS_TCP_PRIVATE_H_ */ | ... | ... |
src/modbus-tcp.c
| ... | ... | @@ -42,6 +42,8 @@ |
| 42 | 42 | # include <netinet/ip.h> |
| 43 | 43 | # include <netinet/tcp.h> |
| 44 | 44 | # include <arpa/inet.h> |
| 45 | +# include <poll.h> | |
| 46 | +# include <netdb.h> | |
| 45 | 47 | #endif |
| 46 | 48 | |
| 47 | 49 | #if !defined(MSG_NOSIGNAL) |
| ... | ... | @@ -185,32 +187,17 @@ int _modbus_tcp_check_integrity(modbus_t *ctx, uint8_t *msg, const int msg_lengt |
| 185 | 187 | return msg_length; |
| 186 | 188 | } |
| 187 | 189 | |
| 188 | -/* Establishes a modbus TCP connection with a Modbus server. */ | |
| 189 | -static int _modbus_tcp_connect(modbus_t *ctx) | |
| 190 | +static int _modbus_tcp_set_ipv4_options(int s) | |
| 190 | 191 | { |
| 191 | 192 | int rc; |
| 192 | 193 | int option; |
| 193 | - struct sockaddr_in addr; | |
| 194 | - modbus_tcp_t *ctx_tcp = ctx->backend_data; | |
| 195 | - | |
| 196 | -#ifdef OS_WIN32 | |
| 197 | - if (_modbus_tcp_init_win32() == -1) { | |
| 198 | - return -1; | |
| 199 | - } | |
| 200 | -#endif | |
| 201 | - | |
| 202 | - ctx->s = socket(PF_INET, SOCK_STREAM, 0); | |
| 203 | - if (ctx->s == -1) { | |
| 204 | - return -1; | |
| 205 | - } | |
| 206 | 194 | |
| 207 | 195 | /* Set the TCP no delay flag */ |
| 208 | 196 | /* SOL_TCP = IPPROTO_TCP */ |
| 209 | 197 | option = 1; |
| 210 | - rc = setsockopt(ctx->s, IPPROTO_TCP, TCP_NODELAY, | |
| 198 | + rc = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, | |
| 211 | 199 | (const void *)&option, sizeof(int)); |
| 212 | 200 | if (rc == -1) { |
| 213 | - close(ctx->s); | |
| 214 | 201 | return -1; |
| 215 | 202 | } |
| 216 | 203 | |
| ... | ... | @@ -221,14 +208,40 @@ static int _modbus_tcp_connect(modbus_t *ctx) |
| 221 | 208 | **/ |
| 222 | 209 | /* Set the IP low delay option */ |
| 223 | 210 | option = IPTOS_LOWDELAY; |
| 224 | - rc = setsockopt(ctx->s, IPPROTO_IP, IP_TOS, | |
| 211 | + rc = setsockopt(s, IPPROTO_IP, IP_TOS, | |
| 225 | 212 | (const void *)&option, sizeof(int)); |
| 226 | 213 | if (rc == -1) { |
| 227 | - close(ctx->s); | |
| 228 | 214 | return -1; |
| 229 | 215 | } |
| 230 | 216 | #endif |
| 231 | 217 | |
| 218 | + return 0; | |
| 219 | +} | |
| 220 | + | |
| 221 | +/* Establishes a modbus TCP connection with a Modbus server. */ | |
| 222 | +static int _modbus_tcp_connect(modbus_t *ctx) | |
| 223 | +{ | |
| 224 | + int rc; | |
| 225 | + struct sockaddr_in addr; | |
| 226 | + modbus_tcp_t *ctx_tcp = ctx->backend_data; | |
| 227 | + | |
| 228 | +#ifdef OS_WIN32 | |
| 229 | + if (_modbus_tcp_init_win32() == -1) { | |
| 230 | + return -1; | |
| 231 | + } | |
| 232 | +#endif | |
| 233 | + | |
| 234 | + ctx->s = socket(PF_INET, SOCK_STREAM, 0); | |
| 235 | + if (ctx->s == -1) { | |
| 236 | + return -1; | |
| 237 | + } | |
| 238 | + | |
| 239 | + rc = _modbus_tcp_set_ipv4_options(ctx->s); | |
| 240 | + if (rc == -1) { | |
| 241 | + close(ctx->s); | |
| 242 | + return -1; | |
| 243 | + } | |
| 244 | + | |
| 232 | 245 | if (ctx->debug) { |
| 233 | 246 | printf("Connecting to %s\n", ctx_tcp->ip); |
| 234 | 247 | } |
| ... | ... | @@ -246,6 +259,61 @@ static int _modbus_tcp_connect(modbus_t *ctx) |
| 246 | 259 | return 0; |
| 247 | 260 | } |
| 248 | 261 | |
| 262 | +/* Establishes a modbus TCP PI connection with a Modbus server. */ | |
| 263 | +static int _modbus_tcp_pi_connect(modbus_t *ctx) | |
| 264 | +{ | |
| 265 | + int rc; | |
| 266 | + struct addrinfo *ai_list; | |
| 267 | + struct addrinfo *ai_ptr; | |
| 268 | + struct addrinfo ai_hints; | |
| 269 | + modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data; | |
| 270 | + | |
| 271 | + memset(&ai_hints, 0, sizeof(ai_hints)); | |
| 272 | +#ifdef AI_ADDRCONFIG | |
| 273 | + ai_hints.ai_flags |= AI_ADDRCONFIG; | |
| 274 | +#endif | |
| 275 | + ai_hints.ai_family = AF_UNSPEC; | |
| 276 | + ai_hints.ai_socktype = SOCK_STREAM; | |
| 277 | + ai_hints.ai_addr = NULL; | |
| 278 | + ai_hints.ai_canonname = NULL; | |
| 279 | + ai_hints.ai_next = NULL; | |
| 280 | + | |
| 281 | + ai_list = NULL; | |
| 282 | + rc = getaddrinfo(ctx_tcp_pi->node, ctx_tcp_pi->service, | |
| 283 | + &ai_hints, &ai_list); | |
| 284 | + if (rc != 0) | |
| 285 | + return rc; | |
| 286 | + | |
| 287 | + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) { | |
| 288 | + int s; | |
| 289 | + | |
| 290 | + s = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); | |
| 291 | + if (s < 0) | |
| 292 | + continue; | |
| 293 | + | |
| 294 | + if (ai_ptr->ai_family == AF_INET) | |
| 295 | + _modbus_tcp_set_ipv4_options(s); | |
| 296 | + | |
| 297 | + rc = connect(s, ai_ptr->ai_addr, ai_ptr->ai_addrlen); | |
| 298 | + if (rc != 0) { | |
| 299 | + close(s); | |
| 300 | + continue; | |
| 301 | + } | |
| 302 | + | |
| 303 | + ctx->s = s; | |
| 304 | + break; | |
| 305 | + } | |
| 306 | + | |
| 307 | + freeaddrinfo(ai_list); | |
| 308 | + | |
| 309 | + if (ctx->s < 0) { | |
| 310 | + errno = ENOTCONN; | |
| 311 | + return -1; | |
| 312 | + } | |
| 313 | + | |
| 314 | + return 0; | |
| 315 | +} | |
| 316 | + | |
| 249 | 317 | /* Closes the network connection and socket in TCP mode */ |
| 250 | 318 | void _modbus_tcp_close(modbus_t *ctx) |
| 251 | 319 | { |
| ... | ... | @@ -330,6 +398,98 @@ int modbus_tcp_listen(modbus_t *ctx, int nb_connection) |
| 330 | 398 | return new_socket; |
| 331 | 399 | } |
| 332 | 400 | |
| 401 | +int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection) | |
| 402 | +{ | |
| 403 | + int rc; | |
| 404 | + struct addrinfo *ai_list; | |
| 405 | + struct addrinfo *ai_ptr; | |
| 406 | + struct addrinfo ai_hints; | |
| 407 | + const char *node; | |
| 408 | + const char *service; | |
| 409 | + int new_socket; | |
| 410 | + modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data; | |
| 411 | + | |
| 412 | + if (ctx_tcp_pi->node[0] == 0) | |
| 413 | + node = NULL; /* == any */ | |
| 414 | + else | |
| 415 | + node = ctx_tcp_pi->node; | |
| 416 | + | |
| 417 | + if (ctx_tcp_pi->service[0] == 0) | |
| 418 | + service = "502"; | |
| 419 | + else | |
| 420 | + service = ctx_tcp_pi->service; | |
| 421 | + | |
| 422 | + memset(&ai_hints, 0, sizeof (ai_hints)); | |
| 423 | + ai_hints.ai_flags |= AI_PASSIVE; | |
| 424 | +#ifdef AI_ADDRCONFIG | |
| 425 | + ai_hints.ai_flags |= AI_ADDRCONFIG; | |
| 426 | +#endif | |
| 427 | + ai_hints.ai_family = AF_UNSPEC; | |
| 428 | + ai_hints.ai_socktype = SOCK_STREAM; | |
| 429 | + ai_hints.ai_addr = NULL; | |
| 430 | + ai_hints.ai_canonname = NULL; | |
| 431 | + ai_hints.ai_next = NULL; | |
| 432 | + | |
| 433 | + ai_list = NULL; | |
| 434 | + rc = getaddrinfo(node, service, &ai_hints, &ai_list); | |
| 435 | + if (rc != 0) | |
| 436 | + return -1; | |
| 437 | + | |
| 438 | + new_socket = -1; | |
| 439 | + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) { | |
| 440 | + int s; | |
| 441 | + | |
| 442 | + s = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, | |
| 443 | + ai_ptr->ai_protocol); | |
| 444 | + if (s < 0) { | |
| 445 | + if (ctx->debug) { | |
| 446 | + perror("socket"); | |
| 447 | + } | |
| 448 | + continue; | |
| 449 | + } else { | |
| 450 | + int yes = 1; | |
| 451 | + rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, | |
| 452 | + (void *) &yes, sizeof (yes)); | |
| 453 | + if (rc != 0) { | |
| 454 | + close(s); | |
| 455 | + if (ctx->debug) { | |
| 456 | + perror("setsockopt"); | |
| 457 | + } | |
| 458 | + continue; | |
| 459 | + } | |
| 460 | + } | |
| 461 | + | |
| 462 | + rc = bind(s, ai_ptr->ai_addr, ai_ptr->ai_addrlen); | |
| 463 | + if (rc != 0) { | |
| 464 | + close(s); | |
| 465 | + if (ctx->debug) { | |
| 466 | + perror("bind"); | |
| 467 | + } | |
| 468 | + continue; | |
| 469 | + } | |
| 470 | + | |
| 471 | + rc = listen(s, nb_connection); | |
| 472 | + if (rc != 0) { | |
| 473 | + close(s); | |
| 474 | + if (ctx->debug) { | |
| 475 | + perror("listen"); | |
| 476 | + } | |
| 477 | + continue; | |
| 478 | + } | |
| 479 | + | |
| 480 | + new_socket = s; | |
| 481 | + break; | |
| 482 | + } | |
| 483 | + freeaddrinfo(ai_list); | |
| 484 | + | |
| 485 | + if (new_socket < 0) { | |
| 486 | + errno = ENOTCONN; | |
| 487 | + return -1; | |
| 488 | + } | |
| 489 | + | |
| 490 | + return new_socket; | |
| 491 | +} | |
| 492 | + | |
| 333 | 493 | /* On success, the function return a non-negative integer that is a descriptor |
| 334 | 494 | for the accepted socket. On error, -1 is returned, and errno is set |
| 335 | 495 | appropriately. */ |
| ... | ... | @@ -338,7 +498,7 @@ int modbus_tcp_accept(modbus_t *ctx, int *socket) |
| 338 | 498 | struct sockaddr_in addr; |
| 339 | 499 | socklen_t addrlen; |
| 340 | 500 | |
| 341 | - addrlen = sizeof(struct sockaddr_in); | |
| 501 | + addrlen = sizeof(addr); | |
| 342 | 502 | ctx->s = accept(*socket, (struct sockaddr *)&addr, &addrlen); |
| 343 | 503 | if (ctx->s == -1) { |
| 344 | 504 | close(*socket); |
| ... | ... | @@ -347,7 +507,27 @@ int modbus_tcp_accept(modbus_t *ctx, int *socket) |
| 347 | 507 | } |
| 348 | 508 | |
| 349 | 509 | if (ctx->debug) { |
| 350 | - printf("The client %s is connected\n", inet_ntoa(addr.sin_addr)); | |
| 510 | + printf("The client connection from %s is accepted\n", | |
| 511 | + inet_ntoa(addr.sin_addr)); | |
| 512 | + } | |
| 513 | + | |
| 514 | + return ctx->s; | |
| 515 | +} | |
| 516 | + | |
| 517 | +int modbus_tcp_pi_accept(modbus_t *ctx, int *socket) | |
| 518 | +{ | |
| 519 | + struct sockaddr_storage addr; | |
| 520 | + socklen_t addrlen; | |
| 521 | + | |
| 522 | + addrlen = sizeof(addr); | |
| 523 | + ctx->s = accept(*socket, (void *)&addr, &addrlen); | |
| 524 | + if (ctx->s == -1) { | |
| 525 | + close(*socket); | |
| 526 | + *socket = 0; | |
| 527 | + } | |
| 528 | + | |
| 529 | + if (ctx->debug) { | |
| 530 | + printf("The client connection is accepted."); | |
| 351 | 531 | } |
| 352 | 532 | |
| 353 | 533 | return ctx->s; |
| ... | ... | @@ -412,9 +592,29 @@ const modbus_backend_t _modbus_tcp_backend = { |
| 412 | 592 | }; |
| 413 | 593 | |
| 414 | 594 | |
| 595 | +const modbus_backend_t _modbus_tcp_pi_backend = { | |
| 596 | + _MODBUS_BACKEND_TYPE_TCP, | |
| 597 | + _MODBUS_TCP_HEADER_LENGTH, | |
| 598 | + _MODBUS_TCP_CHECKSUM_LENGTH, | |
| 599 | + MODBUS_TCP_MAX_ADU_LENGTH, | |
| 600 | + _modbus_set_slave, | |
| 601 | + _modbus_tcp_build_request_basis, | |
| 602 | + _modbus_tcp_build_response_basis, | |
| 603 | + _modbus_tcp_prepare_response_tid, | |
| 604 | + _modbus_tcp_send_msg_pre, | |
| 605 | + _modbus_tcp_send, | |
| 606 | + _modbus_tcp_recv, | |
| 607 | + _modbus_tcp_check_integrity, | |
| 608 | + _modbus_tcp_pi_connect, | |
| 609 | + _modbus_tcp_close, | |
| 610 | + _modbus_tcp_flush, | |
| 611 | + _modbus_tcp_select, | |
| 612 | + _modbus_tcp_filter_request | |
| 613 | +}; | |
| 614 | + | |
| 415 | 615 | /* Allocates and initializes the modbus_t structure for TCP. |
| 416 | - - ip : "192.168.0.5" | |
| 417 | - - port : 1099 | |
| 616 | + - ip: '192.168.0.5' | |
| 617 | + - port: 1099 | |
| 418 | 618 | |
| 419 | 619 | Set the port to MODBUS_TCP_DEFAULT_PORT to use the default one |
| 420 | 620 | (502). It's convenient to use a port number greater than or equal |
| ... | ... | @@ -425,6 +625,8 @@ modbus_t* modbus_new_tcp(const char *ip, int port) |
| 425 | 625 | { |
| 426 | 626 | modbus_t *ctx; |
| 427 | 627 | modbus_tcp_t *ctx_tcp; |
| 628 | + size_t dest_size; | |
| 629 | + size_t ret_size; | |
| 428 | 630 | |
| 429 | 631 | #if defined(OS_BSD) |
| 430 | 632 | /* MSG_NOSIGNAL is unsupported on *BSD so we install an ignore |
| ... | ... | @@ -450,8 +652,84 @@ modbus_t* modbus_new_tcp(const char *ip, int port) |
| 450 | 652 | ctx->backend_data = (modbus_tcp_t *) malloc(sizeof(modbus_tcp_t)); |
| 451 | 653 | ctx_tcp = (modbus_tcp_t *)ctx->backend_data; |
| 452 | 654 | |
| 453 | - strncpy(ctx_tcp->ip, ip, sizeof(char)*16); | |
| 655 | + dest_size = sizeof(char) * 16; | |
| 656 | + ret_size = strlcpy(ctx_tcp->ip, ip, dest_size); | |
| 657 | + if (ret_size == 0) { | |
| 658 | + fprintf(stderr, "The IP string is empty\n"); | |
| 659 | + modbus_free(ctx); | |
| 660 | + errno = EINVAL; | |
| 661 | + return NULL; | |
| 662 | + } | |
| 663 | + | |
| 664 | + if (ret_size >= dest_size) { | |
| 665 | + fprintf(stderr, "The IP string has been truncated\n"); | |
| 666 | + modbus_free(ctx); | |
| 667 | + errno = EINVAL; | |
| 668 | + return NULL; | |
| 669 | + } | |
| 670 | + | |
| 454 | 671 | ctx_tcp->port = port; |
| 455 | 672 | |
| 456 | 673 | return ctx; |
| 457 | 674 | } |
| 675 | + | |
| 676 | +/* Allocates and initializes the modbus_t structure for TCP in a protocol | |
| 677 | + indepedent fashin, i.e. IPv4/IPv6 agnostic. | |
| 678 | + | |
| 679 | + - node: host name or IP address of the host to connect to, eg. '192.168.0.5' | |
| 680 | + or 'server.com'. | |
| 681 | + - service: service name/port number to connect to. Use NULL for the default | |
| 682 | + port, 502/TCP. | |
| 683 | +*/ | |
| 684 | +modbus_t* modbus_new_tcp_pi(const char *node, const char *service) | |
| 685 | +{ | |
| 686 | + modbus_t *ctx; | |
| 687 | + modbus_tcp_pi_t *ctx_tcp_pi; | |
| 688 | + size_t dest_size; | |
| 689 | + size_t ret_size; | |
| 690 | + | |
| 691 | + ctx = (modbus_t *) malloc(sizeof(modbus_t)); | |
| 692 | + _modbus_init_common(ctx); | |
| 693 | + | |
| 694 | + /* Could be changed after to reach a remote serial Modbus device */ | |
| 695 | + ctx->slave = MODBUS_TCP_SLAVE; | |
| 696 | + | |
| 697 | + ctx->backend = &(_modbus_tcp_pi_backend); | |
| 698 | + | |
| 699 | + ctx->backend_data = (modbus_tcp_pi_t *) malloc(sizeof(modbus_tcp_pi_t)); | |
| 700 | + ctx_tcp_pi = (modbus_tcp_pi_t *)ctx->backend_data; | |
| 701 | + | |
| 702 | + dest_size = sizeof(char) * _MODBUS_TCP_PI_NODE_LENGTH; | |
| 703 | + ret_size = strlcpy(ctx_tcp_pi->node, node, dest_size); | |
| 704 | + if (ret_size == 0) { | |
| 705 | + fprintf(stderr, "The node string is empty\n"); | |
| 706 | + modbus_free(ctx); | |
| 707 | + errno = EINVAL; | |
| 708 | + return NULL; | |
| 709 | + } | |
| 710 | + | |
| 711 | + if (ret_size >= dest_size) { | |
| 712 | + fprintf(stderr, "The node string has been truncated\n"); | |
| 713 | + modbus_free(ctx); | |
| 714 | + errno = EINVAL; | |
| 715 | + return NULL; | |
| 716 | + } | |
| 717 | + | |
| 718 | + dest_size = sizeof(char) * _MODBUS_TCP_PI_SERVICE_LENGTH; | |
| 719 | + ret_size = strlcpy(ctx_tcp_pi->service, service, dest_size); | |
| 720 | + if (ret_size == 0) { | |
| 721 | + fprintf(stderr, "The service string is empty\n"); | |
| 722 | + modbus_free(ctx); | |
| 723 | + errno = EINVAL; | |
| 724 | + return NULL; | |
| 725 | + } | |
| 726 | + | |
| 727 | + if (ret_size >= dest_size) { | |
| 728 | + fprintf(stderr, "The service string has been truncated\n"); | |
| 729 | + modbus_free(ctx); | |
| 730 | + errno = EINVAL; | |
| 731 | + return NULL; | |
| 732 | + } | |
| 733 | + | |
| 734 | + return ctx; | |
| 735 | +} | ... | ... |
src/modbus-tcp.h
| ... | ... | @@ -41,4 +41,8 @@ modbus_t* modbus_new_tcp(const char *ip_address, int port); |
| 41 | 41 | int modbus_tcp_listen(modbus_t *ctx, int nb_connection); |
| 42 | 42 | int modbus_tcp_accept(modbus_t *ctx, int *socket); |
| 43 | 43 | |
| 44 | +modbus_t* modbus_new_tcp_pi(const char *node, const char *service); | |
| 45 | +int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection); | |
| 46 | +int modbus_tcp_pi_accept(modbus_t *ctx, int *socket); | |
| 47 | + | |
| 44 | 48 | #endif /* _MODBUS_TCP_H_ */ | ... | ... |
tests/unit-test-client.c
| ... | ... | @@ -26,6 +26,7 @@ |
| 26 | 26 | |
| 27 | 27 | enum { |
| 28 | 28 | TCP, |
| 29 | + TCP_PI, | |
| 29 | 30 | RTU |
| 30 | 31 | }; |
| 31 | 32 | |
| ... | ... | @@ -48,10 +49,12 @@ int main(int argc, char *argv[]) |
| 48 | 49 | if (argc > 1) { |
| 49 | 50 | if (strcmp(argv[1], "tcp") == 0) { |
| 50 | 51 | use_backend = TCP; |
| 52 | + if (strcmp(argv[1], "tcppi") == 0) { | |
| 53 | + use_backend = TCP_PI; | |
| 51 | 54 | } else if (strcmp(argv[1], "rtu") == 0) { |
| 52 | 55 | use_backend = RTU; |
| 53 | 56 | } else { |
| 54 | - printf("Usage:\n %s [tcp|rtu] - Modbus client for unit testing\n\n", argv[0]); | |
| 57 | + printf("Usage:\n %s [tcp|tcppi|rtu] - Modbus client for unit testing\n\n", argv[0]); | |
| 55 | 58 | exit(1); |
| 56 | 59 | } |
| 57 | 60 | } else { |
| ... | ... | @@ -61,7 +64,9 @@ int main(int argc, char *argv[]) |
| 61 | 64 | |
| 62 | 65 | if (use_backend == TCP) { |
| 63 | 66 | ctx = modbus_new_tcp("127.0.0.1", 1502); |
| 64 | - } else { | |
| 67 | + } else if (use_backend == TCP_PI) { | |
| 68 | + ctx = modbus_new_tcp_pi("::1", "1502"); | |
| 69 | + } else | |
| 65 | 70 | ctx = modbus_new_rtu("/dev/ttyUSB1", 115200, 'N', 8, 1); |
| 66 | 71 | } |
| 67 | 72 | if (ctx == NULL) { | ... | ... |
tests/unit-test-server.c
| ... | ... | @@ -26,6 +26,7 @@ |
| 26 | 26 | |
| 27 | 27 | enum { |
| 28 | 28 | TCP, |
| 29 | + TCP_PI, | |
| 29 | 30 | RTU |
| 30 | 31 | }; |
| 31 | 32 | |
| ... | ... | @@ -43,10 +44,12 @@ int main(int argc, char*argv[]) |
| 43 | 44 | if (argc > 1) { |
| 44 | 45 | if (strcmp(argv[1], "tcp") == 0) { |
| 45 | 46 | use_backend = TCP; |
| 47 | + } else if (strcmp(argv[1], "tcppi") == 0) { | |
| 48 | + use_backend = TCP_PI; | |
| 46 | 49 | } else if (strcmp(argv[1], "rtu") == 0) { |
| 47 | 50 | use_backend = RTU; |
| 48 | 51 | } else { |
| 49 | - printf("Usage:\n %s [tcp|rtu] - Modbus server for unit testing\n\n", argv[0]); | |
| 52 | + printf("Usage:\n %s [tcp|tcppi|rtu] - Modbus server for unit testing\n\n", argv[0]); | |
| 50 | 53 | return -1; |
| 51 | 54 | } |
| 52 | 55 | } else { |
| ... | ... | @@ -57,6 +60,9 @@ int main(int argc, char*argv[]) |
| 57 | 60 | if (use_backend == TCP) { |
| 58 | 61 | ctx = modbus_new_tcp("127.0.0.1", 1502); |
| 59 | 62 | query = malloc(MODBUS_TCP_MAX_ADU_LENGTH); |
| 63 | + } else if (use_backend == TCP_PI) { | |
| 64 | + ctx = modbus_new_tcp_pi("::0", "1502"); | |
| 65 | + query = malloc(MODBUS_TCP_MAX_ADU_LENGTH); | |
| 60 | 66 | } else { |
| 61 | 67 | ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1); |
| 62 | 68 | modbus_set_slave(ctx, SERVER_ID); |
| ... | ... | @@ -96,6 +102,9 @@ int main(int argc, char*argv[]) |
| 96 | 102 | if (use_backend == TCP) { |
| 97 | 103 | socket = modbus_tcp_listen(ctx, 1); |
| 98 | 104 | modbus_tcp_accept(ctx, &socket); |
| 105 | + } else if (use_backend == TCP_PI) { | |
| 106 | + socket = modbus_tcp_pi_listen(ctx, 1); | |
| 107 | + modbus_tcp_pi_accept(ctx, &socket); | |
| 99 | 108 | } else { |
| 100 | 109 | rc = modbus_connect(ctx); |
| 101 | 110 | if (rc == -1) { | ... | ... |