Quick Start (C++ / UDP)
Das Beispiel implementiert einen „Echo-Dienst“: Hierbei wird auf einem Port (Standard: 10000) ein UDP Server gestartet. Wenn dieser ein UDP Paket empfängt, sendet er den Inhalt zurück an den Absender (mit gleicher IP und gleichem Port).
Das Engineering System muss dabei die Voraussetzungen für TwinCAT 3 C++ erfüllen.
Das Beispiel ist auch als Download Sample 02 verfügbar.
Implementierung des UDP Echo Servers in einem C++ Projekt
- Eine TwinCAT Solution wurde erzeugt
- 1. Wenn noch kein C++ Projekt in der TwinCAT Solution vorhanden ist, müssen Sie dieses angelegen. Verwenden Sie bitte die Vorlage für „TwiNCAT Module Class with Cyclic IO“.
- 2. Legen Sie ein Task an. Unter System / Tasks Rechts-Klick und „Add new Item…“
Ein normaler Task (ohne Image) ist ausreichend. - 3. In dem C++ Projekt öffnen Sie den TMC Editor durch einen Doppel-Klick auf die TMC Datei.
Das Modul muss das ITcIoUdpProtocolRecv implementieren. Hierdurch wird eine Methode erzeugt, die aufgerufen wird, falls UDP Pakete eintreffen.
- 4. Wählen Sie im TMC Editor „Implemented Interfaces“ legen diese per „+“ an. Es erscheint ein Dialog, in dem der Typ ITcIoUdpProtocolRecv ausgewählt wird:
Das Modul benötigt einen Interface Pointer zum ITcIoUdpProtocol, welches die Referenz auf das TCP/UDP RT Objekt beinhalten wird.
- 5. Wählen Sie im TMC Editor „Interface Pointer“ aus und drücken Sie “+”. Es wird ein Interface angelegt, welches per Doppel-Click geöffnet werden kann. Vergeben Sie einen Namen „UdpProt“ und setzen Sie den Typ des Pointers durch „..:“ und die Auswahl im Dialog:
- 6. Der TMC Codegenerator wird einmal gestartet. Rechts-Klick auf dem C++ Projekt und im Kontext Menü „TMC Code Generator“ auswählen.
In der CPP Datei des Moduls (Module1.cpp) muss in der CycleUpdate() Methode die Methode CheckReceived() des TCP/UDP RT Moduls aufgerufen werden. Hierdurch werden eintreffende UDP Pakete per Callback an die implementierte Methode ReceiveData() übergeben.
- 7. Die CycleUpdate() Methode wird folgendermaßen implementiert
///<AutoGeneratedContent id="ImplementationOf_ITcCyclic">
HRESULT CModule1::CycleUpdate(ITcTask* ipTask, ITcUnknown* ipCaller, ULONG_PTR context)
{
HRESULT hr = S_OK;
m_counter+=m_Inputs.Value;
m_Outputs.Value=m_counter;
m_spUdpProt->CheckReceived(); // ADDED
return hr;
}
Die durch die Implementierung des Interfaces angelegte Methode “ReceiveData” wird durch das CheckReceived() mehrfach aufgerufen werden: Für jedes zwischenzeitlich empfangene Paket ein Aufruf.
- 8. Die Methode ReceiveData besitzt sowohl Absender-Informationen wie auch Daten als Eingangsparameter. In diesem Beispiel wird als Reaktion auf ein eintreffendes Paket das Paket (mit vertauschtem Absender/Empfänger) durch die SendData Methode wieder rückgesendet. Die Implementierung erfolgt folgendermaßen:
///<AutoGeneratedContent id="ImplementationOf_ITcIoUdpProtocolRecv">
HRESULT CModule1::ReceiveData(ULONG ipAddr, USHORT udpDestPort, USHORT udpSrcPort, ULONG nData, PVOID pData, ETYPE_VLAN_HEADER* pVlan)
{
HRESULT hr = S_OK;
// mirror incomming data
hr = m_spUdpProt->SendData(ipAddr, udpSrcPort, udpDestPort, nData, pData, true);
m_Trace.Log(tlInfo, FLEAVEA "UDP ReceiveData: IP: %d.%d.%d.%d udpSrcPort: %d DataSize: %d (hr2=%x) \n",
((PBYTE)&ipAddr)[3], ((PBYTE)&ipAddr)[2], ((PBYTE)&ipAddr)[1], ((PBYTE)&ipAddr)[0],
udpSrcPort, nData, hr);
return hr;
}
///</AutoGeneratedContent>
Beim Starten und Beenden muss aus der konfigurierten OID eine Referenz auf das „UdpProtocol“ Interface gesetzt werden; entsprechende Freigaben sind beim Runterfahren zu erledigen.
- 9. Das Starten wird in der Transition SafeOp to Op durchgeführt. Insbesondere ist hierbei RegisterReceiver von Interesse: Er öffnet einen UDP Port zum Empfang.
HRESULT CModule1::SetObjStateSO()
{
HRESULT hr = S_OK;
//START EDITING
if (SUCCEEDED(hr) && m_spUdpProt.HasOID())
{
m_Trace.Log(tlInfo, FLEAVEA "Register UdpProt");
if (SUCCEEDED_DBG(hr = m_spSrv->TcQuerySmartObjectInterface(m_spUdpProt)))
{
m_Trace.Log(tlInfo, FLEAVEA "Server: UdpProt listen to Port: %d", 10000);
if (FAILED(hr = m_spUdpProt->RegisterReceiver(10000,
THIS_CAST(ITcIoUdpProtocolRecv))))
{
m_Trace.Log(tlError, FLEAVEA "Server: UdpProtRegisterReceiver failed on Port: %d", 10000);
m_spUdpProt = NULL;
}
}
}
// If following call is successful the CycleUpdate method will be
called,
// eventually even before method has been left.
hr = FAILED(hr) ? hr : AddModuleToCaller();
// Cleanup if transition failed at some stage
if ( FAILED(hr) )
{
if (m_spUdpProt != NULL)
m_spUdpProt->UnregisterReceiver(10000);
m_spUdpProt = NULL;
RemoveModuleFromCaller();
}
//END EDITING
m_Trace.Log(tlVerbose, FLEAVEA "hr=0x%08x", hr);
return hr;
}
- 10. Das Beenden erfolgt in der Op to SafeOp Transition. Hier wird der UDP Port wieder geschlossen:
HRESULT CModule1::SetObjStateOS()
{
m_Trace.Log(tlVerbose, FENTERA);
HRESULT hr = S_OK;
if (m_spUdpProt != NULL)
m_spUdpProt->UnregisterReceiver(10000);
m_spUdpProt = NULL;
m_Trace.Log(tlVerbose, FLEAVEA "hr=0x%08x", hr);
return hr;
}
Abschließend muss das Modul instanziiert und konfiguriert werden
- 11. Bauen Sie das Projekt einmal. Mit einem Rechts-Klick auf das Modul wählen Sie „Build“
- 12. Eine Instanz des Moduls anlegen. Mit einem Rechts-Klick auf das Projekt öffnet sich „Add new item…“. Wählen Sie hier das passende Modul aus.
- 13. Durch Doppel-Klick auf die Modul-Instanz wird die Parametrierung möglich. Wählen Sie zuerst im Tab „Context“ den Task aus.
“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. |
Testing
Nachdem die Konfiguration aktiviert wurde, kann mittels des UDP Sample Clients ein UDP Paket zu dem C++ Modul gesendet werden. Durch aktivieren des entsprechenden TraceLevels (hier mindestens tlInfo; vgl. C++ Tracing) kann eine Ausgabe im Log des Visual Studios erzeugt werden. Der Client zeigt die zurückgesendeten Pakete im oberen Bereich an.

![]() | Keine lokale Kommunikation Der UDP Sample Client muss auf einem anderen Rechner laufen als die PLC mit dem TCP/UDP RT Objekt, denn es ist keine lokale Kommunikation von dem Windows Betriebssystem in die Echtzeit möglich. |