Notes on the documentation

This description is only intended for the use of trained specialists in control and automation engineering who are familiar with the applicable national standards.
It is essential that the following notes and explanations are followed when installing and commissioning these components.

The responsible staff must ensure that the application or use of the products described satisfy all the requirements for safety, including all the relevant laws, regulations, guidelines and standards.

Disclaimer

The documentation has been prepared with care. The products described are, however, constantly under development.
For that reason the documentation is not in every case checked for consistency with performance data, standards or other characteristics.
In the event that it contains technical or editorial errors, we retain the right to make alterations at any time and without warning.
No claims for the modification of products that have already been supplied may be made on the basis of the data, diagrams and descriptions in this documentation.

Trademarks

Beckhoff®, TwinCAT®, EtherCAT®, Safety over EtherCAT®, TwinSAFE® and XFC® are registered trademarks of and licensed by Beckhoff Automation GmbH.
Other designations used in this publication may be trademarks whose use by third parties for their own purposes could violate the rights of the owners.

Patent Pending

The TwinCAT Technology is covered, including but not limited to the following patent applications and patents:
EP0851348, US6167425 with corresponding applications or registrations in various other countries.

Copyright

© Beckhoff Automation GmbH & Co. KG, Germany.
The reproduction, distribution and utilization of this document as well as the communication of its contents to others without express authorization are prohibited.
Offenders will be held liable for the payment of damages. All rights reserved in the event of the grant of a patent, utility model or design.


Overview

The TwinCAT TCP/IP Connection Server enables the implementation/realisation of one or several TCP/IP server/clients in the TwinCAT PLC.

Product components

  • TcpIp.Lib (implements basic TCP/IP and UDP functions);
  • TcSocketHelper.Lib (implements TCP/IP helper functions);
  • TcSnmp.Lib (implements SNMPv1 helper functions since v1.0.59);
  • TwinCAT TCP/IP Connection Server (TwinCAT Server);


Introduction

TCP

TCP is a connection-oriented analog protocol that can be compared to a phone connection, where participants have to establish the connection first. The TCP protocol is used in applications where confirmation is required for the data sent by the client or server. Data integrity is managed by the protocol, which requires more resources. The TCP protocol is well suited for sending larger data quantities.

TCP is a stream-oriented transport protocol, i.e. it transports a data stream without defined start and end. No information about length, start and end of a message is transferred. For the transmitter this is not a problem since he knows how many data bytes are transmitted. However, the receiver is unable to detect where a message ends within the data stream and where the next data stream starts. A read call on the receiver side only supplies the data currently in the receive buffer (this may be less or more than the data block sent by the other device).

The transmitter has to specify a message structure that is known to the receiver and can be interpreted. In simple cases the message structure may consist of the data and a final control character (e.g. carriage return). The final control character indicates the end of a message.

The message structure for transferring binary data with variable length is often specified as follows: The first data bytes contain a special control character (start delimiter) and the data length of the subsequent data. This enables the receiver to detect the start and end of the message.

A minimum TCP/IP client implementation within the PLC requires the following function blocks:

  • An instance of the FB_SocketConnect and FB_SocketClose function blocks for establishing and closing the connection to the remote server (Hint: FB_ClientServerConnection encapsulates the functionality of both function blocks in one function block);
  • An instance of the FB_SocketSend and/or FB_SocketReceive function block for the data exchange with the remote server;

A minimum TCP/IP server implementation within the PLC requires the following function blocks:

  • An instance of the FB_SocketListen function block for opening the listener socket. An instance of the FB_SocketAccept and FB_SocketClose function blocks for establishing and closing the connection(s) to the remote clients (Hint: FB_ServerClientConnection encapsulates the functionality of all three function block in one function block);
  • An instance of the FB_SocketSend and/or FB_SocketReceive function block for the data exchange with the remote clients;

An instance of the FB_SocketCloseAll function block is required in each PLC runtime system in which a socket is opened.

The instances of the FB_SocketAccept and FB_SocketReceive function blocks are called cyclically (polling), all others are called as required.

UDP

UDP is a connection-less protocol. Data are sent between devices without an explicit connection. The UDP protocol is well suited for sending small data quantities. An UDP application can be both a client or a server. The UDP protocol does not guarantee that data that were sent actually reach the target (no confirmation for the packets received is sent). The individual data packets may arrive in a different order or may be lost.

UDP is a packet-oriented/message-oriented transport protocol, i.e. the sent data block is received on the receiver side as a complete data block.

The following function blocks are required for a minimum UDP server/client implementation:

  • An instance of the FB_SocketUdpCreate and FB_SocketClose function blocks for opening and closing an UDP socket;
  • An instance of the FB_SocketUdpSendTo and/or FB_SocketUdpReceiveFrom function blocks for the data exchange with other devices;
  • In each PLC runtime system in which a UDP socket is opened an instance of the FB_SocketCloseAll function block is required;

The instances of the FB_SocketUdpReceiveFrom function block are called cyclically (polling), all others are called as required.

Further information can be found on the following documentation pages.

Glossary

Term | Description
TwinCAT TCP/IP Connection Server | A TwinCAT Server that enables opening, closing, sending and receiving of data via the Windows sockets.
Remote-Client | A client on a remote computer (from a server point of view) with which the server wants to communicate.
Remote-Server | A server on a remote computer (from a client point of view) with which the client wants to communicate.
Local-Client | A client on the local computer.
Local-Server | A server on the local computer.
Connection handle (socket handle) | A PLC variable of type T_HSOCKET


Installation

This part of the documentation gives a step-by-step explaination of the TwinCAT TCP/IP Server setup process for Windows XP based operating systems. The following topics are part of this document:

  • Downloading the setup file
  • Starting the installation

Downloading the setup file

Like many other TwinCAT Supplement products, TCP/IP Server is available for download via the Beckhoff FTP-Server. The download represents the most current version, which can be licensed either as a 30-Day Demo or as a full version. To download the setup file, please perform the following steps:

  • Select the TwinCAT TCP/IP Server from the BECKHOFF website.
  • (Optional) Transfer the downloaded file to the TwinCAT runtime system, where you would like to install the Supplement.

Starting the installation

To install the Supplement, please perform the following steps:

  • Double-click the downloaded setup file "TcpIpServer.exe". Please note: Under Windows 7 32-bit/64-bit, please start the installation with "Run as Administrator" by right-clicking the setup file and selecting the corresponding option in the context menu.
  • Select an installation language
  • Click on "Next" and accept the license agreement

  • Enter your user information. All fields are mandatory. If you would like to install a 30-Day demo, please enter "DEMO" as a license key.

  • Click on "Install" to start installation

  • At the end of the setup process, please restart your computer


System requirements

The following system requirements need to be met for the TwinCAT TCP/IP Server to run properly.

Windows XP-platform

Currently all Beckhoff Embedded-PC/IPC devices running one of the following operating systems are supported: Windows XP, Windows XP Embedded, Windows Embedded Standard 2009, Windows Vista, Windows 7. Any difference between these two systems will be described in the corresponding installation manual. Additionally, at least TwinCAT2 CP needs to be installed on the TCP/IP Server and needs to be either in Config- or Run-Mode during operation.

Windows CE-platform

All Beckhoff Embedded-PC/IPC devices running one of the following operating systems are supported: Windows CE5, Windows CE6, Windows CE7.


Installation Windows CE

This part of the documentation describes, how you can install the supplement TwinCAT TCP/IP Server on a Beckhoff Embedded PC Controller based on Windows CE, for example CX1000, CX1020, CX9000, CX9001, CX9010, CP62xx, C69xx, ...

The setup process consists of four steps:

  • Downloading the setup file
  • Installation on a host computer
  • Transfering the setup file to the Windows CE device
  • Executing the setup on the Windows CE device

Downloading the setup file

Like many other TwinCAT Supplement products, TCP/IP Server for CE is available for download via the Beckhoff FTP-Server. The download represents the most current version. To download the setup file, please perform the following step:

  • Select the TwinCAT TCP/IP Server CE from the BECKHOFF website.

Installation on a host computer

To get the installation files for Windows CE, you first need to install the downloaded setup file on a host computer, which can be any Windows XP based system. Please perform the following steps:

  • Double-click the downloaded file "TcTCPIPSvrCE.exe"
  • Select an installation language
  • Click on "Next" and accept the license agreement

  • Enter your user information. All fields are mandatory. Please note that TCP/IP Server for CE is currently not available as a demo version. Therefore you need a valid license key to continue installation.

  • Click on "Install"

After installation, the setup files for Windows CE can be found in the directory ".\TwinCAT\CE". This directory contains setup files for the following CE platforms:

  • TCPIP\Install\TcTCPIPSvrCe.I586.cab: TCP/IP Server for x86 based CPUs (like CX10xx, CP62xx, C69xx, ...)
  • TCPIP\Install\TcTCPIPSvrCe.ARMV4I.cab: TCP/IP Server for ARM based CPUs (like CX9001, CX9010, CP6608, ...)

Transfering the setup file to the Windows CE device

Transfer the corresponding setup file to you CE device. This can be done in the following ways:

  • via a Shared Folder
  • via the integrated FTP-Server
  • via ActiveSync
  • via a CF card

For more information, please consult the "Windows CE" section in our Infosys documentation system.

Executing the setup on the Windows CE device

Now the transfered setup file "TcTCPIPSvrCe.xxxx.CAB" needs to be executed on the CE device. Please perform the following steps:

  • Navigate to the directory where you transfered the setup file to

  • Double-click the CAB file. Should you get a message box stating that this program is not compatible with the current operating system, please re-check if you used the correct CAB-file (ARM, I586) for your IPC/Embedded-PC.
  • If you are sure that the CAB-file corresponds to the Embedded-PC/IPC, please acknowledge this message box with "Yes".

  • Confirm the destination directory "\Hard Disk\System" with "Ok".

  • To start installation, click on "Ok"

After installation, the setup file will be automatically deleted. Please note that the TCP/IP Server will be available after a restart of your CE device.


Overview

The TcpIp.Lib function blocks can be used to realise client or server applications in the TwinCAT PLC. These can exchange data with other communication devices either via User Datagram Protocol (UDP) or via Transmission Control Protocol (TCP).

System requirements:

  • Development eniviroment:
    • NT4, W2K, XP, XPe;
    • TwinCAT System version 2.9 or higher;
    • TwinCAT installation level: TwinCAT PLC or higher;
  • Target system:
    • TwinCAT PLC runtime system version 2.8 or higher;
    • PC or CX (x86)
      • TwinCAT TCP/IP Connection Server v1.0.0.0 or higher;
      • NT4, W2K, XP, XPe, CE (image v1.75 or higher);
    • CX (ARM)
      • TwinCAT TCP/IP Connection Server v1.0.0.44 or higher;
      • CE (image v2.13 or higher);

Installation:

The PLC library is supplied with the TwinCAT TCP/IP Connection Server and copied into folder ...\TwinCAT\PLC\Lib during installation.


FB_SocketConnect

TS6310 | TwinCAT PLC Lib: TCP/IP

FUNCTION_BLOCK FB_SocketConnect

Using the function block FB_SocketConnect, a local client can establish a new TCP/IP connection to a remote server via the TwinCAT TCP/IP Connection Server. If successful, a new socket is opened, and the associated connection handle is returned at the hSocket output. The connection handle is required by the function blocks FB_SocketSend and FB_SocketReceive, for example, in order to exchange data with a remote server. If a connection is no longer required, it can be closed with the function block FB_SocketClose. Several clients can establish a connection with the remote server at the same time. For each new client, a new socket is opened and a new connection handle is returned. The TwinCAT TCP/IP Connection Server automatically assigns a new IP port number for each client.

VAR_INPUT

VAR_INPUT
        sSrvNetId       : T_AmsNetId := '';
        sRemoteHost     : STRING(15);
        nRemotePort     : UDINT;
        bExecute                : BOOL;
        tTimeout                : TIME := T#45s;(*!!!*)
END_VAR

sSrvNetId: String containing the network address of the TwinCAT TCP/IP Connection Server. For the local computer (default) an empty string may be specified.

sRemoteHost: IP address (Ipv4) of the remote server as a string (e.g. '172.33.5.1'). An empty string can be entered on the local computer for a server.

nRemotePort: IP port number of the remote server (e.g. 200).

bExecute: The block is activated by a rising edge at this input.

tTimeout: Maximum time allowed for the execution of the function block.

Important note:

The tTimeout value should not be set too low, since timeout periods of > 30s may occur in the event of a network interruption. If the value is too low, command execution would be interrupted prematurely, and ADS error code 1861 (timeout elapsed) would be returned instead of the Winsocket error WSAETIMEDOUT.

VAR_OUTPUT

VAR_OUTPUT
        bBusy           : BOOL;
        bError          : BOOL;
        nErrId          : UDINT;
        hSocket         : T_HSOCKET;
END_VAR

bBusy: When the function block is activated this output is set. It remains set until and acknowledgement is received.

bError: If an error should occur during the transfer of the command, then this output is set once the bBusy output was reset.

nErrId : If the bError output is set, this parameter returns the TwinCAT TCP/IP Connection Server error number.

hSocket: TCP/IP connection handle for the newly opened local client socket.

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


FB_SocketClose

TS6310 | TwinCAT PLC Lib: TCP/IP

FUNCTION_BLOCK FB_SocketClose

The function block FB_SocketClose can be used to close an open TCP/IP or UDP socket.

TCP/IP: The listener socket is opened with the function block FB_SocketListen, a local client socket with FB_SocketConnect and a remote client socket with FB_SocketAccept.

UDP: The UDP socket is opened with the function block: FB_SocketUdpCreate.

VAR_INPUT

VAR_INPUT
        sSrvNetId       : T_AmsNetId := '';
        hSocket                 : T_HSOCKET;
        bExecute        : BOOL;
        tTimeout        : TIME := T#5s;
END_VAR

sSrvNetId: String containing the network address of the TwinCAT TCP/IP Connection Server. For the local computer (default) an empty string may be specified.

hSocket: TCP/IP: Connection handle of the listener, remote or local client socket to be closed. UDP: Connection handle of the UDP socket.

bExecute: The block is activated by a rising edge at this input.

tTimeout: Maximum time allowed for the execution of the function block.

VAR_OUTPUT

VAR_OUTPUT
        bBusy           : BOOL;
        bError          : BOOL;
        nErrId          : UDINT;
END_VAR

bBusy: When the function block is activated this output is set. It remains set until and acknowledgement is received.

bError: If an error should occur during the transfer of the command, then this output is set once the bBusy output was reset.

nErrId : If the bError output is set, this parameter returns the TwinCAT TCP/IP Connection Server error number.

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


FB_SocketCloseAll

TS6310 | TwinCAT PLC Lib: TCP/IP

FUNCTION_BLOCK FB_SocketCloseAll

If TwinCAT is restarted or stopped, the TwinCAT TCP/IP Connection Server is also stopped. Any open sockets (TCP/IP and UDP connection handles) are closed automatically. The PLC program is reset after a "PLC reset", a "Rebuild all..." or a new "Download", and the information about already opened sockets (connection handles) is no longer available in the PLC. Any open connections can then no longer be closed properly.

The function block FB_SocketCloseAll can be used to close all connection handles (TCP/IP and UDP sockets) that were opened by a PLC runtime system. This means that, if FB_SocketCloseAll is called in one of the tasks of the first runtime systems (port 801), all sockets that were opened in the first runtime system are closed. In each PLC runtime system that uses the socket function blocks, an instance of FB_SocketCloseAll should be called during the PLC start (see below).

VAR_INPUT

VAR_INPUT
        sSrvNetId       : T_AmsNetId := '';
        bExecute                : BOOL;
        tTimeout                : TIME := T#5s;
END_VAR

sSrvNetId: String containing the network address of the TwinCAT TCP/IP Connection Server. For the local computer (default) an empty string may be specified.

bExecute: The block is activated by a rising edge at this input.

tTimeout: Maximum time allowed for the execution of the function block.

VAR_OUTPUT

VAR_OUTPUT
        bBusy           :BOOL;
        bError          :BOOL;
        nErrId          :UDINT;
END_VAR

bBusy: When the function block is activated this output is set. It remains set until and acknowledgement is received.

bError: If an error should occur during the transfer of the command, then this output is set once the bBusy output was reset.

nErrId : If the bError output is set, this parameter returns the TwinCAT TCP/IP Connection Server error number.

Example of an implementation in ST:

The following program code is used to properly close the connection handles (sockets) that were open before a "PLC reset" or "Download" before a PLC restart.

PROGRAM MAIN
VAR
    fbSocketCloseAll 	: FB_SocketCloseAll;
    bCloseAll 		: BOOL := TRUE;
