Quick Start (C++ / TCP Client)
Dieses Quick Start zeigt die Implementierung eines TCP Clients als TwinCAT 3 C++ Projekt.
Das Engineering System muss dabei die Voraussetzungen für TwinCAT 3 C++ erfüllen.
Das Beispiel ist auch als Download Sample 01 verfügbar.
TwinCAT C++ Projekt anlegen
In diesem Arbeitsschritt wird ein neues TwinCAT 3 C++ Projekt angelegt.
- 1. Legen Sie ein neues TwinCAT Projekt an
- 2. Fügen Sie ein TwinCAT C++ Projekt hinzu
- 3. Wählen Sie ein Driver Projekt aus
- 4. Verwenden Sie als Grundlage für den TCP Client den Wizard für eine Modul-Klasse mit „Cyclic IO“.
- Als Ergebnis liegt ein fertiges TwinCAT C++ Projekt vor.
TMC Editor zum Anlegen von Interfaces, Pointern und Parametern
Nach dem Anlegen des Projektes wird in diesem Arbeitsschritt die Implementierung des C++ TCP Clients vorgenommen.
- 1. Das durch den Wizard erstellte Modul muss das Interface „ITcIoTcpProtocolRecv“ implementieren. Öffnen Sie den TMC Editor, indem Sie auf die TMC Datei des Projektes doppelklicken. Fügen Sie das Interface dem Modul unter „Implemented Interfaces“ hinzu.
Unter „Implemented Interfaces“ öffnen Sie eine Auswahl der bereitstehenden Interfaces durch einen Klick auf den „+“-Button. Wählen Sie hier „ITcIoTcpProtocolRecv“ aus.
- 2. Zusätzlich benötigen Sie einen Interface Pointer „ITcIOTcpProtocol“.
- 3. Durch Anlegen eines Parameters werden die zu kontaktierende Server IP Adresse und der Port konfigurierbar.
- 4. Benutzen Sie nun den TMC Code Generator, um den Code des C++ Moduls vorzubereiten.
Starten Sie den TMC Code Generator indem Sie auf dem C++ Projekt im Kontexmenü (Rechts-Klick) den entsprechenden Menüpunkt auswählen. - Alle Schritte im TMC Editor sind nun abgeschlossen.
TCP Client implementieren
- 1. Legen Sie in der Modul Header Datei (hier: Modul1.h) zwei Member-Variablen an.
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. Diese werden in dem Constructor (Module1.cpp) initialisiert.
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. Der Interface Pointer m_spTcpProt wird nun in der Transition SO (also in Methode SetObjStateSO) initialisiert.
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 der Transition OS (also Methode SetObjStateOS) wird eine evtl. vorhandene Verbindung abgebaut und der Socket freigegeben.
///////////////////////////////////////////////////////////////////////////////
// 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. In der „CycleUpdate“ Methode, die zyklisch aufgerufen wird, wird der eigentliche Ablauf implementiert. Hier wird eine TCP Verbindung zu einem Server aufgebaut (Adresse wird in Parametern „m_TcpServerIpAddress“ und „m_TcpServerPort“ bereitgestellt). Das Handle zur Verbindung wird in der Member-Variable „m_SockId“ abgelegt. Die Verbindung wird genutzt, um einen einfachen http-GET-Request abzusetzen.
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. Das Modul implementiert das Interface „ITcIoTcpProtocolRecv“, wodurch der TMC Code Generator eine Methode „ReceiveEvent“ angelegt hat. Diese wird aufgerufen, wenn ein Event empfangen wurde und muss somit mit den unterschiedlichen Event-Typen umgehen können.
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. Äquivalent zu der „ReceiveEvent“ Methode wurde eine „ReceiveData“ Methode ebenfalls aus dem Interface „ITcIoTcpProtocolRecv“ angelegt. Diese ist für das Empfangen der Daten zuständig und wird wie folgt implementiert:
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. Das Modul ist nun fertig und kann kompiliert werden. (Rechts-Klick auf das Projekt “Build”).
- 9. Eine Instanz des Moduls wird angelegt:
Dafür Rechts-Klick auf das C++ Projekt
und Auswahl des Moduls
- Die Instanz wird mit einem Task verbunden, sodass die „CycleUpdate“ Methode aufgerufen wird.
Vorbereitung Netzwerkkarte
Stellen Sie für das TCP/UDP RT Modul sicher, dass der RT-Ethernet-Adapter in der TwinCAT Solution auf die richtige Netzwerkkarte (mit TwinCAT Treiber) verbunden ist.
![]() | Nur Lokale Konfiguration Die Installation des Treibers auf kompatiblen Netzwerkkarten über den Button „Compatible Devices“ erfolgt immer lokal. Auf einer Steuerung mit TwinCAT XAR kann das mitinstallierte Programm TcRteInstall.exe (normalerweise unter C:\TwinCAT\3.1\System) genutzt werden. |

“TCP/UDP RT” Modul Konfiguration
Hinweis | |
Variablennamen Hier werden Variablennamen in Bezug auf TCP verwendet. Diese sind entsprechend zu ersetzen. |
- 1. Legen Sie das „TCP/UDP RT“ Modul unterhalb des RT-Ethernet-Adapters an, indem Sie „Add Object(s)…“ im Context-Menü anwählen.
- 2. Dann wählen Sie das „TCP/UDP RT“ Modul aus:
- Das TCP/UDP RT Objekt wird unterhalb des Adapters angelegt.
- 3. Parametrieren Sie die zuvor angelegte Instanz des Moduls (hier: Modul1) unter „Interface Pointer“ „TcpProt“ mit der OID des angelegten „TCP/UDP RT“ Objekts:
- 4. Bei PLC Projekten ist diese Konfiguration ebenso an der Instanz vorzunehmen, hier jedoch unter dem Reiter „Symbol Initialization“:
- Damit ist die Konfiguration abgeschlossen
![]() | Verbindungsabbruch durch Betriebssystem bei Promiscuous Mode Wenn an dem RT-Ethernet Adapter im Tab „Adapter“ der Promiscuous Mode eingeschaltet ist, werden eintreffende TCP Verbindungsaufbauten durch das Betriebssystem abgebrochen, da dieses einen im TCP/UDP RT Objekt geöffneten Port nicht kennt. |
Handhabung
- 1. Das Beispiel ist betriebsbereit, nachdem Sie an der Modulinstanz sowohl die TcpServerIpAddress als auch den TcpServerPort konfiguriert haben:
- 2. Nach dem Aktivieren der Konfiguration können Sie im Output sowohl Log Meldungen (vgl. Source Code) als auch die ersten 100 Bytes der Antwort des Servers sehen:
- 3. Für die Ausgabe dieser Meldungen kann der „Tracelevel“ (auf Info) konfiguriert werden:
Der Ablauf erfolgt einmalig beim Start des Programms.
Wenn „m_bSendRequest“ auf TRUE gesetzt wird (z.B. durch das TwinCAT Live Watch), wird ein neuer Request gesendet. Die Rückgabe der SendData Methode wird in hrSend abgelegt – für das Beispiel kann sie per Debugger beobachtet werden.