Quick Start (C++ / TCP Client)
This Quick Start shows the implementation of a TCP client as a TwinCAT 3 C++ project.
The engineering system must meet the requirements for TwinCAT 3 C++.
The example is also available for download under Sample 01.
Creating a TwinCAT C++ project
In this step, a new TwinCAT 3 C++ project is created.
- 1. Create a new TwinCAT project
- 2. Add a TwinCAT C++ project
- 3. Select a Driver project
- 4. Use the wizard for a module class with "Cyclic IO" as the basis for the TCP client.
- The result is a complete TwinCAT C++ project.
TMC editor for creating interfaces, pointers and parameters
After creating the project, the next step involves implementation of the C++ TCP client.
- 1. The module created by the wizard must implement the interface "ITcIoTcpProtocolRecv". Open the TMC editor by double-clicking on the TMC file for the project. Add the interface to the module under "Implemented Interfaces".
Under "Implemented Interfaces" open a selection of the available interfaces by clicking on the "+" button. Select "ITcIoTcpProtocolRecv". - 2. In addition, an "ITcIOTcpProtocol" interface pointer is required.
- 3. By creating a parameter the server IP address to be contacted and the port become configurable.
- 4. Now use the TMC code generator to prepare the code of the C++ module.
Start the TMC code generator by selecting the appropriate menu item in the context menu (right-click) of the C++ project. - All steps in the TMC editor are now completed.
Implement TCP client
- 1. Create two member variables in the module header file (here: Modul1.h).
ULONG m_SockId;
BOOL m_bSendRequest; //set by debugger for sending a http command
ULONG m_connections; //count number of connection attempts
HRESULT m_hrSend; //Last hr of SendData
- 2. These are initialized in the Constructor (Module1.cpp).
CModule1::CModule1()
: m_Trace(m_TraceLevelMax, m_spSrv)
, m_TraceLevelMax(tlAlways)
, m_hrSend(0)
{
m_SockId = 0; //added
m_bSendRequest = true; //added
m_connections = 0; //added
}
- 3. The interface pointer m_spTcpProt is now initialized in the Transition SO (i.e. in method SetObjStateSO).
HRESULT CTcpClient::SetObjStateSO()
{
m_Trace.Log(tlVerbose, FENTERA);
RESULT hr = S_OK;
if (SUCCEEDED(hr) && m_spTcpProt.HasOID()) //added
{ //added
hr = m_spSrv->TcQuerySmartObjectInterface(m_spTcpProt); //added
} //added
hr = FAILED(hr) ? hr : AddModuleToCaller();
- 4. In the Transition OS (i.e. method SetObjStateOS) a connection that may exist is closed, and the socket is released.
///////////////////////////////////////////////////////////////////////////////
// State transition from OP to SAFEOP
HRESULT CTcpClient::SetObjStateOS()
{
//start added code
m_Trace.Log(tlVerbose, FENTERA);
HRESULT hr = S_OK;
if ( m_SockId != 0 )
{
if (m_spTcpProt->IsConnected(m_SockId) == S_OK)
{
m_spTcpProt->Close(m_SockId);
m_spTcpProt->CheckReceived();
}
m_spTcpProt->FreeSocket(m_SockId);
m_SockId = 0;
}
RemoveModuleFromCaller();
m_Trace.Log(tlVerbose, FLEAVEA "hr=0x%08x", hr);
return hr;
//end added code
}
- 5. The actual process is implemented in the "CycleUpdate" method, which is called cyclically. Establishes a TCP connection to a server (address is provided in parameters "m_TcpServerIpAddress" and "m_TcpServerPort"). The connection handle is stored in the member variable "m_SockId". The connection is used to issue a simple http GET request.
HRESULT CTcpClient::CycleUpdate(ITcTask* ipTask, ITcUnknown* ipCaller, ULONG_PTR context)
{
HRESULT hr = S_OK;
//start added code
if ( m_SockId == 0 )
{
if (SUCCEEDED_DBG(hr = m_spTcpProt->AllocSocket(THIS_CAST(ITcIoTcpProtocolRecv), m_SockId)))
{
if (FAILED(hr = m_spTcpProt->Connect(m_SockId, ((PULONG)&m_TcpServerIpAddress)[0], m_TcpServerPort)))
{
m_spTcpProt->FreeSocket(m_SockId);
m_SockId = 0;
}
else {
m_connections++; //count number of connections
}
}
}
else
{
if ( m_bSendRequest && m_spTcpProt->IsConnected(m_SockId) == S_OK )
{
PCHAR pRequest = "GET / HTTP/1.1\r\nHOST: beckhoff.com\r\n\r\n ";
ULONG nSendData = 0;
m_hrSend = m_spTcpProt->SendData(m_SockId, strlen(pRequest), pRequest, nSendData);
m_bSendRequest = false;
}
}
m_spTcpProt->CheckReceived();
//end added code
return hr;
}
- 6. The module implements the interface "ITcIoTcpProtocolRecv", as a result of which the TMC code generator created a "ReceiveEvent" method. This is called when an event is received and must therefore be able to deal with a wide range of event types.
HRESULT CTcpClient::ReceiveEvent(ULONG socketId, TCPIP_EVENT tcpEvent)
{
//start added code
m_Trace.Log(tlInfo, FLEAVEA "Receive TCP Event: SocketId: %d Event: %d \n", socketId, tcpEvent);
switch (tcpEvent)
{
case TCPIP_EVENT_ERROR:
case TCPIP_EVENT_RESET:
case TCPIP_EVENT_TIMEOUT:
m_Trace.Log(tlInfo, FLEAVEA "Connection to remote server failed!\n");
m_SockId = 0;
break;
case TCPIP_EVENT_CONN_CLOSED:
m_Trace.Log(tlInfo, FLEAVEA "Close connection: SocketId: %d \n", socketId);
m_SockId = 0;
break;
case TCPIP_EVENT_CONN_INCOMING:
case TCPIP_EVENT_KEEP_ALIVE:
case TCPIP_EVENT_CONN_IDLE:
case TCPIP_EVENT_DATA_SENT:
case TCPIP_EVENT_DATA_RECEIVED:
break;
default:
break;
}
return S_OK;
//end added code
}
- 7. Analogous to the "ReceiveEvent" method, a "ReceiveData" method was created from the "ITcIoTcpProtocolRecv" interface. It is responsible for receiving the data and is implemented as follows:
HRESULT CTcpClient::ReceiveData(ULONG socketId, ULONG nData, PVOID pData)
{
//start added code
HRESULT hr = S_OK;
PCHAR pResponse = new CHAR[100];
memset(pResponse, 0, 100);
memcpy(pResponse, pData, min(100, nData));
m_Trace.Log(tlInfo, FLEAVEA "Receive answer w/ length %d : first 100 chars:'%s'", nData, pResponse);
return hr;
//end added code
}
- 8. The module is now ready and can be compiled. (Right-click on "Build" project).
- 9. An instance of the module is created:
Right-click on the C++ project
and select the module
- The instance is associated with a task, so that the "CycleUpdate" method is called.
Preparing the network card
For the TCP/UDP RT module, make sure that the RT Ethernet adapter in the TwinCAT solution is connected with the correct network card (with TwinCAT driver).
Local configuration only Installation of the driver on compatible network cards via the button "Compatible Devices" always takes place locally. On a controller with TwinCAT XAR, the program TcRteInstall.exe can be used. It is included in the installation (usually under C:TwinCAT\3.1\System). |
“TCP/UDP RT” module configuration
- 1. Create the “TCP/UDP RT” module under the RT Ethernet adapter by selecting “Add Object(s)…” in the context menu.
- 2. Then select the “TCP/UDP RT” module:
- The TCP/UDP RT object is created under the adapter.
- 3. Parameterize the previously created instance of the module (here: Module1) under “Interface Pointer” “TcpProt” with the OID of the created “TCP/UDP RT” object:
- 4. For PLC projects this configuration is also done at the instance, under the tab “Symbol Initialization”:
- The configuration is thus completed
Disconnection by the operating system in Promiscuous mode If Promiscuous mode is active at the RT Ethernet adapter in the “Adapter” tab, any TCP connection attempts are blocked by the operating system, since it does not recognize a port opened in the TCP/UDP RT object. |
Handling
- 1. The sample is ready to use once you have configured both the TcpServerIpAddress and the TcpServerPort at the module instance:
- 2. After activating the configuration you can see log messages (see source code) and the first 100 bytes of the response from the server in the output:
- 3. To output these messages the “Tracelevel” can be configured (via Info):
The procedure is carried out once when the program starts.
A new request is sent if “m_bSendRequest” is set to TRUE (e.g. through TwinCAT Live Watch). The return of the SendData method is stored in hrSend. For the sample it can be monitored via the debugger.