END_VAR
IF bCloseAll THEN (*On PLC reset or program download close all old connections *)
    bCloseAll := FALSE;
    fbSocketCloseAll( sSrvNetId:= '', bExecute:= TRUE, tTimeout:= T#10s );
ELSE
    fbSocketCloseAll( bExecute:= FALSE );
END_IF

IF NOT fbSocketCloseAll.bBusy THEN
(*...
... continue program execution...
...*)
END_IF

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


FB_SocketListen

TS6310 | TwinCAT PLC Lib: TCP/IP

FUNCTION_BLOCK FB_SocketListen

Using the function block FB_SocketListen, a new listener socket can be opened via the TwinCAT TCP/IP Connection Server. Via a listener socket, the TwinCAT TCP/IP Connection Server can 'listen' for incoming connection requests from remote clients. If successful, the associated connection handle is returned at the hListner output. This handle is required by the function block FB_SocketAccept . If a listener socket is no longer required, it can be closed with the function block FB_SocketClose. The listener sockets on an individual computer must have unique IP port numbers.

VAR_INPUT

VAR_INPUT
        sSrvNetId               : T_AmsNetId := '';
        sLocalHost      : STRING(15);
        nLocalPort      : UDINT;
        bExecute                : BOOL;
        tTimeout                : TIME := T#5s;
END_VAR

sSrvNetId: String containing the network address of the TwinCAT TCP/IP Connection Server. For the local computer (default) an empty string may be specified.

sLocalHost: Local server IP address (Ipv4) as a string (e.g. '172.13.15.2'). For a server on the local computer (default), an empty string may be entered.

nLocalPort: Local server IP port (e.g. 200).

bExecute: The block is activated by a rising edge at this input.

tTimeout: Maximum time allowed for the execution of the function block.

VAR_OUTPUT

VAR_OUTPUT
        bBusy           : BOOL;
        bError          : BOOL;
        nErrId          : UDINT;
        hListener               : T_HSOCKET;
END_VAR

bBusy: When the function block is activated this output is set. It remains set until and acknowledgement is received.

bError: If an error should occur during the transfer of the command, then this output is set once the bBusy output was reset.

nErrId : If the bError output is set, this parameter returns the TwinCAT TCP/IP Connection Server error number.

hListener: Connection handle for the new listener socket.

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


FB_SocketAccept

TS6310 | TwinCAT PLC Lib: TCP/IP

FUNCTION_BLOCK FB_SocketAccept

The remote client connection requests arriving at the TwinCAT TCP/IP Connection Server have to be acknowledged (accepted). The function block FB_SocketAccept accepts the incoming remote client connection requests, opens a new remote client socket and returns the associated connection handle. The connection handle is required by the function blocks FB_SocketSend and FB_SocketReceive in order to exchange data with the remote client, for example. All incoming connection requests first have to be accepted. If a connection is no longer required or undesirable, it can be closed with the function block FB_SocketClose.

A server implementation requires at least one instance of this function block. This instance has to be called cyclically (polling) from a PLC task. The block can be activated cyclically via a rising edge at the bExecute input (e.g. every 5 seconds).

If successful, the bAccepted output is set, and the connection handle to the new remote client is returned at the hSocket output. No error is returned if there are no new remote client connection requests. Several remote clients can establish a connection with the server at the same time. The connection handles of several remote clients can be retrieved sequentially via several function block calls. Each connection handle for a remote client can only be retrieved once. It is recommended to keep the connection handles in a list (array). New connections are added to the list, and closed connections must be removed from the list.

VAR_INPUT

VAR_INPUT
        sSrvNetId       : T_AmsNetId := '';
        hListener       : T_HSOCKET;
        bExecute                : BOOL;
        tTimeout                : TIME := T#5s;
END_VAR

sSrvNetId: String containing the network address of the TwinCAT TCP/IP Connection Server. For the local computer (default) an empty string may be specified.

hListener: Connection handle of the listener sockets. This handle must first be requested via the function block FB_SocketListen.

bExecute: The block is activated by a rising edge at this input.

tTimeout: Maximum time allowed for the execution of the function block.

VAR_OUTPUT

VAR_OUTPUT
        bAccepted               : BOOL;
        bBusy           : BOOL;
        bError          : BOOL;
        nErrId          : UDINT;
        hSocket         : T_HSOCKET;
END_VAR

bAccepted: This output is set if a new connection to a remote client was established.

bBusy: When the function block is activated this output is set. It remains set until and acknowledgement is received.

bError: If an error should occur during the transfer of the command, then this output is set once the bBusy output was reset.

nErrId : If the bError output is set, this parameter returns the TwinCAT TCP/IP Connection Server error number.

hSocket: Connection handle of a new remote client.

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


FB_SocketSend

TS6310 | TwinCAT PLC Lib: TCP/IP

FUNCTION_BLOCK FB_SocketSend

Using the function block FB_SocketSend, data can be sent to a remote client or remote server via the TwinCAT TCP/IP Connection Server. A remote client connection will first have to be established via the function block FB_SocketAccept, or a remote server connection via the function block FB_SocketConnect.

VAR_INPUT

VAR_INPUT
        sSrvNetId       : T_AmsNetId := '';
        hSocket         : T_HSOCKET;
        cbLen           : UDINT;
        pSrc            : DWORD;
        bExecute                : BOOL;
        tTimeout                : TIME := T#5s;
END_VAR

sSrvNetId: String containing the network address of the TwinCAT TCP/IP Connection Server. For the local computer (default) an empty string may be specified.

hSocket: Connection handle of the communication partner to which data are to be sent.

cbLen: Number (in bytes) of data to be sent.

pSrc: Address (pointer) of the send buffer.

bExecute: The block is activated by a rising edge at this input.

tTimeout: Maximum time allowed for the execution of the function block.

Important note:

If the send buffer of the socket is full, for example because the remote communication partner receives the transmitted data not quickly enough or large quantities of data are transmitted, the FB_SocketSend function block will return ADS timeout error 1861 after the tTimeout time. In this case, the value of the tTimeout input variable has to be increased accordingly.

VAR_OUTPUT

VAR_OUTPUT
        bBusy           : BOOL;
        bError          : BOOL;
        nErrId          : UDINT;
END_VAR

bBusy: When the function block is activated this output is set. It remains set until and acknowledgement is received.

bError: If an error should occur during the transfer of the command, then this output is set once the bBusy output was reset.

nErrId : If the bError output is set, this parameter returns the TwinCAT TCP/IP Connection Server error number.

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


FB_SocketReceive

TS6310 | TwinCAT PLC Lib: TCP/IP

FUNCTION_BLOCK FB_SocketReceive

Using the function block FB_SocketReceive, data from a remote client or remote server can be received via the TwinCAT TCP/IP Connection Server. A remote client connection will first have to be established via the function block FB_SocketAccept, and a remote server connection via the function block FB_SocketConnect. The data can be received or sent in fragmented form (i.e. in several packets) within a TCP/IP network. It is therefore possible that not all data may be received with a single call of the FB_SocketReceive instance. For this reason, the instance has to be called cyclically (polling) within the PLC task, until all required data have been received. During this process, an rising edge is generated at the bExecute input, e.g. every 100ms. If successful, the data received last are copied into the receive buffer. The nRecBytes output returns the number of the last successfully received data bytes. If no new data could be read during the last call, the function block returns no error and nRecBytes == zero.

In a simple protocol for receiving, for example, a zero-terminated string on a remote server, the function block FB_SocketReceive, for example, will have to be called repeatedly until the zero termination was detected in the data received.

Important note:

If the remote device was disconnected from the TCP/IP network (on the remote side only) while the local device is still connected to the TCP/IP network, the FB_SocketReceive function block returns no error and no data. The socket is still open, but no data are received. In this case, the application may possibly wait for remaining data bytes indefinitely. It is recommended to implement timeout monitoring in the PLC application. If not all data were received after a certain period, e.g. 10 seconds, the connection has to be closed and reinitialised.

VAR_INPUT

VAR_INPUT
        sSrvNetId       : T_AmsNetId := '';
        hSocket         : T_HSOCKET;
        cbLen           : UDINT;
        pDest           : DWORD;
        bExecute                : BOOL;
        tTimeout                : TIME := T#5s;
END_VAR

sSrvNetId: String containing the network address of the TwinCAT TCP/IP Connection Server. For the local computer (default) an empty string may be specified.

hSocket: Connection handle of the communication partner from which data are to be received.

cbLen: Maximum available buffer size in bytes for the data to be read.

pDest: Address (pointer) of the receive buffer.

bExecute: The block is activated by a rising edge at this input.

tTimeout: Maximum time allowed for the execution of the function block.

VAR_OUTPUT

VAR_OUTPUT
        bBusy           : BOOL;
        bError          : BOOL;
        nErrId          : UDINT;
        nRecBytes       : UDINT;
END_VAR

bBusy: When the function block is activated this output is set. It remains set until and acknowledgement is received.

bError: If an error should occur during the transfer of the command, then this output is set once the bBusy output was reset.

nErrId : If the bError output is set, this parameter returns the TwinCAT TCP/IP Connection Server error number.

nRecBytes: Number of the last successfully receive data bytes.

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


FB_SocketUdpCreate

TS6310 | TwinCAT PLC Lib: TCP/IP

FUNCTION_BLOCK FB_SocketUdpCreate

Available in the product version: TwinCAT TCP/IP Connection Server v1,0,0,31 or higher

The function block FB_SocketUdpCreate can be used to open a client/server socket for the User Datagram Protocol (UDP). If successful, a new socket is opened, and the associated socket handle is returned at the hSocket output. The handle is required by the function blocks FB_SocketUdpSendTo and FB_SocketUdpReceiveFrom in order to exchange data with a remote server, for example. If a UDP socket is no longer required, it can be closed with the function block FB_SocketClose. The port address nLocalHost is internally reserved by the TCP/IP Connection Server for the UDP protocol (a "bind" is carried out). Several network adapters may exist in a PC. The input parameter sLocalHost determines the network adapter to be used. If the sLocalHost input variable is ignored (empty string), the TCP/IP Connection Server uses the default network adapter. This is usually the first network adapter from the list of the network adapters in the system control.

Important notes:

  • If an empty string was specified for sLocalHost when FB_SocketUdpCreate was called and the PC was disconnected from the network, the system will open a new socket under the software loopback IP address: '127.0.0.1'.
  • If two or more network adapters are installed in the PC and an empty string was specified as sLocalHost, and the default network adapter was then disconnected from the network, the new socket will be opened under the IP address of the second network adapter.
  • In order to prevent the sockets from being opened under a different IP address, you can specify the sLocalHost address explicitly or check the returned address in the handle variable (hSocket), close the socket and re-open it.

VAR_INPUT

VAR_INPUT
        sSrvNetId       : T_AmsNetId := '';
        sLocalHost      : STRING(15);
        nLocalPort      : UDINT;
        bExecute                : BOOL;
        tTimeout                : TIME := T#5s;
END_VAR

sSrvNetId: String containing the network address of the TwinCAT TCP/IP Connection Server. For the local computer (default) an empty string may be specified.

sLocalHost: The local IP address (Ipv4) of the UDP client/server socket as a string (e.g. '172.33.5.1'). An empty string may be specified for the default network adapter

nLocalPort: The local IP port number of the UDP client/server socket (e.g. 200).

bExecute: The block is activated by a rising edge at this input.

tTimeout: Maximum time allowed for the execution of the function block.

VAR_OUTPUT

VAR_OUTPUT
        bBusy           : BOOL;
        bError          : BOOL;
        nErrId          : UDINT;
        hSocket         : T_HSOCKET;
END_VAR

bBusy: When the function block is activated this output is set. It remains set until and acknowledgement is received.

bError: If an error should occur during the transfer of the command, then this output is set once the bBusy output was reset.

nErrId : If the bError output is set, this parameter returns the TwinCAT TCP/IP Connection Server error number.

hSocket: The handle of the newly opened UDP client/server socket.

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib (v1.0.4 or higher) ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


FB_SocketUdpSendTo

TS6310 | TwinCAT PLC Lib: TCP/IP

FUNCTION_BLOCK FB_SocketUdpSendTo

Available in the product version: TwinCAT TCP/IP Connection Server v1,0,0,31 or higher

The function block FB_SocketUdpSendTo can be used to send UDP data to a remote device via the TwinCAT TCP/IP Connection Server. The UDP socket must first be opened with the function block FB_SocketUdpCreate.

VAR_INPUT

VAR_INPUT
        sSrvNetId       : T_AmsNetId := '';
        hSocket         : T_HSOCKET;
        sRemoteHost     : STRING(15);
        nRemotePort     : UDINT;
        cbLen           : UDINT;
        pSrc            : DWORD;
        bExecute                : BOOL;
        tTimeout                : TIME := T#5s;
END_VAR

sSrvNetId: String containing the network address of the TwinCAT TCP/IP Connection Server. For the local computer (default) an empty string may be specified.

hSocket: The handle of an open UDP socket.

sRemoteHost: IP address (Ipv4) of the remote device to which data are to be sent as a string (e.g. '172.33.5.1'). An empty string can be entered on the local computer for a device

nRemotePort: IP port number of the remote device to which data are to be sent (e.g. 200).

cbLen: Number (in bytes) of data to be sent. The maximum number of data bytes to be sent is limited to 8192 bytes (constant declaration TCPADS_MAXUDP_BUFFSIZE in the library to save memory resources).

pSrc: Address (pointer) of the send buffer.

bExecute: The block is activated by a rising edge at this input.

tTimeout: Maximum time allowed for the execution of the function block.

Hint:

Available in the product version: TwinCAT TCP/IP Connection Server v1.0.50 or higher: Possibility to increase the maximum number of data bytes to be received (only if absolute required).

1) Redefine in your PLC project the global constant (in our example we want to increase the maximum number of data bytes to 32000 bytes):

VAR_GLOBAL CONSTANT
        TCPADS_MAXUDP_BUFFSIZE : UDINT :=32000;
END_VAR

2) Activate the "Replace constants" option in the TwinCAT PLC Control->"Project->Options...->Build" dialog window.

3) Rebuild your project.

VAR_OUTPUT

VAR_OUTPUT
        bBusy           : BOOL;
        bError          : BOOL;
        nErrId          : UDINT;
END_VAR

bBusy: When the function block is activated this output is set. It remains set until and acknowledgement is received.

bError: If an error should occur during the transfer of the command, then this output is set once the bBusy output was reset.

nErrId : If the bError output is set, this parameter returns the TwinCAT TCP/IP Connection Server error number.

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib (v1.0.4 or higher) ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


FB_SocketUdpReceiveFrom

TS6310 | TwinCAT PLC Lib: TCP/IP

FUNCTION_BLOCK FB_SocketUdpReceiveFrom

Available in the product version: TwinCAT TCP/IP Connection Server v1,0,0,31 or higher

Using the function block FB_SocketUdpReceiveFrom, data from an open UDP socket can be received via the TwinCAT TCP/IP Connection Server. The UDP socket must first be opened with the function block FB_SocketUdpCreate. The instance of the FB_SocketUdpReceive function block has to be called cyclically (polling) within the PLC task. During this process, an rising edge is generated at the bExecute input, e.g. every 100ms. If successful, the data received last are copied into the receive buffer. The nRecBytes output returns the number of the last successfully received data bytes. If no new data could be read during the last call, the function block returns no error and nRecBytes == zero.

VAR_INPUT

VAR_INPUT
        sSrvNetId       : T_AmsNetId := '';
        hSocket         : T_HSOCKET;
        cbLen           : UDINT;
        pDest           : DWORD;
        bExecute                : BOOL;
        tTimeout                : TIME := T#5s;
END_VAR

sSrvNetId: String containing the network address of the TwinCAT TCP/IP Connection Server. For the local computer (default) an empty string may be specified.

hSocket: Handle of an open UDP socket whose data are to be received.

cbLen: Maximum available buffer size in bytes for the data to be read. The maximum number of data bytes to be received is limited to 8192 bytes (constant declaration TCPADS_MAXUDP_BUFFSIZE in the library to save memory resources).

pDest: Address (pointer) of the receive buffer.

bExecute: The block is activated by a rising edge at this input.

tTimeout: Maximum time allowed for the execution of the function block.

Hint:

Available in the product version: TwinCAT TCP/IP Connection Server v1.0.50 or higher: Possibility to increase the maximum number of data bytes to be received (only if absolute required).

1) Redefine in your PLC project the global constant (in our example we want to increase the maximum number of data bytes to 32000 bytes):

VAR_GLOBAL CONSTANT
        TCPADS_MAXUDP_BUFFSIZE : UDINT :=32000;
END_VAR

2) Activate the "Replace constants" option in the TwinCAT PLC Control->"Project->Options...->Build" dialog window.

3) Rebuild your project.

VAR_OUTPUT

VAR_OUTPUT
        bBusy           : BOOL;
        bError          : BOOL;
        nErrId          : UDINT;
        sRemoteHost     : STRING(15);
        nRemotePort     : UDINT;
        nRecBytes       : UDINT;
END_VAR

bBusy: When the function block is activated this output is set. It remains set until and acknowledgement is received.

bError: If an error should occur during the transfer of the command, then this output is set once the bBusy output was reset.

nErrId : If the bError output is set, this parameter returns the TwinCAT TCP/IP Connection Server error number.

sRemoteHost: If successful, IP address (Ipv4) of the remote device whose data were received.

nRemotePort: If successful, IP port number of the remote device whose data were received (e.g. 200).

nRecBytes: Number of the last successfully receive data bytes.

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib (v1.0.4 or higher) ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


FB_SocketUdpAddMulticastAddress

TF6310 TC3 TCP/IP

FUNCTION_BLOCK FB_SocketUdpAddMulticastAddress

Available since product version: TwinCAT TCP/IP Connection Server 1.0.64 or higher

Binds the Server to a Multicast IP address so that Multicast UDP packets can be received. This function blocks requires a previously established UDP socket handle, which can be requested using the function block FB_SocketUdpCreate.

VAR_INPUT

VAR_INPUT
    sSrvNetId		: T_AmsNetId := '';
    hSocket		: T_HSOCKET;
    sMulticastAddr	: STRING(15);
    bExecute		: BOOL;
    tTimeout		: TIME := T#5s;
END_VAR

sSrvNetId: String containing the network address of the TwinCAT TCP/IP Connection Server. For the local computer (default) an empty string may be specified.

hSocket: Connection handle of the listener sockets. This handle must first be requested via the function block FB_SocketUdpCreate.

sMulticastAddr: Multicast address to bind to.

bExecute: The block is activated by a rising edge at this input.

tTimeout: Maximum time allowed for the execution of the function block.

VAR_OUTPUT

VAR_OUTPUT
    bBusy 		: BOOL;
    bError 		: BOOL;
    nErrId		: UDINT;
END_VAR

bBusy: When the function block is activated this output is set. It remains set until and acknowledgement is received.

bError: If an error should occur during the transfer of the command, then this output is set once the bBusy output was reset.

nErrId : If the bError output is set, this parameter returns the TwinCAT TCP/IP Connection Server error number.

Entwicklungsumgebung | Zielplattform | Einzubindende SPS Bibliotheken
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.lib
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


FB_SocketUdpDropMulticastAddress

TF6310 TC3 TCP/IP

FUNCTION_BLOCK FB_SocketUdpDropMulticastAddress

Available since product version: TwinCAT TCP/IP Connection Server 1.0.64 or higher

Removes the binding to a Multicast IP address which has previously been added via the function block FB_SocketUdpAddMulticastAddress.

VAR_INPUT

VAR_INPUT
    sSrvNetId		: T_AmsNetId := '';
    hSocket		: T_HSOCKET;
    sMulticastAddr	: STRING(15);
    bExecute		: BOOL;
    tTimeout		: TIME := T#5s;
END_VAR

sSrvNetId: String containing the network address of the TwinCAT TCP/IP Connection Server. For the local computer (default) an empty string may be specified.

hSocket: Connection handle of the listener sockets. This handle must first be requested via the function block FB_SocketUdpCreate.

sMulticastAddr: Multicast address for which the binding should be removed.

bExecute: The block is activated by a rising edge at this input.

tTimeout: Maximum time allowed for the execution of the function block.

VAR_OUTPUT

VAR_OUTPUT
    bBusy 		: BOOL;
    bError 		: BOOL;
    nErrId		: UDINT;
END_VAR

bBusy: When the function block is activated this output is set. It remains set until and acknowledgement is received.

bError: If an error should occur during the transfer of the command, then this output is set once the bBusy output was reset.

nErrId : If the bError output is set, this parameter returns the TwinCAT TCP/IP Connection Server error number.

Entwicklungsumgebung | Zielplattform | Einzubindende SPS Bibliotheken
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.lib
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


F_GetVersionTcpIp

TS6310 | TwinCAT PLC Lib: TCP/IP

FUNCTION F_GetVersionTcpIp

This function can be used to read PLC library version information.

FUNCTION F_GetVersionTcpIp : UINT

VAR_INPUT
        nVersionElement : INT;
END_VAR

nVersionElement : Version element to be read. Possible parameters:

  • 1 : major number;
  • 2 : minor number;
  • 3 : revision number;
Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


TYPE ST_SockAddr

Structure with address information for an open socket.

TYPE ST_SockAddr : (* Local or remote endpoint address *)
STRUCT
        nPort   : UDINT;        (* Internet Protocol (IP) port. *)
 sAddr   : STRING(15);   (* String containing an (Ipv4) Internet Protocol dotted address. *)
END_STRUCT
END_TYPE

nPort: Internet Protocol (IP) port;

sAddr: Internet protocol address (Ipv4) separated by dots as a string, e.g. "172.34.12.3";

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


TYPE T_HSOCKET

Variables of this type represent a connection handle or a handle of an open socket. Via this handle, data can be sent to or received from a socket. The handle can be used to close an open socket.

TYPE T_HSOCKET
STRUCT
        handle          : UDINT;
        localAddr       : ST_SockAddr; (* Local address *)
        remoteAddr      : ST_SockAddr; (* Remote endpoint address *)
END_STRUCT
END_TYPE

handle: Internal TwinCAT TCP/IP Connection Server socket handle;

localAddr: Local socket address;

remoteAddr: Remote socket address;

The following sockets can be opened and closed via the TwinCAT TCP/IP Connection Server: listener socket, remote client socket, or local client socket. Depending on which of these sockets was opened by the TwinCAT TCP/IP Connection Server, suitable address information is entered into the localAddr and remoteAddr variables.

Connection handle on the server side

  • The function block FB_SocketListen opens a listener socket and returns the connection handle of the listener socket;
  • The connection handle of the listener sockets is transferred to the function block FB_SocketAccept. FB_SocketAccept will then return the connection handles of the remote clients;
  • The function block FB_SocketAccept returns a new connection handle for each connected remote client;
  • The connection handle is then transferred to the function blocks FB_SocketSend and/or FB_SocketReceive, in order to be able to exchange data with the remote clients;
  • A connection handle of a remote client that is not desirable or no longer required is transferred to the function block FB_SocketClose, which closes the remote client socket;
  • A listener socket connection handle that is no longer required is also transferred to the function block FB_SocketClose, which closes the listener socket;

Connection handle on the client side

  • The function block FB_SocketConnect returns the connection handle of a local client socket;
  • The connection handle is then transferred to the function blocks FB_SocketSend and FB_SocketReceive, in order to be able to exchange data with a remote server;
  • The same connection handle is then transferred to the function block FB_SocketClose, in order to close a connection that is no longer required.

The function block FB_SocketCloseAll can be used to close all connection handles (sockets) that were opened by a PLC runtime system. This means that, if FB_SocketCloseAll is called in one of the tasks of the first runtime systems (port 801), all sockets that were opened in the first runtime system are closed.

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


TYPE E_WinsockError

TYPE E_WinsockError :
  (
        WSOK,
        WSAEINTR                        :=      10004   ,(* A blocking operation was interrupted by a call to WSACancelBlockingCall. *)
        WSAEBADF                        :=      10009   ,(* The file handle supplied is not valid. *)
        WSAEACCES                       :=      10013   ,(* An attempt was made to access a socket in a way forbidden by its access permissions. *)
        WSAEFAULT                       :=      10014   ,(* The system detected an invalid pointer address in attempting to use a pointer argument in a call. *)
        WSAEINVAL                       :=      10022   ,(* An invalid argument was supplied. *)
        WSAEMFILE                       :=      10024   ,(* Too many open sockets. *)
        WSAEWOULDBLOCK          :=      10035   ,(* A non-blocking socket operation could not be completed immediately. *)
        WSAEINPROGRESS          :=      10036   ,(* A blocking operation is currently executing. *)
        WSAEALREADY             :=      10037   ,(* An operation was attempted on a non-blocking socket that already had an operation in progress.  *)
        WSAENOTSOCK             :=      10038   ,(* An operation was attempted on something that is not a socket.  *)
        WSAEDESTADDRREQ         :=      10039   ,(* A required address was omitted from an operation on a socket.  *)
        WSAEMSGSIZE             :=      10040   ,(* A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself. *)
        WSAEPROTOTYPE           :=      10041   ,(* A protocol was specified in the socket function call that does not support the semantics of the socket type requested.  *)
        WSAENOPROTOOPT          :=      10042   ,(* An unknown, invalid, or unsupported option or level was specified in a getsockopt or setsockopt call.  *)
        WSAEPROTONOSUPPORT              :=      10043   ,(* The requested protocol has not been configured into the system, or no implementation for it exists.  *)
        WSAESOCKTNOSUPPORT              :=      10044   ,(* The support for the specified socket type does not exist in this address family.  *)
        WSAEOPNOTSUPP           :=      10045   ,(* The attempted operation is not supported for the type of object referenced.  *)
        WSAEPFNOSUPPORT         :=      10046   ,(* The protocol family has not been configured into the system or no implementation for it exists.  *)
        WSAEAFNOSUPPORT         :=      10047   ,(* An address incompatible with the requested protocol was used.  *)
        WSAEADDRINUSE           :=      10048   ,(* Only one usage of each socket address (protocol/network address/port) is normally permitted.  *)
        WSAEADDRNOTAVAIL                :=      10049   ,(* The requested address is not valid in its context.  *)
        WSAENETDOWN             :=      10050   ,(* A socket operation encountered a dead network.  *)
        WSAENETUNREACH          :=      10051   ,(* A socket operation was attempted to an unreachable network.  *)
        WSAENETRESET            :=      10052   ,(* The connection has been broken due to keep-alive activity detecting a failure while the operation was in progress.  *)
        WSAECONNABORTED         :=      10053   ,(* An established connection was aborted by the software in your host machine.  *)
        WSAECONNRESET           :=      10054   ,(* An existing connection was forcibly closed by the remote host.  *)
        WSAENOBUFS              :=      10055   ,(* An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.  *)
        WSAEISCONN              :=      10056   ,(* A connect request was made on an already connected socket.  *)
        WSAENOTCONN             :=      10057   ,(* A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied.  *)
        WSAESHUTDOWN            :=      10058   ,(* A request to send or receive data was disallowed because the socket had already been shut down in that direction with a previous shutdown call.  *)
        WSAETOOMANYREFS         :=      10059   ,(* Too many references to some kernel object.  *)
        WSAETIMEDOUT            :=      10060   ,(* A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.  *)
        WSAECONNREFUSED         :=      10061   ,(* No connection could be made because the target machine actively refused it.  *)
        WSAELOOP                        :=      10062   ,(* Cannot translate name.  *)
        WSAENAMETOOLONG         :=      10063   ,(* Name component or name was too long.  *)
        WSAEHOSTDOWN            :=      10064   ,(* A socket operation failed because the destination host was down.  *)
        WSAEHOSTUNREACH         :=      10065   ,(* A socket operation was attempted to an unreachable host.  *)
        WSAENOTEMPTY            :=      10066   ,(* Cannot remove a directory that is not empty.  *)
        WSAEPROCLIM             :=      10067   ,(* A Windows Sockets implementation may have a limit on the number of applications that may use it simultaneously.  *)
        WSAEUSERS                       :=      10068   ,(* Ran out of quota.  *)
        WSAEDQUOT                       :=      10069   ,(* Ran out of disk quota.  *)
        WSAESTALE                       :=      10070   ,(* File handle reference is no longer available.  *)
        WSAEREMOTE              :=      10071   ,(* Item is not available locally.  *)
        WSASYSNOTREADY          :=      10091   ,(* WSAStartup cannot function at this time because the underlying system it uses to provide network services is currently unavailable.  *)
        WSAVERNOTSUPPORTED              :=      10092   ,(* The Windows Sockets version requested is not supported.  *)
        WSANOTINITIALISED               :=      10093   ,(* Either the application has not called WSAStartup, or WSAStartup failed.  *)
        WSAEDISCON              :=      10101   ,(* Returned by WSARecv or WSARecvFrom to indicate the remote party has initiated a graceful shutdown sequence.  *)
        WSAENOMORE              :=      10102   ,(* No more results can be returned by WSALookupServiceNext.  *)
        WSAECANCELLED           :=      10103   ,(* A call to WSALookupServiceEnd was made while this call was still processing. The call has been canceled.  *)
        WSAEINVALIDPROCTABLE    :=      10104   ,(* The procedure call table is invalid.  *)
        WSAEINVALIDPROVIDER     :=      10105   ,(* The requested service provider is invalid.  *)
        WSAEPROVIDERFAILEDINIT  :=      10106   ,(* The requested service provider could not be loaded or initialized.  *)
        WSASYSCALLFAILURE               :=      10107   ,(* A system call that should never fail has failed.  *)
        WSASERVICE_NOT_FOU      ND      :=      10108   ,(* No such service is known. The service cannot be found in the specified name space.  *)
        WSATYPE_NOT_FOUND               :=      10109   ,(* The specified class was not found.  *)
        WSA_E_NO_MORE           :=      10110   ,(* No more results can be returned by WSALookupServiceNext.  *)
        WSA_E_CANCELLED         :=      10111   ,(* A call to WSALookupServiceEnd was made while this call was still processing. The call has been canceled.  *)
        WSAEREFUSED             :=      10112   ,(* A database query failed because it was actively refused.  *)
        WSAHOST_NOT_FOUND               :=      11001   ,(* No such host is known.  *)
        WSATRY_AGAIN            :=      11002   ,(* This is usually a temporary error during hostname resolution and means that the local server did not receive a response from an authoritative server.  *)
        WSANO_RECOVERY          :=      11003   ,(* A non-recoverable error occurred during a database lookup.  *)
        WSANO_DATA              :=      11004   (* The requested name is valid and was found in the database, but it does not have the correct associated data being resolved for.  *)
  );
END_TYPE

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


Globale Variablen

TS6310 | TwinCAT PLC Lib: TCP/IP

Global Variables

VAR_GLOBAL CONSTANT

AMSPORT_TCPIPSRV                 :UINT   := 10201;

TCPADS_IGR_CONLIST      :UDINT  :=16#80000001;
TCPADS_IGR_CLOSEBYHDL   :UDINT  :=16#80000002;
TCPADS_IGR_SENDBYHDL    :UDINT  :=16#80000003;
TCPADS_IGR_PEERBYHDL    :UDINT  :=16#80000004;
TCPADS_IGR_RECVBYHDL    :UDINT  :=16#80000005;
TCPADS_IGR_RECVFROMBYHDL        :UDINT  :=16#80000006;(* TCP/IP Connection Server v1,0,0,31 and higher *)
TCPADS_IGR_SENDTOBYHDL  :UDINT  :=16#80000007;(* TCP/IP Connection Server v1,0,0,31 and higher *)

TCPADSCONLST_IOF_CONNECT        :UDINT  :=1;
TCPADSCONLST_IOF_LISTEN         :UDINT  :=2;
TCPADSCONLST_IOF_CLOSEALL       :UDINT  :=3;
TCPADSCONLST_IOF_ACCEPT         :UDINT  :=4;
TCPADSCONLST_IOF_UDPBIND        :UDINT  :=5;(* TCP/IP Connection Server v1,0,0,31 and higher *)

TCPADS_MAXUDP_BUFFSIZE  : UDINT :=1472;(* TCP/IP Connection Server v1,0,0,31 and higher *)

END_VAR

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


Overview

The TcSocketHelper.lib contains usefull TCP/IP helper functions and function blocks.

System requirements:

  • Development eniviroment:
    • TwinCAT System version 2.9 or higher (NT4, W2K, XP, XPe);
    • Installation level: TwinCAT PLC or higher;
  • Target system:
    • TwinCAT PLC runtime system version 2.9 or higher;
    • PC or CX (x86)
      • TwinCAT TCP/IP Connection Server v1.0.0.41 or higher;
      • NT4, W2K, XP, XPe, CE (image v1.75 or higher);
    • CX (ARM)
      • TwinCAT TCP/IP Connection Server v1.0.0.44 or higher;
      • CE (image v2.13 or higher);

Installation:

The PLC library is supplied with the TwinCAT TCP/IP Connection Server and copied into folder ...\TwinCAT\PLC\Lib during installation.


FB_ServerClientConnection

TwinCAT PLC Library: Socket Helper

FUNCION_BLOCK FB_ServerClientConnection

The function block FB_ServerClientConnection can be used to manage (establish or remove) a server connection. FB_ServerClientConnection simplifies the implementation of a server application by encapsulating the functionality of the three function blocks FB_SocketListen, FB_SocketAccept and FB_SocketClose internally. The integrated debugging output of the connection status facilitates troubleshooting in the event of configuration or communication errors. In addition, a minimum server application only requires an instance of the FB_SocketSend function block and/or an instance of the FB_SocketReceive function block.

In the first step a typical server application establishes the connection with the client via the FB_ServerClientConnection function block (more precisely, the server application accepts the incoming connection request). In the next step instances of FB_SocketSend and/or FB_SocketReceive can be used to exchange data with the server. When a connection is closed depends on the requirements of the application.

VAR_IN_OUT

VAR_IN_OUT
    hServer 		: T_HSERVER;
END_VAR

hServer: server handle. This input variable has to be initialised via the F_CreateServerHnd function.

VAR_INPUT

VAR_INPUT
    eMode 		: E_SocketAcceptMode := eACCEPT_ALL;
    sRemoteHost 	: STRING(15) := '';
    nRemotePort 	: UDINT := 0;
    bEnable 		: BOOL;
    tReconnect 	: TIME := T#1s;
END_VAR

eMode: Determines whether all or only certain connections should be accepted.

sRemoteHost: IP address (Ipv4) of the remote client whose connection is to be accepted as a string (e.g.: '172.33.5.1'). For a client on the local computer an empty string may be specified.

nRemotePort: IP port number of the remote client whose connection is to be accepted (e.g. 200).

bEnable: As long as this input is TRUE, the system attempts to establish a connection at regular intervals until a connection was established successfully. Once established, a connection can be closed again with FALSE.

tReconnect: Cycle time used by the function block to try and establish a connection.

VAR_OUTPUT

VAR_OUTPUT
    bBusy  		: BOOL;
    bError  		: BOOL;
    nErrID 		: UDINT;
    hSocket 		: T_HSOCKET;
    eState 		: E_SocketConnectionState := eSOCKET_DISCONNECTED;
END_VAR

bBusy: This output is TRUE as long as the function block is active.

bError: Becomes TRUE as soon as an error has occurred.

nErrId : If the bError output is set, this parameter returns the TwinCAT TCP/IP Connection Server error number.

hSocket: Connection handle for the newly opened remote client socket. If successful, this variable is transferred to the instances of the function blocks FB_SocketSend and/or FB_SocketReceive.

eState: Returns the current connection status.

Example in FBD:

The following example illustrates initialisation of a server handle variable. The server handle is then transferred to three instances of the FB_ServerClientConnection function block.

PROGRAM MAIN
VAR
    hServer 		: T_HSERVER;
    bListen 		: BOOL;

    fbServerConnection1 : FB_ServerClientConnection;
    bConnect1 	: BOOL;
    bBusy1 		: BOOL;
    bError1 		: BOOL;
    nErrID1 		: UDINT;
    hSocket1 	: T_HSOCKET;
    eState1 		: E_SocketConnectionState;

    fbServerConnection2 : FB_ServerClientConnection;
    bConnect2 	: BOOL;
    bBusy2 		: BOOL;
    bError2 		: BOOL;
    nErrID2 		: UDINT;
    hSocket2 	: T_HSOCKET;
    eState2 		: E_SocketConnectionState;

    fbServerConnection3 : FB_ServerClientConnection;
    bConnect3 	: BOOL;
    bBusy3 		: BOOL;
    bError3 		: BOOL;
    nErrID3 		: UDINT;
    hSocket3 	: T_HSOCKET;
    eState3 		: E_SocketConnectionState;
END_VAR

Online View:

The first connection is activated (bConnect1=TRUE), although the connection has not yet been established (passive open).

The second connection has not yet been activated (bConnect2=FALSE) (closed).

The third connection was activated (bConnect3=TRUE), and a connection to the remote client has been established.

Further application examples (including source code) can be found here: Examples

Development Environment | Target System | PLC libraries to include
TwinCAT v2.9.0 Build >= 1030 | PC or CX (x86) | TcSocketHelper.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib; TcUtilities.Lib; TcpIp.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


FB_ClientServerConnection

TwinCAT PLC Library: Socket Helper

FUNCTION_BLOCK FB_ClientServerConnection

The function block FB_ClientServerConnection can be used to manage (establish or remove) a client connection. FB_ClientServerConnection simplifies the implementation of a client application by encapsulating the functionality of the two function blocks FB_SocketConnect and FB_SocketClose internally. The integrated debugging output of the connection status facilitates troubleshooting in the event of configuration or communication errors. In addition, a minimum client application only requires an instance of the FB_SocketSend function block and/or an instance of the FB_SocketReceive function block.

In the first step, a typical client application establishes the connection with the server via the FB_ClientServerConnection function block. In the next step instances of FB_SocketSend and/or FB_SocketReceive can be used to exchange data with the server. When a connection is closed depends on the requirements of the application.

VAR_INPUT

VAR_INPUT
    sSrvNetID 	: T_AmsNetID := '';
    nMode 		: DWORD := 0;
    sRemoteHost 	: STRING(15) := '';
    nRemotePort 	: UDINT;
    bEnable 		: BOOL;
    tReconnect 	: TIME := T#45s;(*!!!*)
END_VAR

sSrvNetID: String containing the Ams network address of the TwinCAT TCP/IP Connection Server. For the local computer (default) an empty string may be specified.

nMode: parameter flags (modes). The permissible parameters are listed in the table and can be combined via an OR operation:

Flag | Description
CONNECT_MODE_ENABLEDBG | Activates logging of debugging messages in the application log. In order to view the debugging messages open the TwinCAT System Manager and activate log view.

sRemoteHost: IP address (Ipv4) of the remote server as a string (e.g. '172.33.5.1'). An empty string can be entered on the local computer for a server.

nRemotePort: IP port number of the remote server (e.g. 200).

bEnable: As long as this input is TRUE, the system attempts to establish a connection at regular intervals until a connection was established successfully. Once established, a connection can be closed again with FALSE.

tReconnect: Cycle time used by the function block to try and establish the connection.

Important note:

The tReconnect value should not be set too low, since timeout periods of > 30s may occur in the event of a network interruption. If the value is too low, command execution would be interrupted prematurely, and ADS error code 1861 (timeout elapsed) would be returned instead of the Winsocket error WSAETIMEDOUT.

VAR_OUTPUT

VAR_OUTPUT
    bBusy  		: BOOL;
    bError  		: BOOL;
    nErrId 		: UDINT;
    hSocket 		: T_HSOCKET;
    eState 		: E_SocketConnectionState := eSOCKET_DISCONNECTED;
END_VAR

bBusy: This output is TRUE as long as the function block is active.

bError: Becomes TRUE as soon as an error has occurred.

nErrID: If the bError output is set, this parameter returns the TwinCAT TCP/IP Connection Server error number.

hSocket: Connection handle for the newly opened local client socket. If successful, this variable is transferred to the instances of the function blocks FB_SocketSend and/or FB_SocketReceive.

eState: Returns the current connection status.

Example of a call in FBD:

PROGRAM MAIN
VAR
    fbClientConnection1 	: FB_ClientServerConnection;
    bConnect1 		: BOOL;
    bBusy1 			: BOOL;
    bError1 			: BOOL;
    nErrID1 			: UDINT;
    hSocket1 			: T_HSOCKET;
    eState1 			: E_SocketConnectionState;
END_VAR

Further application examples (including source code) can be found here: Examples

Development Environment | Target System | PLC libraries to include
TwinCAT v2.9.0 Build >= 1030 | PC or CX (x86) | TcSocketHelper.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib; TcUtilities.Lib; TcpIp.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


F_CreateServerHnd

TwinCAT PLC Library: Socket Helper

FUNCTION F_CreateServerHnd

The function F_CreateServerHnd is used to initialise/set the internal parameters of a server handle variable hServer. The server handle is then transferred to the instances of the FB_ServerClientConnection function block via VAR_IN_OUT. An instance of the FB_ServerClientConnection function block can be used to manage (establish or remove) a sever connection in a straightforward manner. The same server handle can be transferred to several instances of the FB_ServerClientConnection function block, in order to enable the server to establish several concurrent connections.

FUNCTION F_CreateServerHnd : BOOL

VAR_IN_OUT
    hServer 		: T_HSERVER;
END_VAR
VAR_INPUT
    sSrvNetID 	: T_AmsNetID := '';
    sLocalHost 	: STRING(15) := '';
    nLocalPort 	: UDINT := 0;
    nMode 		: DWORD := LISTEN_MODE_CLOSEALL (* OR CONNECT_MODE_ENABLEDBG*);
    bEnable 		: BOOL := TRUE;
END_VAR

hServer: Server handle variable whose internal parameters are to be initialised.

sSrvNetID: String containing the Ams network address of the TwinCAT TCP/IP Connection Server. For the local computer (default) an empty string may be specified.

sLocalHost: Local server IP address (Ipv4) as a string (e.g. '172.13.15.2'). For a server on the local computer (default), an empty string may be entered.

nLocalPort: Local server IP port (e.g. 200).

nMode: parameter flags (modes). The permissible parameters are listed in the table and can be combined via an OR operation:

Flag | Description
LISTEN_MODE_CLOSEALL | All previously opened socket connections are closed (default).
CONNECT_MODE_ENABLEDBG | Activates logging of debugging messages in the application log. In order to view the debugging messages open the TwinCAT System Manager and activate log view.

bEnable: This input determines the behaviour of the listener socket. Once opened, a listener socket remains open until this input becomes TRUE. If this input is FALSE, the listener socket is closed automatically, but only once the last (previously) accepted connection was also closed.

Return value | Description
TRUE | No error
FALSE | Error, invalid parameter value

Example:

See in the FB_ServerClientConnection function block description.

Development Environment | Target System | PLC libraries to include
TwinCAT v2.9.0 Build >= 1030 | PC or CX (x86) | TcSocketHelper.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib; TcUtilities.Lib; TcpIp.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


F_GetVersionTcSocketHelper

TwinCAT PLC Library: Socket Helper

FUNCTION F_GetVersionTcSocketHelper

This function reads version information from the PLC library.

FUNCTION F_GetVersionTcSocketHelper : UINT

VAR_INPUT
    nVersionElement : INT;
END_VAR

nVersionElement : Version element, that is to be read. Possible parameters::

  • 1 : major number;
  • 2 : minor number;
  • 3 : revision number;
Development Environment | Target System | PLC libraries to include
TwinCAT v2.9.0 Build >= 1030 | PC or CX (x86) | TcSocketHelper.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib; TcUtilities.Lib; TcpIp.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


T_HSERVER

TwinCAT PLC Library: Socket Helper

TYPE T_HSERVER

The variable of this type represents a TCP/IP Server Handle. The Handle has to be initialisized with F_CreateServerHnd bevor it can be used. In doing so the internal parameters of variables T_HSERVER are set.

Important note:

The structure elements are not to be written or changed.

Development Environment | Target System | PLC libraries to include
TwinCAT v2.9.0 Build >= 1030 | PC or CX (x86) | TcSocketHelper.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib; TcUtilities.Lib; TcpIp.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


E_SocketAcceptMode

TwinCAT PLC Library: Socket Helper

TYPE E_SocketAcceptMode

TYPE E_SocketAcceptMode:
(* Connection accept modes *)
(
    eACCEPT_ALL, (* Accept connection to all remote clients *)
    eACCEPT_SEL_HOST, (* Accept connection to selected host address *)
    eACCEPT_SEL_PORT, (* Accept connection to selected port address *)
    eACCEPT_SEL_HOST_PORT (* Accept connection to selected host and port address *)
);
END_TYPE

The variable E_SocketAcceptMode defines which connections are to be accepted by a server.

Development Environment | Target System | PLC libraries to include
TwinCAT v2.9.0 Build >= 1030 | PC or CX (x86) | TcSocketHelper.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib; TcUtilities.Lib; TcpIp.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


E_SocketConnectionState

TwinCAT PLC Library: Socket Helper

TYPE E_SocketConnectionState

TYPE E_SocketConnectionState:
(
    eSOCKET_DISCONNECTED,
    eSOCKET_CONNECTED,
    eSOCKET_SUSPENDED
);
END_TYPE

TCP/IP Socket Connection Staus (eSOCKET_SUSPENDED == the status changes e.g.from eSOCKET_CONNECTED=>eSOCKET_DISCONNECTED).

Development Environment | Target System | PLC libraries to include
TwinCAT v2.9.0 Build >= 1030 | PC or CX (x86) | TcSocketHelper.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib; TcUtilities.Lib; TcpIp.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


Constants

TwinCAT PLC Library: Socket Helper

Global constants

Constant | Value | Description
LISTEN_MODE_CLOSEALL | 16#00000001 | FORCED close of all previous opened sockets
LISTEN_MODE_USEOPENED | 16#00000002 | Try to use allready opened listener socket
CONNECT_MODE_ENABLEDBG | 16#80000000 | Enables/Disables debugging messages

Development Environment | Target System | PLC libraries to include
TwinCAT v2.9.0 Build >= 1030 | PC or CX (x86) | TcSocketHelper.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib; TcUtilities.Lib; TcpIp.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


Overview

The TcSnmp.Lib function blocks can be used to send SNMP Traps or receive SNMP GET using the TwinCAT TCP/IP Connection Server. A TwinCAT TCP/IP Connection Server must be running.

Product components

  • TcSnmp.Lib

Installation

Windows NT (NT4, W2K, XP, XPe, 7)

Please do the following steps:

  • Copy the library into ...\TwinCAT\Lib folder.
  • Install the TwinCAT TCP/IP Connection Server. The Server will start automatically after starting TwinCAT


FUNCTION_BLOCK FB_SEND_TRAP

The function block allows to send SNMPv1 Traps. You can add variable bindings for sending additional information to the manager along with the trap. The following SNMP Data Types are supported by the function block for the variable bindings: OCTET_STRING, INTEGER32, COUNTER32, GAUGE32, TIMETICKS, OBJECT_ID

The uptime field is always zero.

The maximum paket size is limited to 2000 byte. If more bytes are committed the function block will return a error.

The function block FB_SEND_TRAP should be called in each cycle for a proper operation.

VAR_INPUT

VAR_INPUT
bEnable :BOOL;
bExecute :BOOL;
sLocalHostIp :STRING(15);
sLocalHostPort :UDINT;
sManagerIP :STRING(15);
sTcIpConnSvrAddr :T_AmsNetId;
sObjectID :T_MAXSTRING;
sCommunity :STRING(60);
iGenericTrapNumber :INT;
bySpecificTrapNumber :BYTE;
nVarBindings :USINT;
pArrVarBinding :POINTER TO ARRAY[1..iMAX_TRAPBUF_SIZE] OF ST_SNMP_VariableBinding;

END_VAR

bEnable: With a rising edge on this input the system attempts to create a Socket. Once established the Output bEnabled is set to TRUE. The Socket can be closed again with a falling edge.

bExecute: Send one Trap with a rising edge on bExecute. A rising edge will clear the output nErrID and bError. A opened Socket is required.

sLocalHostIP: String containing the (IPv4) dotted network address of the local host (e.g. '172.33.5.1'). If there is more than one network adapter present on the machine, the sLocalHostIP parameter allows you to specify which adapter to use.

sLocalHostPort(optional): The local IP port number.

sManagerIP: IP address (IPv4) of the SNMP Manager.

sTcIpConnSvrAddr: not supported

sObjectID: String containing the dotted numerical value that represents the MIB (Management Information Base) of the device. Maximum length of the string is 255. Valid values for each number between the dots is 0...65535. (e.g. '1.3.1.3.2555.3')

sCommunity: String containing the SNMP Community String (e.g. public)

iGenericTrapNumber: The SNMP Generic Trap Number defined in E_SNMP_GenericTrapNumber.

bySpecificTrapNumber: The SNMP Specific Trap Number. Automatically set to 0 if iGenericTrapNumber is not 0x06(E_SNMP_EnterpriseSpecific). Valid values are 1...255.

pArrVarBinding (optional): Pointer to an array of SNMP_ST_VariableBinding.

nVarBindings (optional): Number of the elements in pArrVarBinding. Maximum is iMAX_TRAPBUF_SIZE (defined in Global_Variables)

VAR_OUTPUT

VAR_OUTPUT
bBusy :BOOL;
bEnabled :BOOL;
bError :BOOL;
nErrID :DINT;
END_VAR

bBusy: When the function block is activated this output is set while sending.

bEnabled: This output is set if a Socket is open.

bError: Becomes TRUE as soon as an error has occurred

nErrID: If the bError output is set, this parameter returns a TwinCAT TCP/IP Connection Server error or E_SNMP_ErrorCodes.

The function block is tested with the following software:

SNMP Trap Watcher (BTT Software)

Wireshark 1.2.5

iReasoning MIB Browser Personal Edition 7.0

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )


FUNCTION_BLOCK FB_GetSnmp

The Function Block allows to receive SNMPv1 GET-commands. You can add variable bindings for sending additional information to the SNMP. The following SNMP Data Types are supported by the function block for the variable bindings: OCTET_STRING, INTEGER32, COUNTER32, GAUGE32, TIMETICKS, OBJECT_ID

VAR_INPUT

VAR_INPUT

bEnable :BOOL;
bReceive :BOOL;
bSendTrap :BOOL;
bSendResponse :BOOL;
sLocalHostIp :STRING(15);
nLocalHostPort :UDINT;
sManagerIP :STRING(15);
sTcIpConnSvrAddr :T_AmsNetId := '';

sObjectID :T_MAXSTRING;
sCommunity :STRING(60);
iGenericTrapNumber :INT;
iSpecificTrapNumber :BYTE;

nVarBindings :USINT := 0;
pArrVarBinding :POINTER TO ARRAY[1..iMAX_TRAPBUF_SIZE] OF ST_SNMP_VariableBinding;

iError :INT;
END_VAR

bEnable: With a rising edge on this input the system attempts to create a Socket. Once established the Output bEnabled is set to TRUE. The Socket can be closed again with a falling edge.

bReceive: Signal a received GET command.

bSendTrap: Signal a sended TRAP.

bSendResponse: Signal an answer is ready to send.

sLocalHostIP: String containing the (IPv4) dotted network address of the local host (e.g. '172.33.5.1'). If there is more than one network adapter present on the machine, the sLocalHostIP parameter allows you to specify which adapter to use.

sLocalHostPort(optional): The local IP port number.

sManagerIP: IP address (IPv4) of the SNMP Manager.

sTcIpConnSvrAddr: not supported

sObjectID: String containing the dotted numerical value that represents the MIB (Management Information Base) of the device. Maximum length of the string is 255. Valid values for each number between the dots is 0...65535. (e.g. '1.3.1.3.2555.3')

sCommunity: String containing the SNMP Community String (e.g. public)

iGenericTrapNumber: The SNMP Generic Trap Number defined in E_SNMP_GenericTrapNumber.

iSpecificTrapNumber: The SNMP Specific Trap Number. Automatically set to 0 if iGenericTrapNumber is not 0x06(E_SNMP_EnterpriseSpecific). Valid values are 1...255.

pArrVarBinding (optional): Pointer to an array of SNMP_ST_VariableBinding.

nVarBindings (optional): Number of the elements in pArrVarBinding. Maximum is iMAX_TRAPBUF_SIZE (defined in Global_Variables)

iError: Returns specific SNMP error code.

VAR_OUTPUT

VAR_OUTPUT
bBusy :BOOL;
bError :BOOL;
bEnabled :BOOL;
bReceived :BoOL;
iRecSNMPVersion :INT; (* SNMP Version, sample SNMPv1 = 0 *)
sRecCommunity :STRING(60); (* Community Name *)
iRecPDUType :INT; (* SNMP PDU Type, sample GET = A0 *)
arrRecPDURequestID :ARRAY[0..3] OF BYTE; (* SNMP PDU Request ID *)
iRecPDUError :INT; (* SNMP PDU Error *)
iRecPDUErrorIndex :INT; (* SNMP PDU Error Index *)
sRecObjectID :T_MAXSTRING; (* Object Identifier *)
arrRecObjectID :ARRAY[0..128] OF UDINT;
nErrID :UDINT;
END_VAR

bBusy: When the function block is activated this output is set while sending.

bError: Becomes TRUE as soon as an error has occurred

bEnabled: This output is set if a Socket is open.

bReceived: This oputput is set if a get command has occured..

iRecSNMPVersion: Info about SNMP version. SNMPv1 = 0.

sRecCommunity: Info about the received community strings.

iRecPDUType: Info about the PDU type.

arrRecPDURequestID: Array with PDU request IDs.

iRecPDUError: Output of the PDU errorID.

iRecPDUErrorIndex: Output of error index.

sRecObjectID: String with the objectID.

arrRecObjectID: Array with the received objectIDs .

nErrID: If the bError output is set, this parameter returns a TwinCAT TCP/IP Connection Server error or E_SNMP_ErrorCodes.

The function block is tested with:

SNMP Trap Watcher (BTT Software)

Wireshark 1.2.5

iReasoning MIB Browser Personal Edition 7.0

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )


TYPE SNMP_ST_VariableBinding

Structure for SNMP variable bindings

TYPE ST_SNMP_VariableBinding :
STRUCT
iType :INT;
iLength :INT;
pArrValue :POINTER TO ARRAY[1..10000] OF BYTE;
sOID :STRING(300) ;
END_STRUCT
END_TYPE

iType: The SNMP Data Type defined in E_SNMP_DataTypes.

iLength: The number of elements of pArrValue.

pArrValue: A pointer to the array filled with the values.

sOID: String containing the dotted numerical value of the variable binding.

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )


TYPE E_SNMP_GenericTrapNumber

TYPE E_SNMP_GenericTrapNumber :
(
E_SNMP_ColdStart:= 16#00,
E_SNMP_WarmStart:= 16#01,
E_SNMP_LinkDown:=16#02,
E_SNMP_LinkUp:= 16#03,
E_SNMP_AuthentificationFailure:= 16#04,
E_SNMP_EgpNeighborLoss:= 16#05,
E_SNMP_EnterpriseSpecific:= 16#06
);
END_TYPE

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcSnmp.lib, TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )


TYPE E_SNMP_DataTypes

TYPE E_SNMP_DataTypes :
(
E_SNMP_INTEGER:= 16#02,
E_SNMP_OCTETSTRING:= 16#04,
E_SNMP_OBJECTID:=16#06,
E_SNMP_SEQUENCE := 16#30,
E_SNMP_IPADDRESS:= 16#40,
E_SNMP_COUNTER32:= 16#41,
E_SNMP_GAUGE32:= 16#42,
E_SNMP_TIMETICKS:= 16#43,
E_SNMP_TRAPTYPE:= 16#A4
);
END_TYPE

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib werden automatisch hinzugefügt )


Global Variables

VAR_GLOBAL CONSTANT

VAR_GLOBAL CONSTANT
    iMAX_TRAPBUF_SIZE :USINT := 255;
END_VAR

END_VAR

iMAX_TRAPBUF_SIZE: Maximum Number of elements in pArrVarBinding (POINTER TO ARRAY[1..iMAX_TRAPBUF_SIZE] OF ST_SNMP_VariableBinding;)

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcpIp.Lib ( Standard.Lib; TcBase.Lib; TcSystem.Lib wird automatisch hinzugefügt )


TCP Example: Overview

The following example shows an implementation of an "echo" client/server. The local client should send a test string to the remote server at certain intervals (e.g. every second). The remote server should then immediately resend the same string unchanged to the client.

The client should be implemented as a function block, of which several instances can be created. The server should be able to communicate with several clients.

Several instances of the server may be created. Each server instance is then addressed via a different port number. The server implementation is more difficult if the server has to communicate with more than one client. An implementation of a suitable client in .NET is also presented. The example can be used as a basis for realising more complex implementations.

System requirements

  • TwinCAT v2.8 or higher. Level: TwinCAT PLC as a minimum.
  • Installed TwinCAT TCP/IP Connection Server. If two PCs are used for the test, the TwinCAT TCP/IP Connection Server should be installed on both PCs.

Project sources

  • Unpacking TCP/IP client project
  • Unpacking TCP/IP server project
  • Unpacking .NET client project

Project description

  • FB_LocalClient function block
  • FB_LocalServer function block
  • Testing the function blocks
  • .NET client project

Auxiliary functions in the project example

In the example several functions, constants and function blocks are used, which are briefly described below:

FUNCTION LogError : DINT

The function writes a message with the error code into the log book of the operating system (Event Viewer). The global variable bLogDebugMessages must first be set to TRUE.

FUNCTION LogMessage : DINT

The function writes a message into the log book of the operating system (Event Viewer) if a new socket was opened or closed. The global variable bLogDebugMessages must first be set to TRUE.

FUNCTION SCODE_CODE : DWORD

The function masks the lower 16bits of a Win32 error code returns them.

Global constants/variables

Name | Default value | Description
bLogDebugMessages | TRUE | Activates/deactivates writing of messages into the log book of the operating system;
MAX_CLIENT_CONNECTIONS | 5 | Maximum number of remote clients that can simultaneously establish a connection with the server;
MAX_PLCPRJ_RXBUFFER_SIZE | 1000 | Max. length of the internal receive buffer;
PLCPRJ_RECONNECT_TIME T#3s SERVER: After this time has elapsed, the local server will attempt to re-open the listener socket;

CLIENT: Once this time has elapsed, the local client will attempt to re-establish the connection with the remote server;
PLCPRJ_SEND_CYCLE_TIME | T#1s | The test string is sent cyclically at these intervals from the local client to the remote server;
PLCPRJ_RECEIVE_POLLING_TIME | T#1s | SERVER and CLIENT: The client and server reads (polls) data from the server or client using this cycle;
PLCPRJ_RECEIVE_TIMEOUT T#50s (SERVER)

T#10s (CLIENT)

 SERVER: After this time has elapsed, the local server aborts the reception if no data bytes could be received during this time;

CLIENT: After this time has elapsed, the local client aborts the reception if no data bytes could be received during this time;
PLCPRJ_ACCEPT_POLLING_TIME | T#1s | At these intervals, the local server will attempt to accept the connection requests of the remote client;
PLCPRJ_ERROR_RECEIVE_BUFFER_OVERFLOW | 16#8101 | Sample project error code: Too many characters without zero termination were received;
PLCPRJ_ERROR_RECEIVE_TIMEOUT | 16#8102 | Sample project error code: No new data could be received within the timeout time (PLCPRJ_RECEIVE_TIMEOUT).


Testing the client and server function blocks

1. Open the TCP/IP client project on the local PC. The IP address of the server has to be adapted to your remote system (initialisation values of the sRemoteHost variables). Load the project into the PLC runtime system on the local PC. Start the PLC.

PROGRAM MAIN
VAR
        fbClient1               : FB_LocalClient := ( sRemoteHost:= '172.16.11.83' (* IP address of remote server! *), nRemotePort:= 200 );
        fbClient2               : FB_LocalClient := ( sRemoteHost:= '172.16.11.83', nRemotePort:= 200 );
        fbClient3               : FB_LocalClient := ( sRemoteHost:= '172.16.11.83', nRemotePort:= 200 );
        fbClient4               : FB_LocalClient := ( sRemoteHost:= '172.16.11.83', nRemotePort:= 200 );
        fbClient5               : FB_LocalClient := ( sRemoteHost:= '172.16.11.83', nRemotePort:= 200 );

        bEnableClient1  : BOOL := TRUE;
        bEnableClient2  : BOOL := FALSE;
        bEnableClient3  : BOOL := FALSE;
        bEnableClient4  : BOOL := FALSE;
        bEnableClient5  : BOOL := FALSE;

        fbSocketCloseAll        : FB_SocketCloseAll := ( sSrvNetID := '', tTimeout := DEFAULT_ADS_TIMEOUT );
        bCloseAll               : BOOL := TRUE;

        nCount          : UDINT;
END_VAR

IF bCloseAll THEN (*On PLC reset or program download close all old connections *)
        bCloseAll := FALSE;
        fbSocketCloseAll( bExecute:= TRUE );
ELSE
        fbSocketCloseAll( bExecute:= FALSE );
END_IF

IF NOT fbSocketCloseAll.bBusy THEN
        nCount := nCount + 1;
        fbClient1( bEnable := bEnableClient1, sToServer := CONCAT( 'CLIENT1-', UDINT_TO_STRING( nCount ) ) );
        fbClient2( bEnable := bEnableClient2, sToServer := CONCAT( 'CLIENT2-', UDINT_TO_STRING( nCount ) ) );
        fbClient3( bEnable := bEnableClient3, sToServer := CONCAT( 'CLIENT3-', UDINT_TO_STRING( nCount ) ) );
        fbClient4( bEnable := bEnableClient4  );
        fbClient5( bEnable := bEnableClient5  );
END_IF

Up to 5 client instances can be activated by setting the bEnableCientX variable. Each client sends a string (default: 'TEST') to the server approximately every second. The server returns the same string to the client (echo server). For the test, a string with a counter value is generated automatically for the first three instances. The first client is activated automatically when the program is started.

2. Open the TCP/IP server project also on the local PC. Select a PLC runtime system on a remote PC as the target system. Load the PLC program into the runtime system. Start the PLC.

PROGRAM MAIN
VAR
        fbServer                : FB_LocalServer := ( sLocalHost := '172.16.11.83' (*own IP address!*), nLocalPort := 200 );
        bEnableServer   : BOOL := TRUE;
        fbSocketCloseAll        : FB_SocketCloseAll :=  ( sSrvNetID := '', tTimeout := DEFAULT_ADS_TIMEOUT );
        bCloseAll               : BOOL := TRUE;
END_VAR

IF bCloseAll THEN (*On PLC reset or program download close all old connections *)
        bCloseAll := FALSE;
        fbSocketCloseAll( bExecute:= TRUE );
ELSE
        fbSocketCloseAll( bExecute:= FALSE );
END_IF

IF NOT fbSocketCloseAll.bBusy THEN
        fbServer( bEnable := bEnableServer );
END_IF

3. Set the bEnableCilent4 variable in the client project to TRUE. The second client will then attempt to establish a connection with the server. If successful, the 'TEST' string is sent cyclically. Now open the fbClient4 instance of the FB_LocalClient function block.

Double-click to open the dialog for writing the sToString variable. Change the value of the string variable, for example to 'Hello'. Close the dialog with OK. Write the new value into the PLC (CTRL+F7). Shortly afterwards, the value read back by the server can also be seen online.

4. Now open the fbServer instance of the FB_LocalServer function block in the server project. Our string: 'Hello' can be seen in the online data of the server.

5. In the server and client examples, messages are written into the log book of the operating system during establishment/closing of the connection and in the event of an error. This facilitates troubleshooting. These messages can be displayed in the logger output of the TwinCAT System Manager. Start the TwinCAT System Manager on the local system and activate the logger output. Now deactivate the two clients (set bEnableClient1 and bEnableClient4 to FALSE ).

The server messages can also be displayed on the local PC. To this end, you have to open a second instance of the TwinCAT System Manager on the local PC and select the remote PC as the target system in the TwinCAT System Manager.


FB_LocalClient

TS6310 | TwinCAT PLC Lib: TCP/IP

FUNCTION_BLOCK FB_LocalClient

Here you can unpack the complete source for the client project: Example 1: Client;

If the bEnable input is set, the system will keep trying to establish the connection to the remote server once the PLCPRJ_RECONNECT_TIME has elapsed. The remote server is identified via the sRemoteHost IP address and the nRemotePort IP port address. The data exchange with the server was encapsulated in a separate function block (FB_ClientDataExcha). Data exchange is always cyclic once PLCPRJ_SEND_CYCLE_TIME has elapsed. The sToServer string variable is sent to the server, and the string sent back by the server is returned at output sFormServer. Another implementation, in which the remote server is addressed as required is also possible. In the event of an error, the existing connection is closed, and a new connection is established.

Interface

FUNCTION_BLOCK FB_LocalClient
VAR_INPUT
        sRemoteHost             : STRING(15)  := '127.0.0.1';(* IP adress of remote server *)
        nRemotePort             : UDINT := 0;
        sToServer               : T_MaxString:= 'TEST';
        bEnable                         : BOOL;
END_VAR
VAR_OUTPUT
        bConnected              : BOOL;
        hSocket                         : T_HSOCKET;
        bBusy                   : BOOL;
        bError                  : BOOL;
        nErrId                  : UDINT;
        sFromServer             : T_MaxString;
END_VAR
VAR
        fbConnect               : FB_SocketConnect := ( sSrvNetId := '' );
        fbClose                         : FB_SocketClose := ( sSrvNetId := '', tTimeout := DEFAULT_ADS_TIMEOUT );
        fbClientDataExcha               : FB_ClientDataExcha;

        fbConnectTON            : TON := ( PT := PLCPRJ_RECONNECT_TIME );
        fbDataExchaTON          : TON := ( PT := PLCPRJ_SEND_CYCLE_TIME );
        eStep                   : E_ClientSteps;
END_VAR

Implementation

CASE eStep OF
        CLIENT_STATE_IDLE:
                IF bEnable XOR bConnected THEN
                        bBusy := TRUE;
                        bError := FALSE;
                        nErrid := 0;
                        sFromServer := '';
                        IF bEnable THEN
                                fbConnectTON( IN := FALSE );
                                eStep := CLIENT_STATE_CONNECT_START;
                        ELSE
                                eStep := CLIENT_STATE_CLOSE_START;
                        END_IF
                ELSIF bConnected THEN
                        fbDataExchaTON( IN := FALSE );
                        eStep := CLIENT_STATE_DATAEXCHA_START;
                ELSE
                        bBusy := FALSE;
                END_IF

        CLIENT_STATE_CONNECT_START:
                fbConnectTON( IN := TRUE, PT := PLCPRJ_RECONNECT_TIME );
                IF fbConnectTON.Q THEN
                        fbConnectTON( IN := FALSE );
                        fbConnect(  bExecute := FALSE  );
                        fbConnect(sRemoteHost   := sRemoteHost,
                                nRemotePort     := nRemotePort,
                                bExecute                := TRUE );
                        eStep := CLIENT_STATE_CONNECT_WAIT;
                END_IF

        CLIENT_STATE_CONNECT_WAIT:
                fbConnect( bExecute := FALSE );
                IF NOT fbConnect.bBusy THEN
                        IF NOT fbConnect.bError THEN
                                bConnected      := TRUE;
                                hSocket                 := fbConnect.hSocket;
                                eStep           := CLIENT_STATE_IDLE;
                                LogMessage( 'LOCAL client CONNECTED!', hSocket );
                        ELSE
                                LogError( 'FB_SocketConnect', fbConnect.nErrId );
                                nErrId := fbConnect.nErrId;
                                eStep := CLIENT_STATE_ERROR;
                        END_IF
                END_IF

        CLIENT_STATE_DATAEXCHA_START:
                fbDataExchaTON( IN := TRUE, PT := PLCPRJ_SEND_CYCLE_TIME );
                IF fbDataExchaTON.Q THEN
                        fbDataExchaTON( IN := FALSE );
                        fbClientDataExcha( bExecute := FALSE );
                        fbClientDataExcha( hSocket      := hSocket,
                                        sToServer := sToServer,
                                        bExecute        := TRUE );
                        eStep := CLIENT_STATE_DATAEXCHA_WAIT;
                END_IF

        CLIENT_STATE_DATAEXCHA_WAIT:
                fbClientDataExcha( bExecute := FALSE );
                IF NOT fbClientDataExcha.bBusy THEN
                        IF NOT fbClientDataExcha.bError THEN
                                sFromServer     := fbClientDataExcha.sFromServer;
                                eStep           := CLIENT_STATE_IDLE;
                        ELSE
                                (* possible errors are logged inside of fbClientDataExcha function block *)
                                nErrId := fbClientDataExcha.nErrId;
                                eStep :=CLIENT_STATE_ERROR;
                        END_IF
                END_IF

        CLIENT_STATE_CLOSE_START:
                fbClose( bExecute := FALSE );
                fbClose(        hSocket:= hSocket,
                        bExecute:= TRUE );
                eStep := CLIENT_STATE_CLOSE_WAIT;

        CLIENT_STATE_CLOSE_WAIT:
                fbClose( bExecute := FALSE );
                IF NOT fbClose.bBusy THEN
                        LogMessage( 'LOCAL client CLOSED!', hSocket );
                        bConnected := FALSE;
                        MEMSET( ADR(hSocket), 0, SIZEOF(hSocket));
                        IF fbClose.bError THEN
                                LogError( 'FB_SocketClose (local client)', fbClose.nErrId );
                                nErrId := fbClose.nErrId;
                                eStep := CLIENT_STATE_ERROR;
                        ELSE
                                bBusy := FALSE;
                                bError := FALSE;
                                nErrId := 0;
                                eStep := CLIENT_STATE_IDLE;
                        END_IF
                END_IF

        CLIENT_STATE_ERROR: (* Error step *)
                bError := TRUE;
                IF bConnected THEN
                        eStep := CLIENT_STATE_CLOSE_START;
                ELSE
                        bBusy := FALSE;
                        eStep := CLIENT_STATE_IDLE;
                END_IF
END_CASE


FUNCTION_BLOCK FB_ClientDataExcha

In the event of an rising edge at the bExecute input, a zero-terminated string is sent to the remote server, and a string returned by the remote server is read. The function block will try reading the data until zero termination was detected in the string received. Reception is aborted in the event of an error, and if no new data were received within the PLCPRJ_RECEIVE_TIMEOUT timeout time. Data are attempted to be read again after a certain delay time, if no new data could be read during the last read attempt. This reduces the system load.

Interface

FUNCTION_BLOCK FB_ClientDataExcha
VAR_INPUT
    hSocket 		: T_HSOCKET;
    sToServer 	: T_MaxString;
    bExecute 		: BOOL;
END_VAR
VAR_OUTPUT
    bBusy 		: BOOL;
    bError 		: BOOL;
    nErrId 		: UDINT;
    sFromServer 	: T_MaxString;
END_VAR
VAR
    fbSocketSend 	: FB_SocketSend := ( sSrvNetID := '', tTimeout := DEFAULT_ADS_TIMEOUT );
    fbSocketReceive 	: FB_SocketReceive := ( sSrvNetID := '', tTimeout := DEFAULT_ADS_TIMEOUT );
    fbReceiveTON	: TON;
    fbDisconnectTON	: TON;
    RisingEdge	: R_TRIG;
    eStep		: E_DataExchaSteps;
    cbReceived, startPos, endPos, idx	: UDINT;
    cbFrame		: UDINT;
    rxBuffer		: ARRAY[0..MAX_PLCPRJ_RXBUFFER_SIZE] OF BYTE;
END_VAR

Implementation

RisingEdge( CLK := bExecute );
CASE eStep OF
    DATAEXCHA_STATE_IDLE:
        IF RisingEdge.Q THEN
            bBusy := TRUE;
            bError := FALSE;
            nErrid := 0;
            cbReceived := 0;
            fbReceiveTON( IN := FALSE, PT := T#0s ); (* don't wait, read the first answer data immediately *)
            fbDisconnectTON( IN := FALSE, PT := T#0s );(* disable timeout check first *)
            eStep := DATAEXCHA_STATE_SEND_START;
        END_IF

    DATAEXCHA_STATE_SEND_START:
        fbSocketSend( bExecute := FALSE );
        fbSocketSend(	hSocket	:= hSocket,
                pSrc	:= ADR( sToServer ),
                cbLen	:= LEN( sToServer ) + 1,(* string length inclusive zero delimiter *)
                bExecute:= TRUE );
        eStep := DATAEXCHA_STATE_SEND_WAIT;

    DATAEXCHA_STATE_SEND_WAIT:
        fbSocketSend( bExecute := FALSE );
        IF NOT fbSocketSend.bBusy THEN
            IF NOT fbSocketSend.bError THEN
                eStep := DATAEXCHA_STATE_RECEIVE_START;
            ELSE
                LogError( 'FB_SocketSend (local client)', fbSocketSend.nErrId );
                nErrId := fbSocketSend.nErrId;
                eStep := DATAEXCHA_STATE_ERROR;
            END_IF
        END_IF

    DATAEXCHA_STATE_RECEIVE_START:
        fbDisconnectTON(  );
        fbReceiveTON( IN := TRUE );
        IF fbReceiveTON.Q THEN
            fbReceiveTON( IN := FALSE );
            fbSocketReceive( bExecute := FALSE );
            fbSocketReceive(  hSocket:= hSocket,
                    pDest:= ADR( rxBuffer ) + cbReceived,
                    cbLen:= SIZEOF( rxBuffer ) - cbReceived,
                    bExecute:= TRUE );
            eStep := DATAEXCHA_STATE_RECEIVE_WAIT;
        END_IF

    DATAEXCHA_STATE_RECEIVE_WAIT:
        fbSocketReceive( bExecute := FALSE );
        IF NOT fbSocketReceive.bBusy THEN
            IF NOT fbSocketReceive.bError THEN
                IF (fbSocketReceive.nRecBytes > 0) THEN(* bytes received *)
                    startPos 		:= cbReceived;(* rxBuffer array index of first data byte *)
                    endPos  		:= cbReceived + fbSocketReceive.nRecBytes  - 1;(* rxBuffer array index of last data byte *)
                    cbReceived 	:= cbReceived + fbSocketReceive.nRecBytes;(* calculate the number of received data bytes *)
                    cbFrame		:= 0;(* reset frame length *)
                    IF cbReceived < SIZEOF( sFromServer ) THEN(* no overflow *)
                        fbReceiveTON( PT := T#0s ); (* bytes received => increase the read (polling) speed *)
                        fbDisconnectTON( IN := FALSE );(* bytes received => disable timeout check *)
                        (* search for string end delimiter *)
                        FOR idx := startPos TO endPos BY 1 DO
                            IF rxBuffer[idx] = 0 THEN(* string end delimiter found *)
                                cbFrame := idx + 1;(* calculate the length of the received string (inclusive the end delimiter) *)
                                MEMCPY(  ADR( sFromServer ), ADR( rxBuffer ), cbFrame );(* copy the received string to the output variable (inclusive the end delimiter) *)
                                MEMMOVE( ADR( rxBuffer ), ADR( rxBuffer[cbFrame] ),  cbReceived - cbFrame  );(* move the reamaining data bytes *)
                                cbReceived := cbReceived - cbFrame;(* recalculate the remaining data byte length *)
                                bBusy := FALSE;
                                eStep := DATAEXCHA_STATE_IDLE;
                                EXIT;
                            END_IF
                        END_FOR
                    ELSE(* there is no more free read buffer space => the answer string should be terminated *)
                        LogError( 'FB_SocketReceive (local client)',  PLCPRJ_ERROR_RECEIVE_BUFFER_OVERFLOW );
                        nErrId := PLCPRJ_ERROR_RECEIVE_BUFFER_OVERFLOW;(* buffer overflow !*)
                        eStep := DATAEXCHA_STATE_ERROR;
                    END_IF
                ELSE(* no bytes received *)
                    fbReceiveTON( PT := PLCPRJ_RECEIVE_POLLING_TIME );(* no bytes received => decrease the read (polling) speed *)
                    fbDisconnectTON( IN := TRUE, PT := PLCPRJ_RECEIVE_TIMEOUT );(* no bytes received => enable timeout check*)
                    IF fbDisconnectTON.Q THEN (* timeout error*)
                        fbDisconnectTON( IN := FALSE );
                        LogError( 'FB_SocketReceive (local client)',  PLCPRJ_ERROR_RECEIVE_TIMEOUT );
                        nErrID := PLCPRJ_ERROR_RECEIVE_TIMEOUT;
                        eStep := DATAEXCHA_STATE_ERROR;
                    ELSE(* repeat reading *)
                        eStep := DATAEXCHA_STATE_RECEIVE_START; (* repeat reading *)
                    END_IF
                END_IF
            ELSE(* receive error *)
                LogError( 'FB_SocketReceive (local client)',  fbSocketReceive.nErrId );
                nErrId := fbSocketReceive.nErrId;
                eStep := DATAEXCHA_STATE_ERROR;
            END_IF
        END_IF

    DATAEXCHA_STATE_ERROR:(* error step *)
        bBusy := FALSE;
        bError := TRUE;
        cbReceived := 0;
        eStep := DATAEXCHA_STATE_IDLE;
END_CASE


FB_LocalServer

TS6310 | TwinCAT PLC Lib: TCP/IP

FUNCTION_BLOCK FB_LocalServer

Here you can unpack the complete source for the server project: Example 1: Server;

The server must first be allocated a unique sLocalHost IP address and an nLocaPort IP port number. If the bEnable input is set, the local server will repeatedly try to open the listener socket once the PLCPRJ_RECONNECT_TIME has elapsed. The listener socket can usually be opened at the first attempt, if the TwinCAT TCP/IP Connection Server resides on the local PC. The functionality of a remote client was encapsulated in the function block FB_RemoteClient. The remote client instances are activated once the listener socket was opened successfully. Each instance of the FB_RemoteClient corresponds to a remote client, with which the local server can communicate simultaneously. The maximum number of remote clients communicating with the server can be modified via the value of the MAX_CLIENT_CONNECTIONS constant. In the event of an error, first all remote client connections are closed, followed by the listener sockets. The nAcceptedClients output provides information about the current number of connected clients.

Interface

FUNCTION_BLOCK FB_LocalServer
VAR_INPUT
        sLocalHost                      : STRING(15)  := '127.0.0.1';(* own IP address! *)
        nLocalPort                      : UDINT := 0;
        bEnable                                 : BOOL;
END_VAR
VAR_OUTPUT
        bListening                      : BOOL;
        hListener                       : T_HSOCKET;
        nAcceptedClients                        : UDINT;
        bBusy                           : BOOL;
        bError                          : BOOL;
        nErrId                          : UDINT;
END_VAR
VAR
        fbListen                                : FB_SocketListen  := ( sSrvNetID := '', tTimeout := DEFAULT_ADS_TIMEOUT );
        fbClose                                 : FB_SocketClose  := ( sSrvNetID := '', tTimeout := DEFAULT_ADS_TIMEOUT );
        fbConnectTON                    : TON  := ( PT := PLCPRJ_RECONNECT_TIME );
        eStep                           : E_ServerSteps;
        fbRemoteClient                  : ARRAY[1..MAX_CLIENT_CONNECTIONS ] OF FB_RemoteClient;
        i                               : UDINT;
END_VAR

Implementation

CASE eStep OF

        SERVER_STATE_IDLE:
                IF bEnable XOR bListening THEN
                        bBusy := TRUE;
                        bError := FALSE;
                        nErrId := 0;
                        IF bEnable THEN
                                fbConnectTON( IN := FALSE );
                                eStep := SERVER_STATE_LISTENER_OPEN_START;
                        ELSE
                                eStep := SERVER_STATE_REMOTE_CLIENTS_CLOSE;
                        END_IF
                ELSIF bListening THEN
                        eStep := SERVER_STATE_REMOTE_CLIENTS_COMM;
                END_IF

        SERVER_STATE_LISTENER_OPEN_START:
                fbConnectTON( IN := TRUE, PT := PLCPRJ_RECONNECT_TIME );
                IF fbConnectTON.Q THEN
                        fbConnectTON( IN := FALSE );
                        fbListen(  bExecute := FALSE  );
                        fbListen(       sLocalHost:= sLocalHost,
                                nLocalPort:= nLocalPort,
                                bExecute        := TRUE );
                        eStep := SERVER_STATE_LISTENER_OPEN_WAIT;
                END_IF

        SERVER_STATE_LISTENER_OPEN_WAIT:
                fbListen( bExecute := FALSE );
                IF NOT fbListen.bBusy THEN
                        IF NOT fbListen.bError THEN
                                bListening      := TRUE;
                                hListener       := fbListen.hListener;
                                eStep           := SERVER_STATE_IDLE;
                                LogMessage( 'LISTENER socket OPENED!', hListener );
                        ELSE
                                LogError( 'FB_SocketListen',  fbListen.nErrId );
                                nErrId := fbListen.nErrId;
                                eStep := SERVER_STATE_ERROR;
                        END_IF
                END_IF

        SERVER_STATE_REMOTE_CLIENTS_COMM:
                eStep := SERVER_STATE_IDLE;
                nAcceptedClients := 0;
                FOR i:= 1 TO MAX_CLIENT_CONNECTIONS DO
                        fbRemoteClient[ i ]( hListener := hListener, bEnable := TRUE );
                        IF NOT  fbRemoteClient[ i ].bBusy AND fbRemoteClient[ i ].bError THEN (*FB_SocketAccept returned error!*)
                                eStep := SERVER_STATE_REMOTE_CLIENTS_CLOSE;
                                EXIT;
                        END_IF
                        (* count the number of connected remote clients *)
                        IF fbRemoteClient[ i ].bAccepted THEN
                                nAcceptedClients := nAcceptedClients + 1;
                        END_IF
                END_FOR

        SERVER_STATE_REMOTE_CLIENTS_CLOSE:
                nAcceptedClients := 0;
                eStep := SERVER_STATE_LISTENER_CLOSE_START; (* close listener socket too *)
                FOR i:= 1 TO MAX_CLIENT_CONNECTIONS DO
                        fbRemoteClient[ i ]( bEnable := FALSE );(* close all remote client (accepted) sockets *)
                        (* check if all remote client sockets are closed *)
                        IF fbRemoteClient[ i ].bAccepted THEN
                                eStep := SERVER_STATE_REMOTE_CLIENTS_CLOSE; (* stay here and close all remote clients first *)
                                nAcceptedClients := nAcceptedClients + 1;
                        END_IF
                END_FOR

        SERVER_STATE_LISTENER_CLOSE_START:
                fbClose( bExecute := FALSE );
                fbClose(        hSocket := hListener,
                                bExecute:= TRUE );
                eStep := SERVER_STATE_LISTENER_CLOSE_WAIT;

        SERVER_STATE_LISTENER_CLOSE_WAIT:
                fbClose( bExecute := FALSE );
                IF NOT fbClose.bBusy THEN
                        LogMessage( 'LISTENER socket CLOSED!', hListener );
                        bListening := FALSE;
                        MEMSET( ADR(hListener), 0, SIZEOF(hListener));
                        IF fbClose.bError THEN
                                LogError( 'FB_SocketClose (listener)',  fbClose.nErrId );
                                nErrId := fbClose.nErrId;
                                eStep := SERVER_STATE_ERROR;
                        ELSE
                                bBusy := FALSE;
                                bError := FALSE;
                                nErrId := 0;
                                eStep := SERVER_STATE_IDLE;
                        END_IF
                END_IF

        SERVER_STATE_ERROR:
                bError := TRUE;
                IF bListening THEN
                        eStep := SERVER_STATE_REMOTE_CLIENTS_CLOSE;
                ELSE
                        bBusy := FALSE;
                        eStep := SERVER_STATE_IDLE;
                END_IF

END_CASE


FUNCTION_BLOCK FB_RemoteClient

If the bEnable input is set, an attempt is made to accept the connection request of a remote client, once the PLCPRJ_ACCEPT_POOLING_TIME has elapsed. The data exchange with the remote client was encapsulated in a separate function block (FB_ServerDataExcha). Once the connection was established successfully, the instance is activated via the FB_ServerDataExcha function block. In the event of an error, the accepted connection is closed, and a new connection is established.

Interface

FUNCTION_BLOCK FB_RemoteClient
VAR_INPUT
    hListener 			: T_HSOCKET;
    bEnable 				: BOOL;
END_VAR
VAR_OUTPUT
    bAccepted 			: BOOL;
    hSocket 				: T_HSOCKET;
    bBusy 				: BOOL;
    bError 				: BOOL;
    nErrID 				: UDINT;
    sFromClient 			: T_MaxString;
END_VAR
VAR
    fbAccept 				: FB_SocketAccept  := ( sSrvNetID := '', tTimeout := DEFAULT_ADS_TIMEOUT );
    fbClose 				: FB_SocketClose := ( sSrvNetID := '', tTimeout := DEFAULT_ADS_TIMEOUT );
    fbServerDataExcha 			: FB_ServerDataExcha;
    fbAcceptTON 			: TON := ( PT := PLCPRJ_ACCEPT_POLLING_TIME );
    eStep 				: E_ClientSteps;
END_VAR

Implementation

CASE eStep OF

    CLIENT_STATE_IDLE:
        IF bEnable XOR bAccepted THEN
            bBusy := TRUE;
            bError := FALSE;
            nErrId := 0;
            sFromClient := '';
            IF bEnable THEN
                fbAcceptTON( IN := FALSE );
                eStep := CLIENT_STATE_CONNECT_START;
            ELSE
                eStep := CLIENT_STATE_CLOSE_START;
            END_IF
        ELSIF bAccepted THEN
            eStep := CLIENT_STATE_DATAEXCHA_START;
        ELSE
            bBusy := FALSE;
        END_IF

    CLIENT_STATE_CONNECT_START:
        fbAcceptTON( IN := TRUE, PT := PLCPRJ_ACCEPT_POLLING_TIME );
        IF fbAcceptTON.Q THEN
            fbAcceptTON( IN := FALSE );
            fbAccept(  bExecute := FALSE  );
            fbAccept(	hListener := hListener,
                        bExecute:= TRUE );
            eStep := CLIENT_STATE_CONNECT_WAIT;
        END_IF

    CLIENT_STATE_CONNECT_WAIT:
        fbAccept( bExecute := FALSE );
        IF NOT fbAccept.bBusy THEN
            IF NOT fbAccept.bError THEN
                IF fbAccept.bAccepted THEN
                    bAccepted := TRUE;
                    hSocket := fbAccept.hSocket;
                    LogMessage( 'REMOTE client ACCEPTED!', hSocket );
                END_IF
                eStep := CLIENT_STATE_IDLE;
            ELSE
                LogError( 'FB_SocketAccept',  fbAccept.nErrId );
                nErrId := fbAccept.nErrId;
                eStep := CLIENT_STATE_ERROR;
            END_IF
        END_IF

    CLIENT_STATE_DATAEXCHA_START:
        fbServerDataExcha( bExecute := FALSE );
        fbServerDataExcha( hSocket := hSocket,
                bExecute := TRUE );
        eStep := CLIENT_STATE_DATAEXCHA_WAIT;

    CLIENT_STATE_DATAEXCHA_WAIT:
        fbServerDataExcha( bExecute := FALSE, sFromClient=>sFromClient );
        IF NOT fbServerDataExcha.bBusy THEN
            IF NOT fbServerDataExcha.bError THEN
                eStep := CLIENT_STATE_IDLE;
            ELSE
                (* possible errors are logged inside of fbServerDataExcha function block *)
                nErrId := fbServerDataExcha.nErrID;
                eStep := CLIENT_STATE_ERROR;
            END_IF
        END_IF

    CLIENT_STATE_CLOSE_START:
        fbClose( bExecute := FALSE );
        fbClose(	hSocket:= hSocket,
                bExecute:= TRUE );
        eStep := CLIENT_STATE_CLOSE_WAIT;

    CLIENT_STATE_CLOSE_WAIT:
        fbClose( bExecute := FALSE );
        IF NOT fbClose.bBusy THEN
            LogMessage( 'REMOTE client CLOSED!', hSocket );
            bAccepted := FALSE;
            MEMSET( ADR( hSocket ), 0, SIZEOF( hSocket ) );
            IF fbClose.bError THEN
                LogError( 'FB_SocketClose (remote client)', fbClose.nErrId );
                nErrId := fbClose.nErrId;
                eStep := CLIENT_STATE_ERROR;
            ELSE
                bBusy := FALSE;
                bError := FALSE;
                nErrId := 0;
                eStep := CLIENT_STATE_IDLE;
            END_IF
        END_IF

    CLIENT_STATE_ERROR:
        bError := TRUE;
        IF bAccepted THEN
            eStep := CLIENT_STATE_CLOSE_START;
        ELSE
            eStep := CLIENT_STATE_IDLE;
            bBusy := FALSE;
        END_IF

END_CASE


FUNCTION_BLOCK FB_ServerDataExcha

In the event of an rising edge at the bExecute input, a zero-terminated string is read by the remote client and returned to the remote client, if zero termination was detected. The function block will try reading the data until zero termination was detected in the string received. Reception is aborted in the event of an error, and if no new data were received within the PLCPRJ_RECEIVE_TIMEOUT timeout time. Data are attempted to be read again after a certain delay time, if no new data could be read during the last read attempt. This reduces the system load.

Interface

FUNCTION_BLOCK FB_ServerDataExcha
VAR_INPUT
    hSocket		: T_HSOCKET;
    bExecute		: BOOL;
END_VAR
VAR_OUTPUT
    bBusy		: BOOL;
    bError 		: BOOL;
    nErrID		: UDINT;
    sFromClient	: T_MaxString;
END_VAR
VAR
    fbSocketReceive	: FB_SocketReceive  := ( sSrvNetId := '', tTimeout := DEFAULT_ADS_TIMEOUT );
    fbSocketSend	: FB_SocketSend := ( sSrvNetId := '', tTimeout := DEFAULT_ADS_TIMEOUT );
    eStep		: E_DataExchaSteps;
    RisingEdge	: R_TRIG;
    fbReceiveTON	: TON;
    fbDisconnectTON 	: TON;
    cbReceived, startPos, endPos, idx	: UDINT;
    cbFrame		: UDINT;
    rxBuffer		: ARRAY[0..MAX_PLCPRJ_RXBUFFER_SIZE] OF BYTE;
END_VAR

Implementation

RisingEdge( CLK := bExecute );
CASE eStep OF

    DATAEXCHA_STATE_IDLE:
        IF RisingEdge.Q THEN
            bBusy 	:= TRUE;
            bError 	:= FALSE;
            nErrId 	:= 0;
            fbDisconnectTON( IN := FALSE, PT := T#0s );(* disable timeout check first *)
            fbReceiveTON( IN := FALSE, PT := T#0s ); (* receive first request immediately *)
            eStep 	:= DATAEXCHA_STATE_RECEIVE_START;
        END_IF

    DATAEXCHA_STATE_RECEIVE_START: (* Receive remote client data *)
        fbReceiveTON(  IN := TRUE );
        IF fbReceiveTON.Q THEN
            fbReceiveTON( IN := FALSE );
            fbSocketReceive( bExecute := FALSE );
            fbSocketReceive( 	hSocket	:= hSocket,
                    pDest	:= ADR( rxBuffer ) + cbReceived,
                    cbLen	:= SIZEOF( rxBuffer ) - cbReceived,
                    bExecute	:= TRUE );
            eStep := DATAEXCHA_STATE_RECEIVE_WAIT;
        END_IF

    DATAEXCHA_STATE_RECEIVE_WAIT:
        fbSocketReceive( bExecute := FALSE );
        IF NOT fbSocketReceive.bBusy THEN
            IF NOT fbSocketReceive.bError THEN

                IF (fbSocketReceive.nRecBytes > 0) THEN(* bytes received *)

                    startPos 		:= cbReceived;(* rxBuffer array index of first data byte *)
                    endPos  		:= cbReceived + fbSocketReceive.nRecBytes  - 1;(* rxBuffer array index of last data byte *)
                    cbReceived 	:= cbReceived + fbSocketReceive.nRecBytes;(* calculate the number of received data bytes *)
                    cbFrame		:= 0;(* reset frame length *)

                    IF cbReceived < SIZEOF( sFromClient ) THEN(* no overflow *)

                        fbReceiveTON( IN := FALSE, PT := T#0s ); (* bytes received => increase the read (polling) speed *)
                        fbDisconnectTON( IN := FALSE, PT := PLCPRJ_RECEIVE_TIMEOUT );(* bytes received => disable timeout check *)

                        (* search for string end delimiter *)
                        FOR idx := startPos TO endPos BY 1 DO
                            IF rxBuffer[idx] = 0 THEN(* string end delimiter found *)
                                cbFrame := idx + 1;(* calculate the length of the received string (inclusive the end delimiter) *)
                                MEMCPY(  ADR( sFromClient ), ADR( rxBuffer ), cbFrame );(* copy the received string to the output variable (inclusive the end delimiter) *)
                                MEMMOVE( ADR( rxBuffer ), ADR( rxBuffer[cbFrame] ),  cbReceived - cbFrame  );(* move the reamaining data bytes *)
                                cbReceived := cbReceived - cbFrame;(* recalculate the reamaining data byte length *)
                                eStep := DATAEXCHA_STATE_SEND_START;
                                EXIT;
                            END_IF
                        END_FOR

                    ELSE(* there is no more free read buffer space => the answer string should be terminated *)
                        LogError( 'FB_SocketReceive (remote client)',  PLCPRJ_ERROR_RECEIVE_BUFFER_OVERFLOW );
                        nErrId := PLCPRJ_ERROR_RECEIVE_BUFFER_OVERFLOW;(* buffer overflow !*)
                        eStep := DATAEXCHA_STATE_ERROR;
                    END_IF

                ELSE(* no bytes received *)
                    fbReceiveTON( IN := FALSE, PT := PLCPRJ_RECEIVE_POLLING_TIME );(* no bytes received => decrease the read (polling) speed *)
                    fbDisconnectTON( IN := TRUE, PT := PLCPRJ_RECEIVE_TIMEOUT );(* no bytes received => enable timeout check*)
                    IF fbDisconnectTON.Q THEN (* timeout error*)
                        fbDisconnectTON( IN := FALSE );
                        LogError( 'FB_SocketReceive (remote client)',  PLCPRJ_ERROR_RECEIVE_TIMEOUT );
                        nErrID := PLCPRJ_ERROR_RECEIVE_TIMEOUT;
                        eStep := DATAEXCHA_STATE_ERROR;
                    ELSE(* repeat reading *)
                        eStep := DATAEXCHA_STATE_RECEIVE_START; (* repeat reading *)
                    END_IF
                END_IF
            ELSE(* receive error *)
                LogError( 'FB_SocketReceive (remote client)',  fbSocketReceive.nErrId );
                nErrId := fbSocketReceive.nErrId;
                eStep := DATAEXCHA_STATE_ERROR;
            END_IF
        END_IF

    DATAEXCHA_STATE_SEND_START:
        fbSocketSend( bExecute := FALSE );
        fbSocketSend(	hSocket	:= hSocket,
                        pSrc	:= ADR( sFromClient ),
                        cbLen	:= LEN( sFromClient ) + 1,(* string length inclusive the zero delimiter *)
                        bExecute:= TRUE );
        eStep := DATAEXCHA_STATE_SEND_WAIT;

    DATAEXCHA_STATE_SEND_WAIT:
        fbSocketSend( bExecute := FALSE );
        IF NOT fbSocketSend.bBusy THEN
            IF NOT fbSocketSend.bError THEN
                bBusy := FALSE;
                eStep := DATAEXCHA_STATE_IDLE;
            ELSE
                LogError( 'fbSocketSend (remote client)',  fbSocketSend.nErrId );
                nErrId := fbSocketSend.nErrId;
                eStep := DATAEXCHA_STATE_ERROR;
            END_IF
        END_IF

    DATAEXCHA_STATE_ERROR:
        bBusy := FALSE;
        bError := TRUE;
        cbReceived := 0;(* reset old received data bytes *)
        eStep := DATAEXCHA_STATE_IDLE;
END_CASE


.NET sample client

This project example shows how a client for the PLC TCP/IP server can be realised by writing a .NET4.0 application using C#.

This sample client makes use of the .NET libraries System.Net and System.Net.Sockets which enable a programmer easy access to socket functionalities. By pressing the button "Enable", the application attempts to cycically (depending on the value of TIMERTICK in [ms]) establish a connection with the server. If successful, a string with a maximum length of 255 characters can be sent to the server via the "Send" button. The server will then take this string and send it back to the client. On the server side, the connection is closed automatically if the server was unable to receive new data from the client within a defined period, as specified by PLCPRJ_RECEIVE_TIMEOUT in the server sample - by default 50 seconds.

      using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;

/* ########################################################################################## * This sample TCP/IP client connects to a TCP/IP-Server, sends a message and waits for the * response. It is being delivered together with our TCP-Sample, which implements an echo server * in PLC. * ########################################################################################## */namespace TcpIpServer_SampleClient
{
    publicpartialclassForm1 : Form
    {
        /* ##########################################################################################         * Constants         * ########################################################################################## */privateconstint RCVBUFFERSIZE = 256; // buffer size for receive bufferprivateconststring DEFAULTIP = "127.0.0.1";
        privateconststring DEFAULTPORT = "200";
        privateconstint TIMERTICK = 100;

        /* ##########################################################################################         * Global variables         * ########################################################################################## */privatestaticbool _isConnected; // signals whether socket connection is active or notprivatestaticSocket _socket; // object used for socket connection to TCP/IP-ServerprivatestaticIPEndPoint _ipAddress; // contains IP address as entered in text fieldprivatestaticbyte[] _rcvBuffer; // receive buffer used for receiving response from TCP/IP-Serverpublic Form1()
        {
            InitializeComponent();
        }

        privatevoid Form1_Load(object sender, EventArgs e)
        {
            _rcvBuffer = newbyte[RCVBUFFERSIZE];

            /* ##########################################################################################             * Prepare GUI             * ########################################################################################## */
            cmd_send.Enabled = false;
            cmd_enable.Enabled = true;
            cmd_disable.Enabled = false;
            rtb_rcvMsg.Enabled = false;
            rtb_sendMsg.Enabled = false;
            rtb_statMsg.Enabled = false;
            txt_host.Text = DEFAULTIP;
            txt_port.Text = DEFAULTPORT;

            timer1.Enabled = false;
            timer1.Interval = TIMERTICK;
            _isConnected = false;
        }

        privatevoid cmd_enable_Click(object sender, EventArgs e)
        {
            /* ##########################################################################################             * Parse IP address in text field, start background timer and prepare GUI             * ########################################################################################## */try
            {
                _ipAddress = newIPEndPoint(IPAddress.Parse(txt_host.Text), Convert.ToInt32(txt_port.Text));
                timer1.Enabled = true;
                cmd_enable.Enabled = false;
                cmd_disable.Enabled = true;
                rtb_sendMsg.Enabled = true;
                cmd_send.Enabled = true;
                txt_host.Enabled = false;
                txt_port.Enabled = false;
                rtb_sendMsg.Focus();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Could not parse entered IP address. Please check spelling and retry. " + ex);
            }
        }

        /* ##########################################################################################         * Timer periodically checks for connection to TCP/IP-Server and reestablishes if not connected         * ########################################################################################## */privatevoid timer1_Tick(object sender, EventArgs e)
        {
            if (!_isConnected)
                connect();
        }

        privatevoid connect()
        {
            /* ##########################################################################################             * Connect to TCP/IP-Server using the IP address specified in the text field             * ########################################################################################## */try
            {
                _socket = newSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
                _socket.Connect(_ipAddress);
                _isConnected = true;
                if (_socket.Connected)
                    rtb_statMsg.AppendText(DateTime.Now.ToString() + ": Connectection to host established!\n");
                else
                    rtb_statMsg.AppendText(DateTime.Now.ToString() + ": A connection to the host could not be established!\n");
            }
            catch (Exception ex)
            {
                MessageBox.Show("An error occured while establishing a connection to the server: " + ex);
            }
        }

        privatevoid cmd_send_Click(object sender, EventArgs e)
        {
            /* ##########################################################################################             * Read message from text field and prepare send buffer, which is a byte[] array. The last             * character in the buffer needs to be a termination character, so that the TCP/IP-Server knows             * when the TCP stream ends. In this case, the termination character is '0'.             * ########################################################################################## */ASCIIEncoding enc = newASCIIEncoding();
            byte[] tempBuffer = enc.GetBytes(rtb_sendMsg.Text);
            byte[] sendBuffer = newbyte[tempBuffer.Length + 1];
            for (int i = 0; i < tempBuffer.Length; i++)
                sendBuffer[i] = tempBuffer[i];
            sendBuffer[tempBuffer.Length] = 0;

            /* ##########################################################################################             * Send buffer content via TCP/IP connection             * ########################################################################################## */try
            {
                int send = _socket.Send(sendBuffer);
                if (send == 0)
                    thrownewException();
                else
                {
                    /* ##########################################################################################                     * As the TCP/IP-Server returns a message, receive this message and store content in receive buffer.                     * When message receive is complete, show the received message in text field.                     * ########################################################################################## */
                    rtb_statMsg.AppendText(DateTime.Now.ToString() + ": Message successfully sent!\n");
                    IAsyncResult asynRes = _socket.BeginReceive(_rcvBuffer, 0, 256, SocketFlags.None, null, null);
                    if (asynRes.AsyncWaitHandle.WaitOne())
                    {
                        int res = _socket.EndReceive(asynRes);
                        char[] resChars = newchar[res + 1];
                        Decoder d = Encoding.UTF8.GetDecoder();
                        int charLength = d.GetChars(_rcvBuffer, 0, res, resChars, 0, true);
                        String result = newString(resChars);
                        rtb_rcvMsg.AppendText("\n" + DateTime.Now.ToString() + ": " + result);
                        rtb_sendMsg.Clear();
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("An error occured while sending the message: " + ex);
            }
        }

        privatevoid cmd_disable_Click(object sender, EventArgs e)
        {
            /* ##########################################################################################             * Disconnect from TCP/IP-Server, stop the timer and prepare GUI             * ########################################################################################## */
            timer1.Enabled = false;
            _socket.Disconnect(true);
            if (!_socket.Connected)
            {
                _isConnected = false;
                cmd_disable.Enabled = false;
                cmd_enable.Enabled = true;
                txt_host.Enabled = true;
                txt_port.Enabled = true;
                rtb_sendMsg.Enabled = false;
                cmd_send.Enabled = false;
                rtb_statMsg.AppendText(DateTime.Now.ToString() + ": Connectection to host closed!\n");
                rtb_rcvMsg.Clear();
                rtb_statMsg.Clear();
            }
        }
    }
}


UDP example: Overview

The following example shows the implementation of a simple peer-to-peer application in the PLC. The PLC application presented can send a test string to a remote PC and at the same time receive test strings from a remote PC. The test strings are displayed in a message box on the monitor of the target computer. A simple implementation of a suitable communication partner in .NET is also presented. The example can be used as a basis for realising more complex implementations.

System requirements

  • TwinCAT v2.8 or higher. Level: TwinCAT PLC as a minimum.
  • Installed TwinCAT TCP/IP connection server (v1.0.0.31 or higher). If two PCs are used for the test, the TwinCAT TCP/IP Connection Server should be installed on both PCs.
  • TwinCAT PLC library TcpIp.Lib (v1.0.4 or higher).

Project sources

The sources of the two PLC devices only differ in terms of different IP addresses of the remote communication partners.

  • PLC project: peer-to-peer device A
  • PLC project: peer-to-peer device B
  • .NET: peer-to-peer-communication partner for PLC device A or B

Project description

  • peer-to-peer PLC application
  • .NET communication partner for the PLC
  • Testing the applications

Auxiliary functions in the project example

In the example several functions, constants and function blocks are used, which are briefly described below:

FUNCTION_BLOCK FB_Fifo
VAR_INPUT
        new     : ST_FifoEntry;
END_VAR
VAR_OUTPUT
        bOk     : BOOL;
        old     : ST_FifoEntry;
END_VAR

A simple Fifo function block. One instance of this block is used as "send Fifo", another one as "receive Fifo". The messages to be sent are stored in the send Fifo, the received messages are stored in the receive Fifo. The bOk output variable is set to FALSE if errors occurred during the last action (AddTail or RemoveHead) (Fifo empty or overfilled).

A Fifo entry consists of the following components:

TYPE ST_FifoEntry :
STRUCT
        sRemoteHost : STRING(15);       (* Remote address. String containing an (Ipv4) Internet Protocol dotted address. *)
 nRemotePort : UDINT;    (* Remote Internet Protocol (IP) port. *)
 msg : STRING;           (* Udp packet data *)
END_STRUCT
END_TYPE

FUNCTION LogError : DINT

The function writes a message with the error code into the log book of the operating system (Event Viewer). The global variable bLogDebugMessages must first be set to TRUE.

FUNCTION LogMessage : DINT

The function writes a message into the log book of the operating system (Event Viewer) if a new socket was opened or closed. The global variable bLogDebugMessages must first be set to TRUE.

FUNCTION SCODE_CODE : DWORD

The function masks the lower 16bits of a Win32 error code returns them.

Global constants/variables

Name | Default value | Description
g_sTcIpConnSvrAddr | '' | Network address of the TwinCAT TCP/IP Connection Server. Default: Empty string (the server is located on the local PC);
bLogDebugMessages | TRUE | Activates/deactivates writing of messages into the log book of the operating system;
PLCPRJ_ERROR_SENDFIFO_OVERFLOW | 16#8103 | Sample project error code: The send Fifo is full.
PLCPRJ_ERROR_RECFIFO_OVERFLOW | 16#8104 | Sample project error code: The receive Fifo is full.


Peer-To-Peer: Test

TS6310 | TwinCAT PLC Lib: TCP/IP

UDP example: Testing the peer-to-peer applications

The test requires two PCs. Alternatively, the test may be carried out with two runtime systems on a single PC. The constants with the port numbers and the IP addresses of the communication partners have to be modified accordingly.

Example test configuration with 2 PCs:

Device A is located on the local PC and has the IP address '172.16.2.209'

Device B is located on the remote PC and has the IP address '172.16.6.195'

Testing the PLC devices

1. Open the peer-to-peer PLC project for device A on the local PC. The constant REMOTE_HOST_IP in MAIN has to be adapted to the real IP address of your remote system (in our example: '172.16.6.195'). Load the project into the PLC runtime system. Start the PLC.

2. Local PC: In online mode, write the value TRUE to the boolean variable bSendOnceToItself in MAIN. Shortly afterwards, a message box with the test string should appear. The UDP data were sent to the local port and IP address.

3. Remote PC: Open the peer-to-peer PLC project for device B on the remote PC. The constant REMOTE_HOST_IP in MAIN has to be adapted to the IP address of your local PC (in our example: '172.16.2.209'). Load the project into the PLC runtime system. Start the PLC.

4. Remote PC: In online mode, write the value TRUE to the boolean variable bSendOnceToRemote in MAIN. Shortly afterwards, a message box with the test string should appear on the local PC.

Test with the Visual Basic application

Here you can unpack the Visual Basic sources: VB communication partner for the PLC.

5. Local PC: Start the Visual Basic application (PeerToPeer.exe).

6. Local PC: In the VB dialog, the IP address of the remote host has to be adapted to the real IP address of the remote PC (in our example '172.16.6.195' ).

7. Local PC: Click the send button. A test string is sent to the remote device with port number 1001. In our case it is the PLC application.

8. Remote PC: Shortly afterwards, a message box with the test string should appear.


Peer-To-Peer: SPS client/server

TS6310 | TwinCAT PLC Lib: TCP/IP

UDP example: peer-to-peer PLC devices A and B

Here you can unpack the complete sources: peer-to-peer communication partner A, and peer-to-peer communication partner B.

The required functionality was encapsulated in the function block FB_PeerToPeer. Each of the communication partners uses an instance of the FB_PeerToPeer function block. The block is activated through a rising edge at the bEnable input. A new UDP socket is opened, and data exchange commences. The socket address is specified via the variables sLocalHost and nLocalPort. A falling edge stops the data exchange and closes the socket. The data to be sent are transferred to the block through a reference (VAR_IN_OUT) via the variable sendFifo. The data received are stored in the variable receiveFifo.

FUNCTION_BLOCK FB_PeerToPeer

Interface

VAR_IN_OUT
        sendFifo                : FB_Fifo;
        receiveFifo     : FB_Fifo;
END_VAR
VAR_INPUT
        sLocalHost      : STRING(15);
        nLocalPort      : UDINT;
        bEnable                 : BOOL;
END_VAR
VAR_OUTPUT
        bCreated                : BOOL;
        bBusy           : BOOL;
        bError          : BOOL;
        nErrId          : UDINT;
END_VAR
VAR
        fbCreate                : FB_SocketUdpCreate;
        fbClose         : FB_SocketClose;
        fbReceiveFrom   : FB_SocketUdpReceiveFrom;
        fbSendTo                : FB_SocketUdpSendTo;
        hSocket                 : T_HSOCKET;
        eStep           : E_ClientServerSteps;
        sendTo          : ST_FifoEntry;
        receivedFrom    : ST_FifoEntry;
END_VAR

Implementation

CASE eStep OF
        UDP_STATE_IDLE:
                IF bEnable XOR bCreated THEN
                        bBusy   := TRUE;
                        bError  := FALSE;
                        nErrid  := 0;
                        IF bEnable THEN
                                eStep := UDP_STATE_CREATE_START;
                        ELSE
                                eStep := UDP_STATE_CLOSE_START;
                        END_IF
                ELSIF bCreated THEN
                        sendFifo.RemoveHead( old => sendTo );
                        IF sendFifo.bOk THEN
                                eStep := UDP_STATE_SEND_START;
                        ELSE (* empty *)
                                eStep := UDP_STATE_RECEIVE_START;
                        END_IF
                ELSE
                        bBusy := FALSE;
                END_IF

        UDP_STATE_CREATE_START:
                fbCreate(  bExecute := FALSE  );
                fbCreate(       sSrvNetId:= g_sTcIpConnSvrAddr,
                        sLocalHost:= sLocalHost,
                        nLocalPort:= nLocalPort,
                        bExecute:= TRUE );
                eStep := UDP_STATE_CREATE_WAIT;

        UDP_STATE_CREATE_WAIT:
                fbCreate( bExecute := FALSE );
                IF NOT fbCreate.bBusy THEN
                        IF NOT fbCreate.bError THEN
                                bCreated := TRUE;
                                hSocket := fbCreate.hSocket;
                                eStep := UDP_STATE_IDLE;
                                LogMessage( 'Socket opened (UDP)!', hSocket );
                        ELSE
                                LogError( 'FB_SocketUdpCreate', fbCreate.nErrId );
                                nErrId := fbCreate.nErrId;
                                eStep := UDP_STATE_ERROR;
                        END_IF
                END_IF

        UDP_STATE_SEND_START:
                fbSendTo( bExecute := FALSE );
                fbSendTo(       sSrvNetId:=g_sTcIpConnSvrAddr,
                        sRemoteHost := sendTo.sRemoteHost,
                        nRemotePort := sendTo.nRemotePort,
                        hSocket:= hSocket,
                        pSrc:= ADR( sendTo.msg ),
                        cbLen:= LEN( sendTo.msg ) + 1, (* include the end delimiter *)
                 bExecute:= TRUE );
                eStep := UDP_STATE_SEND_WAIT;

        UDP_STATE_SEND_WAIT:
                fbSendTo( bExecute := FALSE );
                IF NOT fbSendTo.bBusy THEN
                        IF NOT fbSendTo.bError THEN
                                eStep := UDP_STATE_RECEIVE_START;
                        ELSE
                                LogError( 'FB_SocketSendTo (UDP)', fbSendTo.nErrId );
                                nErrId := fbSendTo.nErrId;
                                eStep := UDP_STATE_ERROR;
                        END_IF
                END_IF

        UDP_STATE_RECEIVE_START:
                MEMSET( ADR( receivedFrom ), 0, SIZEOF( receivedFrom ) );
           fbReceiveFrom( bExecute := FALSE );
                fbReceiveFrom(  sSrvNetId:=g_sTcIpConnSvrAddr,
                                hSocket:= hSocket,
                                pDest:= ADR( receivedFrom.msg ),
                                cbLen:= SIZEOF( receivedFrom.msg ) - 1, (*without string delimiter *)
                         bExecute:= TRUE );
                eStep := UDP_STATE_RECEIVE_WAIT;

        UDP_STATE_RECEIVE_WAIT:
                fbReceiveFrom( bExecute := FALSE );
                IF NOT fbReceiveFrom.bBusy THEN
                        IF NOT fbReceiveFrom.bError THEN
                                IF fbReceiveFrom.nRecBytes > 0 THEN
                                        receivedFrom.nRemotePort := fbReceiveFrom.nRemotePort;
                                        receivedFrom.sRemoteHost := fbReceiveFrom.sRemoteHost;
                                        receiveFifo.AddTail( new := receivedFrom );
                                        IF NOT receiveFifo.bOk THEN(* Check for fifo overflow *)
                                         LogError( 'Receive fifo overflow!', PLCPRJ_ERROR_RECFIFO_OVERFLOW );
                                        END_IF
                                END_IF
                                eStep := UDP_STATE_IDLE;
                        ELSIF fbReceiveFrom.nErrId = 16#80072746 THEN
                                LogError( 'The connection is reset by remote side.', fbReceiveFrom.nErrId );
                                eStep := UDP_STATE_IDLE;
                        ELSE
                                LogError( 'FB_SocketUdpReceiveFrom (UDP client/server)', fbReceiveFrom.nErrId );
                                nErrId := fbReceiveFrom.nErrId;
                                eStep := UDP_STATE_ERROR;
                        END_IF
                END_IF

        UDP_STATE_CLOSE_START:
                fbClose( bExecute := FALSE );
                fbClose(        sSrvNetId:= g_sTcIpConnSvrAddr,
                        hSocket:= hSocket,
                        bExecute:= TRUE );
                eStep := UDP_STATE_CLOSE_WAIT;

        UDP_STATE_CLOSE_WAIT:
                fbClose( bExecute := FALSE );
                IF NOT fbClose.bBusy THEN
                        LogMessage( 'Socket closed (UDP)!', hSocket );
                        bCreated := FALSE;
                        MEMSET( ADR(hSocket), 0, SIZEOF(hSocket));
                        IF fbClose.bError THEN
                                LogError( 'FB_SocketClose (UDP)', fbClose.nErrId );
                                nErrId := fbClose.nErrId;
                                eStep := UDP_STATE_ERROR;
                        ELSE
                                bBusy := FALSE;
                                bError := FALSE;
                                nErrId := 0;
                                eStep := UDP_STATE_IDLE;
                        END_IF
                END_IF

        UDP_STATE_ERROR: (* Error step *)
                bError := TRUE;
                IF bCreated THEN
                        eStep := UDP_STATE_CLOSE_START;
                ELSE
                        bBusy := FALSE;
                        eStep := UDP_STATE_IDLE;
                END_IF
END_CASE

MAIN program

Previously opened sockets must be closed after a program download or a PLC reset. During PLC start-up, this is done by calling an instance of the FB_SocketCloseAll function block. If one of the variables bSendOnceToItself or bSendOnceToRemote has an raising edge, a new Fifo entry is generated and stored in the send Fifo. Received messages are removed from the receive Fifo and displayed in a message box.

PROGRAM MAIN
VAR CONSTANT
        LOCAL_HOST_IP           : STRING(15)            := '';
        LOCAL_HOST_PORT         : UDINT                 := 1001;
        REMOTE_HOST_IP          : STRING(15)            := '172.16.2.209';
        REMOTE_HOST_PORT                : UDINT                         := 1001;
END_VAR
VAR
        fbSocketCloseAll                : FB_SocketCloseAll;
        bCloseAll               : BOOL := TRUE;

        fbPeerToPeer            : FB_PeerToPeer;
        sendFifo                        : FB_Fifo;
        receiveFifo             : FB_Fifo;
        sendToEntry             : ST_FifoEntry;
        entryReceivedFrom               : ST_FifoEntry;
        tmp                     : STRING;

        bSendOnceToItself               : BOOL;
        bSendOnceToRemote               : BOOL;
END_VAR

IF bCloseAll THEN (*On PLC reset or program download close all old connections *)
 bCloseAll := FALSE;
        fbSocketCloseAll( sSrvNetId:= g_sTcIpConnSvrAddr, bExecute:= TRUE, tTimeout:= T#10s );
ELSE
        fbSocketCloseAll( bExecute:= FALSE );
END_IF

IF NOT fbSocketCloseAll.bBusy AND NOT fbSocketCloseAll.bError THEN

        IF bSendOnceToRemote THEN
                bSendOnceToRemote                       := FALSE;                                       (* clear flag *)
         sendToEntry.nRemotePort                 := REMOTE_HOST_PORT;                    (* remote host port number*)
         sendToEntry.sRemoteHost                 := REMOTE_HOST_IP;                              (* remote host IP address *)
         sendToEntry.msg                         := 'Hello remote host!';                        (* message text*);
         sendFifo.AddTail( new := sendToEntry );                                 (* add new entry to the send queue*)
         IF NOT sendFifo.bOk THEN                                                        (* check for fifo overflow*)
                 LogError( 'Send fifo overflow!', PLCPRJ_ERROR_SENDFIFO_OVERFLOW );
                END_IF
        END_IF

        IF bSendOnceToItself THEN
                bSendOnceToItself                       := FALSE;                                       (* clear flag *)
         sendToEntry.nRemotePort                 := LOCAL_HOST_PORT;                     (* nRemotePort == nLocalPort => send it to itself *)
         sendToEntry.sRemoteHost                 := LOCAL_HOST_IP;                               (* sRemoteHost == sLocalHost =>send it to itself *)
         sendToEntry.msg                         := 'Hello itself!';                     (* message text*);
         sendFifo.AddTail( new := sendToEntry );                                 (* add new entry to the send queue*)
         IF NOT sendFifo.bOk THEN                                                        (* check for fifo overflow*)
                 LogError( 'Send fifo overflow!', PLCPRJ_ERROR_SENDFIFO_OVERFLOW );
                END_IF
        END_IF

  (* send and receive messages *)
 fbPeerToPeer( sendFifo := sendFifo, receiveFifo := receiveFifo, sLocalHost := LOCAL_HOST_IP, nLocalPort := LOCAL_HOST_PORT, bEnable := TRUE );

  (* remove all received messages  from receive queue *)
 REPEAT
                receiveFifo.RemoveHead( old => entryReceivedFrom );
                IF receiveFifo.bOk THEN
                        tmp := CONCAT( 'RECEIVED from: ', entryReceivedFrom.sRemoteHost  );
                        tmp := CONCAT( tmp, ', Port: ' );
                        tmp := CONCAT( tmp, UDINT_TO_STRING( entryReceivedFrom.nRemotePort ) );
                        tmp := CONCAT( tmp, ', msg: %s' );
                        ADSLOGSTR(  ADSLOG_MSGTYPE_HINT OR ADSLOG_MSGTYPE_MSGBOX, tmp, entryReceivedFrom.msg );
                END_IF
        UNTIL NOT receiveFifo.bOk
        END_REPEAT

END_IF


UDP sample: .NET Peer-to-Peer communication

This sample demonstrates how a .NET communication partner for PLC samples Peer-to-Peer device A or B can be realised.

How it works

The sample uses the includes System.Net and System.Net.Sockets to implement a UDP client (class UdpClient). While listening for incoming UDP packets in a background thread, a string can be sent to a remote device by specifying its IP address and port number and clicking the "Send" button.

For a better understanding of this article, imagine the following setup:

  • The PLC project Peer-to-Peer device A is running on a computer with IP address 10.1.128.21
  • The .NET application is running on a computer with IP address 10.1.128.30

How to set up the PLC sample

This .NET sample may be used together with the PLC samples Peer-to-Peer device A or B. If you run the application on separate computer than the PLC runtime, you need to configure IP addresses in both applications according to the assumed specifications from above.

Setting | Type | Description
LOCAL_HOST_IP | Global constant | Needs to be set to 10.1.128.21 (IP of computer which runs PLC)
LOCAL_HOST_PORT | Global constant | Set this constant to 1001 (default value)
REMOTE_HOST_IP | Global constant | Needs to be set to 10.1.128.30 (IP of computer which runs .NET sample)
REMOTE_HOST_PORT | Global constant | Set this constant to 1002 (default value)
bSendOnceToRemote | Global variable | If set to TRUE, a UDP packet will be send to REMOTE_HOST_IP

How to set up the .NET sample

Setting | Type | Description
DEFAULTIP | Global constant | Will be used in text field "Host" as default value. Set it to 10.1.128.21.
DEFAULTDESTPORT | Global constant | Will be used in text field "Port" as default value. Set it to 1001.
DEFAULTOWNPORT | Global constant | Will be used by PLC sample in REMOTE_HOST_PORT. Set it to 1002.
DEFAULTSOURCEPORT | Global constant | Used as source port for sending out UDP packet. Not important in this sample.

      using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace TcpIpServer_SampleClientUdp
{
    publicpartialclassForm1 : Form
    {
        /* ##########################################################################################         * Constants         * ########################################################################################## */privateconststring DEFAULTIP = "127.0.0.1";
        privateconstint DEFAULTDESTPORT = 1001; // Used as destination portprivateconstint DEFAULTOWNPORT = 1002; // Used for binding when listening for UDP messagesprivateconstint DEFAULTSOURCEPORT = 11000; // Only used as source port when sending UDP messages/* ##########################################################################################         * Global variables         * ########################################################################################## */privatestaticUdpClient _udpClient;
        privatestaticIPEndPoint _ipAddress; // Contains IP address as entered in text fieldprivatestaticThread _rcvThread; // Background thread used to listen for incoming UDP packetspublic Form1()
        {
            InitializeComponent();
        }

        /* ##########################################################################################         * Event handler method called when button "Send" is pressed         * ########################################################################################## */privatevoid cmd_send_Click(object sender, EventArgs e)
        {
            byte[] sendBuffer = null;

            /* ##########################################################################################             * Preparing UdpClient, connecting to UDP server and sending content of text field             * ########################################################################################## */try
            {
                _ipAddress = newIPEndPoint(IPAddress.Parse(txt_host.Text), Convert.ToInt32(txt_port.Text));
                _udpClient = newUdpClient(DEFAULTSOURCEPORT);
                _udpClient.Connect(_ipAddress);

                sendBuffer = Encoding.ASCII.GetBytes(txt_send.Text);
                _udpClient.Send(sendBuffer, sendBuffer.Length);

                _udpClient.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show("An unknown error occured: " + ex);
            }
        }

        /* ##########################################################################################         * Event handler method called when application starts         * ########################################################################################## */privatevoid Form1_Load(object sender, EventArgs e)
        {
            txt_host.Text = DEFAULTIP;
            txt_port.Text = DEFAULTDESTPORT.ToString();
            rtb_rcv.Enabled = false;

            /* ##########################################################################################             * Creating background thread which synchronously listens for incoming UDP packets             * ########################################################################################## */
            _rcvThread = newThread(rcvThreadMethod);
            _rcvThread.Start();
        }

        /* ##########################################################################################         * Delegate, so that background thread may write into text field on GUI         * ########################################################################################## */publicdelegatevoidrcvThreadCallback(string text);

        /* ##########################################################################################         * Method called by background thread         * ########################################################################################## */privatevoid rcvThreadMethod()
        {
            /* ##########################################################################################             * Listen on any available local IP address and specified port (DEFAULTOWNPORT)             * ########################################################################################## */byte[] rcvBuffer = null;
            IPEndPoint ipEndPoint = newIPEndPoint(IPAddress.Any, DEFAULTOWNPORT);;
            UdpClient udpClient = newUdpClient(ipEndPoint);

            /* ##########################################################################################             * Continously start a synchronous listen for incoming UDP packets. If a packet has arrived,             * write its content to receive buffer and then into the text field. After that, start circle             * again.             * ########################################################################################## */while (true)
            {
                rcvBuffer = udpClient.Receive(ref ipEndPoint); // synchronous call
                rtb_rcv.Invoke(newrcvThreadCallback(this.AppendText), newobject[] { "\n" + DateTime.Now.ToString() + ": " + Encoding.ASCII.GetString(rcvBuffer) });
            }
        }

        /* ##########################################################################################         * Helper method for delegate         * ########################################################################################## */privatevoid AppendText(string text)
        {
            rtb_rcv.AppendText(text);
        }

        /* ##########################################################################################         * Stop background thread when application closes         * ########################################################################################## */privatevoid Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            _rcvThread.Abort();
        }
    }
}


Examples

The examples presented here are based on the functionality offered by TcSocketHelper.Lib.

System requirements:

  • TwinCAT version 2.10 Build 1331 (or higher)
  • TwinCAT Connection Server v1.0.0.47 or higher installed on the client and server PC;

Communication settings used in the examples:

  • PLC client application: Port and IP address of the remote server: 200, "127.0.0.1";
  • PLC server application: Port and IP address of the local server: 200, "127.0.0.1";

To test the client and server application on two different PCs, you have to adjust the port address and the IP address accordingly (use the PING command in the prompt to check the connection).

You can test the client and server with the default values on one PC by loading the client application into the first PLC runtime system (801) and the server application into the second PLC runtime system (811).

The behaviour of the PLC project example is determined by the following constants.

Constant | Value | Description
PLCPRJ_MAX_CONNECTIONS | 5 | Max. number of server->client connections. A server can establish connections to more than one client. A client can establish a connection to only one server at a time.
PLCPRJ_SERVER_RESPONSE_TIMEOUT | T#10s | Max. delay time (timeout time) after which a server should send a response to the client.
PLCPRJ_CLIENT_SEND_CYCLE_TIME | T#1s | Cycle time based on which a client sends send data (TX) to the server.
PLCPRJ_RECEIVER_POLLING_CYCLE_TIME | T#200ms | Cycle time based on which a client or server polls for receive data (RX).
PLCPRJ_BUFFER_SIZE | 10000 | Max. internal buffer size for RX/TX data.

The PLC examples define and use the following internal error codes:

Error code | Value | Description
PLCPRJ_ERROR_RECEIVE_BUFFER_OVERFLOW | 16#8101 | The internal receive buffer reports an overflow.
PLCPRJ_ERROR_SEND_BUFFER_OVERFLOW | 16#8102 | The internal send buffer reports an overflow.
PLCPRJ_ERROR_RESPONSE_TIMEOUT | 16#8103 | The server has not sent the response within the specified timeout time.
PLCPRJ_ERROR_INVALID_FRAME_FORMAT | 16#8104 | The telegram formatting is incorrect (size, faulty data bytes etc.).

PLC project Description
TcSocketHelper_SingleClientString.pro (Client)

TcSocketHelper_SingleServerString.pro (Server)

Implementation of an "echo" client/server. The client cyclically sends a test string (sToServer) to the remote server. The server returns the same string to the client unchanged (sFromServer).
TcSocketHelper_MultiClientString.pro (Client)

TcSocketHelper_MultiServerString.pro (Server)

As above, with the difference that the server can establish up to 5 connections. The client application has 5 client instances. Each instance establishes a connection to the server.
TcSocketHelper_SingleClientBinary.pro (Client)

TcSocketHelper_SingleServerBinary.pro (Server)

A client-server application for the exchanging of binary data.

A simple sample protocol is used. The length of the binary data and a frame counter for the sent and received telegrams are transferred in the protocol header.

The structure of the binary data is defined by the PLC structure ST_ApplicationBinaryData. The binary data are appended to the headers and transferred. The instances of the binary structure are called toServer, fromServer on the client side and toClient, fromClient on the server side.

The structure declaration on the client and server sides can be adapted as required. The structure declaration must be identical on both sides.

The maximum size of the structure must not exceed the maximum buffer size of the send/receive Fifos. The maximum buffer size is determined by a constant.

The server functionality is implemented in the function block FB_ServerApplication and the client functionality in the function block FB_ClientApplication.

In the standard implementation the client cyclically sends the data of the binary structure to the server and waits for a response from the server. The server modifies some data and returns them to the client.

If you require a functionality, you have to modify the function blocks FB_ServerApplication and FB_ClientApplication accordingly.

TcSocketHelper_MultiClientBinary.pro (Client)

TcSocketHelper_MultiServerBinary.pro (Server)

As above, with the difference that the server can establish up to 5 connections. The client application has 5 client instances. Each instance establishes a connection to the server.

The client and server applications (FB_ServerApplication, FB_ClientApplication) were implemented as function blocks. The application and the connection can thus be instanced repeatedly.

For troubleshooting purposes you can set the input variable bDbg to TRUE, thereby activating the debugging output for the sent data in the TwinCAT System Manager Log View:


Beispiel: Trap senden ohne variable binding

TwinCAT PLC Examples

Sample: Client trap

This sample describes a simple Trap send from a PLC to a SNMP management server. Traps can be used to alert tresholds. On every hundred increment of the counter a trap will be send.

Download: PLC Project

Variable Declaration

PROGRAM MAIN
VAR
    SendTrap:   	FB_SendTrap;
    iState: 		INT := 0;
    iCounter: 	UDINT:= 0;
    stVarBind:	ST_SNMP_VariableBinding;
END_VAR

PLC Program

CASE iState OF

    0: (* send trap on every hundred cycle *)
        iCounter := iCounter + 1;
        IF ((iCounter MOD 100) = 0) THEN
            iState := 10;
        END_IF

    10: (* enable FB *)
        SendTrap.bEnable := TRUE;
        IF SendTrap.bEnabled THEN
            iState := 20;
        END_IF

    20: (* set SNMP trap parameter *)
        stVarBind.iType := E_SNMP_INTEGER;
        stVarBind.iLength := SIZEOF(iCounter);
        stVarBind.pArrValue := ADR(iCounter);

        (* TODO: assign object ID of management information base (MIB) *)
        SendTrap.sObjectID := '1.3.6.1.2.1.1.5.0';

        (* TODO: check default community string *)
        SendTrap.sCommunity:= 'public';
        SendTrap.iGenericTrapNumber:= E_SNMP_WarmStart;
        SendTrap.bExecute := TRUE;
        iState := 30;

    30: (* reset *)
        SendTrap.bExecute := FALSE;
        SendTrap.bEnable := FALSE;
        IF NOT SendTrap.bBusy THEN
            iState := 0;
        END_IF

        IF SendTrap.bError THEN
            SendTrap.bEnable := FALSE;
            iState := 99;
        END_IF

    99:(* Error case *)
        ;
END_CASE

SendTrap(
    (* TODO: add device IP of TwinCAT device *)
        sLocalHostIp := '',
    (* TODO: add SNMP Manager IP *)
        sManagerIP := ''
    );
Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcSnmp.lib ( TcpIp.Lib; Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


Beispiel: Trap mit mehrfachen variable bindings

TwinCAT PLC Examples

Sample: SNMP multiple client trap

This Sample describes the sending of a trap by multiple values. Traps can be used to alert tresholds. On every hundred increment of the counter a trap will be send.

Download: PLC Project

Variablen Declaration

PROGRAM MAIN
VAR
    SendTrap:    FB_SendTrap;
    iState: 		INT := 0;
    iCounter: 	UINT:= 0;
    sObjectId: 	STRING :='1.3.6.1.4.1.2';
    sMessage: 	T_MaxString:='Information string';
    iGauge: 		UDINT:= 4294967295;
    iTimeTicks: 	UDINT:= 1000000;
    arrVarBind: 	ARRAY[1..5] OF ST_SNMP_VariableBinding;
END_VAR

PLC Program

CASE iState OF

    0: (* send trap on every hundred cycle *)
        iCounter := iCounter + 1;
        IF ((iCounter MOD 100) = 0) THEN
            iState := 10;
        END_IF

    10:(* enable FB *)
        SendTrap.bEnable := TRUE;
        IF SendTrap.bEnabled THEN
            iState := 20;
        END_IF

    20: (* set SNMP trap parameter *)
        arrVarBind[1].iType := E_SNMP_INTEGER;
        arrVarBind[1].iLength := 4;
        arrVarBind[1].pArrValue := ADR(iCounter);
        arrVarBind[1].sOID := '1.3.1.3.255.1.1';

        (*Variable Binding 2*)
        arrVarBind[2].iType := E_SNMP_OBJECTID;
        arrVarBind[2].iLength := LEN(sObjectId);
        arrVarBind[2].pArrValue: = ADR(sObjectId);
        arrVarBind[2].sOID:= '1.3.6.1.3.255.2';

        (*Variable Binding 3*)
        arrVarBind[3].iType := E_SNMP_OCTETSTRING;
        arrVarBind[3].iLength := LEN(sMessage);
        arrVarBind[3].pArrValue := ADR(sMessage);
        arrVarBind[3].sOID := '1.3.6.1.3.255.3';

        (*Variable Binding 4*)
        arrVarBind[4].iType := E_SNMP_GAUGE32;
        arrVarBind[4].iLength := 4;
        arrVarBind[4].pArrValue := ADR(iGauge);
        arrVarBind[4].sOID := '1.3.6.1.3.255.4';

        (*Variable Binding 5*)
        arrVarBind[5].iType := E_SNMP_TIMETICKS;
        arrVarBind[5].iLength := 4;
        arrVarBind[5].pArrValue := ADR(iTimeTicks);
        arrVarBind[5].sOID := '1.3.6.1.3.255.5';

        SendTrap.sCommunity := 'public';
        SendTrap.iGenericTrapNumber := E_SNMP_WarmStart;
        SendTrap.bExecute := TRUE;
        iState := 30;

    30:	(* reset *)
        SendTrap.bExecute := FALSE;
        SendTrap.bEnable := FALSE;
        IF NOT SendTrap.bBusy THEN
            iState := 0;
        END_IF

        IF SendTrap.bError THEN
            SendTrap.bEnable := FALSE;
            iState := 99;
        END_IF

    99:	(* Error case *)
        ;
END_CASE

SendTrap(
        (* TODO: add device IP of TwinCAT device *)
            sLocalHostIp := '',
        (* TODO: add SNMP Manager IP *)
            sManagerIP := '',
            pArrVarBinding := ADR(arrVarBind),
            nVarBindings := 5
    );

Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcSnmp.lib ( TcpIp.Lib; Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


Beispiel: SNMP Get Anfrage

TwinCAT PLC Examples

Sample: SNMP Get request

This samples describes how a TwinCAT device can answer on a SNMP GET-request from SNMP manager. If a GET-Request arrived in the PLC, the requested information will be sent if the object id match.

Download: PLC Project

Variablen Declaration

PROGRAM MAIN
VAR
    GetSnmp:    FB_GetSnmp;
    iValue:     DINT;
       sValue:     T_MaxString;
        arrVarBind: ARRAY[0..1] OF ST_SNMP_VariableBinding;
    iState : INT := 0;
END_VAR

PLC Program

CASE iState OF
    0:(* Enable *)
        GetSnmp.bEnable := TRUE;
        IF GetSnmp.bEnabled THEN
            iState := 10;
        END_IF

    10:(* Wait for SNMP GET request *)
        GetSnmp.bReceive := TRUE;
        IF GetSnmp.bReceived THEN
            GetSnmp.bReceive := FALSE;
            iState := 20;
        END_IF

    20: (* Compare oid's *)

        (* TODO: Assign your own ObjectID (oid) of the management information base (MIB) *)
        IF GetSnmp.sRecObjectID = '1.3.6.1.2.1.1.5.0' THEN
            sValue := 'BECKHOFF_DEVICE';
            arrVarBind[0].iType := E_SNMP_OCTETSTRING;
            arrVarBind[0].iLength :=LEN(sValue);
            arrVarBind[0].pArrValue := ADR(sValue);
            arrVarBind[0].sOID := GetSnmp.sRecObjectID;
            GetSnmp.pArrVarBinding := ADR(arrVarBind);
            GetSnmp.nVarBindings := 1;
            GetSnmp.iError := 0;
            GetSnmp.bSendResponse := TRUE;
        ELSE
        (* The requested ObjectID  was not found *)
            GetSnmp.nVarBindings := 0;
            GetSnmp.iError := 2;
            GetSnmp.bSendResponse := TRUE;
        END_IF
        iState := 30;

    30: (* reset *)
        GetSnmp.bSendResponse := FALSE;
        GetSnmp.bSendTrap := FALSE;
        IF NOT GetSnmp.bBusy THEN
            iState := 10;
        END_IF
        IF GetSnmp.bError THEN
            GetSnmp.bEnable := FALSE;
            iState := 0;
        END_IF
END_CASE

GetSnmp(
    (* TODO: check community string *)
        sCommunity := 'public',
            iGenericTrapNumber:= E_SNMP_WarmStart,
    (* TODO: add device IP of TwinCAT device *)
        sLocalHostIp := '',
    (* use SNMP Port 163, if default port 161 is in use by OS *)
        sLocalHostPort := 163,
    (* TODO: ADD SNMP Manager IP *)
        sManagerIP := '',
    );
Development environment | Target system type | PLC libraries to be linked
TwinCAT version 2.8.0 or higher | PC or CX (x86) | TcSnmp.lib ( TcpIp.Lib; Standard.Lib; TcBase.Lib; TcSystem.Lib are included automatically )
TwinCAT v2.10.0 Build >= 1301 | CX (ARM)


Übersicht

TS6310 | TwinCAT PLC Lib: TCP/IP

Overview of the error codes

Codes (hex) | Codes (dec) | Error source | Description
0x00000000-0x00007800 | 0-30720 | TwinCAT system error codes | TwinCAT system error (including ADS error codes)
0x00008000-0x000080FF | 32768-33023 | Internal TwinCAT TCP/IP Connection Server error codes | Internal error of the TwinCAT TCP/IP Connection Server
0x00009000-0x000090FF | 36864-37119 | Internal SNMP error codes | Internal SNMP error codes
0x80070000-0x8007FFFF | 2147942400-2148007935 | Error source = Code - 0x80070000 = Win32 system error codes | Win32 system error (including Windows sockets error codes)


Internal TCP/IP Connection Server error codes

TS6310 | TwinCAT PLC Lib: TCP/IP

Internal error codes of the TwinCAT TCP/IP Connection Server

Code (hex) | Code (dec) | Symbolic constant | Description
0x00008001 | 32769 | TCPADSERROR_NOMOREENTRIES | No new sockets can be created (for FB_SocketListen and FB_SocketConnect).
0x00008002 | 32770 | TCPADSERROR_NOTFOUND | Socket handle is invalid (for FB_SocketReceive, FB_SocketAccept, FB_SocketSend etc.).
0x00008003 | 32771 | TCPADSERROR_ALREADYEXISTS | Is returned when FB_SocketListen is called, if the TcpIp port listener already exists.
0x00008004 | 32772 | TCPADSERROR_NOTCONNECTED | Is returned when FB_SocketReceive is called, if the client socket is no longer connected with the server.
0x00008005 | 32773 | TCPADSERROR_NOTLISTENING | Is returned when FB_SocketAccept is called, if an error was registered in the listener socket.


Troubleshooting

TwinCAT TCP/IP Connection Server - Reference

Troubleshooting/diagnostics

1. In the event of connection problems the PING command can be used to ascertain whether the external communication partner can be reached via the network connection. If this is not the case, check the network configuration and firewall settings.

2. Sniffer tools such as Wireshark enable logging of the entire network communication. The log can then be analysed by Beckhoff support staff.

3. Check the hardware and software requirements described in this documentation (TwinCAT version, CE image version etc.).

4. Check the software installation hints described in this documentation (e.g. installation of CAB files on CE plattform).

5. Check the input parameters that are transferred to the function blocks (network address, port number, data etc, connection handle.) for correctness. Check whether the function block issues an error code. The documentation for the error codes can be found here: Overview of error codes.

6. Check if the other communication partner/software/device issues an error code.

7. Activate the debug output integrated in the TcSocketHelper.Lib during connection establishment/disconnect process (keyword: CONNECT_MODE_ENABLEDBG). Open the TwinCAT System Manager and activate the LogView window. Analyze/check the debug output strings.


TYPE E_SNMP_ErrorCodes

TwinCAT PLC Library: SNMP

SNMP_ErrorCodes

Hex | Dec | Description
0x9001 | 36865 | INCORRECT PARAMETER SIZE
0x9002 | 36866 | INVALID PARAMETER


Visual Basic Client

TwinCAT PLC Examples

Visual Basic client project

This project example shows how a suitable VB client for the PLC TCP/IP server can be realised (FB_LocalServer).

Here you can unpack the complete VB source of the client: Example 1: VB-Client;

Winsock control is used in the example. After the application has been started select button Enable, The application attempts to established a connection with the server cyclically. If successful, a string with a maximum length of 255 characters can be sent to the server via the send button. The server will then send this string back to the client. On the server side, the connection is closed automatically (after the PLCPRJ_RECEIVE_TIMEOUT time defined in the PLC, default: 50 seconds), if the server was unable to receive new data from the client within this period.

Option Explicit
Dim bConnected As Boolean
Dim socketError As Integer
Dim sReceived As String

Private Function OnConnected()
    bConnected = True
    cmdSend.Enabled = True
    txtSend.Enabled = True

    sReceived = ""
    Call lstReceived.Clear
    Call lstMsg.Clear
    Call lstMsg.AddItem(Now & "::Client CONNECTED!", 0)
End Function

Private Function OnDisconnect()
    bConnected = False ' force reconnect
    cmdSend.Enabled = False
    txtSend.Enabled = False

    Screen.MousePointer = vbNormal
    Call lstMsg.AddItem(Now & "::Client CLOSED!", 0)
End Function

Private Function OnError(err As Integer, description As String)
    bConnected = False 'force reconnect
    cmdSend.Enabled = False
    If socketError <> err Then
        Call lstMsg.AddItem(Now & "::Client ERROR:" & err & "(" & description & ")", 0)
        socketError = err
    End If
End Function

Private Function Connect()
    lblState.Caption = IIf(Len(lblState.Caption) < 4, lblState.Caption & ">", ">")

    Winsock1.RemoteHost = txtHost.Text
    Winsock1.RemotePort = txtPort.Text
    Winsock1.Protocol = sckTCPProtocol
    Call Winsock1.Connect
End Function

Private Function Disconnect()
    Call Winsock1.Close
    bConnected = False
End Function

Private Sub Form_Load()
    cmdConnect.Enabled = True
    cmdDisconnect.Enabled = False
    cmdSend.Enabled = False
    txtSend.Enabled = False
    Timer1.Enabled = False
    Call lstMsg.Clear
    Call lstReceived.Clear
    lstReceived.Enabled = False
    lstMsg.Enabled = False
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Call Disconnect
End Sub

Private Sub cmdConnect_Click()
    cmdConnect.Enabled = False
    cmdDisconnect.Enabled = True
    txtHost.Enabled = False
    txtPort.Enabled = False
    Timer1.Enabled = True
End Sub

Private Sub cmdDisconnect_Click()
    Timer1.Enabled = False
    cmdConnect.Enabled = True
    cmdDisconnect.Enabled = False
    txtHost.Enabled = True
    txtPort.Enabled = True
    Call Disconnect
    cmdSend.Enabled = False
    txtSend.Enabled = False
End Sub

Private Sub Timer1_Timer()
    If Not bConnected Then
        Call Disconnect
        Call Connect
    End If
End Sub

Private Sub cmdSend_Click()
    Winsock1.SendData txtSend.Text
    Dim nTerminate(0 To 0) As Byte
    nTerminate(0) = 0
    Winsock1.SendData nTerminate 'append the terminating null character
End Sub

' Winsock events
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
    Dim data() As Byte
    Call Winsock1.GetData(data)
    If data(UBound(data)) = 0 Then
        sReceived = sReceived & StrConv(data, vbUnicode)
        Call lstReceived.AddItem(Now & "::" & sReceived, 0)
        sReceived = ""
    End If
End Sub

Private Sub Winsock1_Close()
    Call OnDisconnect
End Sub

Private Sub Winsock1_Connect()
    Call OnConnected
End Sub

Private Sub Winsock1_Error(ByVal Number As Integer, description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)
    Call OnError(Number, description)
End Sub

Private Sub Winsock1_SendComplete()
    Screen.MousePointer = vbNormal
End Sub

Private Sub Winsock1_SendProgress(ByVal bytesSent As Long, ByVal bytesRemaining As Long)
    Screen.MousePointer = vbHourglass
End Sub


Peer-To-Peer: VB client/server

TwinCAT PLC Examples

UDP example: Visual Basic peer-to-peer-communication partner

This project example demonstrates how a suitable VB communication partner for PLC peer-to-peer device A or B can be realised.

Here you can unpack the complete VB sources: Visual Basic peer-to-peer.

Winsock control is used for the UDP communication. A string can be sent to a remote device by clicking the send button. At the same time, the received strings are added to a list box.

Private Sub cmdSend_Click()
    Call send(txtSend.Text)
'    txtSend.Text = ""
End Sub

Private Sub Form_Load()
    On Error Resume Next
    txtLocalHost.Text = Winsock1.LocalIP
    txtLocalPort.Text = 1002
    txtRemoteHost.Text = Winsock1.LocalIP
    txtRemotePort.Text = 1001

    Winsock1.RemoteHost = txtRemoteHost.Text ' Host to connect to.
    Winsock1.RemotePort = txtRemotePort     ' Port to connect to.
    If Not (Winsock1.State = sckOpen) Then
        Winsock1.Bind txtLocalPort.Text     ' Bind to the local port.
    End If
    txtSend.Enabled = IIf(Err, False, True)
    cmdSend.Enabled = IIf(Err, False, True)
    If Err Then
        Call lstOutput.AddItem(Err.Description)
    End If
End Sub

Private Sub send(msg As String)
    Winsock1.RemotePort = txtRemotePort.Text     ' Port to send to.
    Winsock1.RemoteHost = txtRemoteHost.Text    ' Host to send to.
    Call Winsock1.SendData(txtSend.Text)
    Call lstOutput.AddItem(Winsock1.RemoteHost & "[" & Winsock1.RemotePort & "]<<" & txtSend.Text)
End Sub

Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
    On Error Resume Next
    Dim received As String
    Call Winsock1.GetData(received)

    If Err Then
        Call lstOutput.AddItem(Err.Description)
    Else
        Call lstOutput.AddItem(Winsock1.RemoteHostIP & "[" & Winsock1.RemotePort & "]>>" & received)
    End If
End Sub

Private Sub Winsock1_Error(ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)
    CancelDisplay = True
    Call lstOutput.AddItem(Err.Description)
End Sub