因為項目需要,學習了TwinCAT3中使用UDP協議進行通訊的基本知識。這個做個簡單的筆記,方便以后查詢。
1 概述
倍福為了實現從實時環境中直接訪問網卡(network cards)專門提供了一個函數 “TCP/UDP Realtime”, 這個訪問要么來自PLC(61131-3), 要么來自C++.這個函數對使用以下協議進行的通訊提供支持:
- TCP/IP
- UDP/IP
- ARP/Ping
TC3中使用該函數實現網絡通信的示意圖如下所示:

不管使用何種協議, 在基於該協議的項目和TC3之間的通訊連接的實現都必須通過一對接口(interfaces)來實現:
- 一個接口指針對發送數據和建立連接等功能提供支持
- 基於回調形式(callbacks)實現一個接收器接口,該接收器接口以事件或數據的形式為項目提供反饋
通信組件是一個TcCom對象——TCP/UDP RT, 這個通信組件通過網卡完成實例化和配置。
2 TCP/UDP RT模塊的配置
1)在實時的以太網適配器下創建TCP/UDP RT模塊

2) 選擇對應模塊

3)參數化先前創建的模塊實例

至此,我們完成了對TCP/UDP RT模塊的實例化創建。
3 實現一個具有反射服務的例程
1) 在VS2012下創建一個TC3的解決方案

2)在Tasks下添加一個task. 然后在TMC Files里面實現一個ITcIoUdpProtocolRecv類型的接口。通過該接口的實現,TC3生成了一個method. 這個method在UDP數據包到達是被調用。

TCP/UDP RT模塊需要一個接口指針ITcIoUdpProtocol,該指針包含了對TCP/UDP RT對象的引用。
3)創建一個名字為udpPort 的ITcIoUdpProtocol類型的接口指針

4)生成TMC code. 接下來我們着重描述一個代碼實現細節
TCP/UDP RT 模塊中的CycleUpdate() method 和CheckReceived() method作為模塊的一部分必須被調用。
5) CycleUpdate() method實現如下:
1 ///<AutoGeneratedContent id="ImplementationOf_ITcCyclic"> 2 HRESULT CModule1::CycleUpdate(ITcTask* ipTask, ITcUnknown* ipCaller, ULONG_PTR context) 3 { 4 HRESULT hr = S_OK; 5 m_counter+=m_Inputs.Value; 6 m_Outputs.Value=m_counter; 7 m_spUdpProt->CheckReceived(); // ADDED 8 return hr; 9 }
ReceivedData() method通過接口的實現被創建,並且此method將被CheckReceived()反復調用。
6)ReveiveData() method以發送的信息和數據作為輸入參數
1 ///<AutoGeneratedContent id="ImplementationOf_ITcIoUdpProtocolRecv"> 2 HRESULT CModule1::ReceiveData(ULONG ipAddr, USHORT udpDestPort, USHORT udpSrcPort, ULONG nData, PVOID pData, ETYPE_VLAN_HEADER* pVlan) 3 { 4 HRESULT hr = S_OK; 5 // mirror incomming data 6 hr = m_spUdpProt->SendData(ipAddr, udpSrcPort, udpDestPort, nData, pData, true); 7 m_Trace.Log(tlInfo, FLEAVEA "UDP ReceiveData: IP: %d.%d.%d.%d udpSrcPort: %d DataSize: %d (hr2=%x) \n", 8 ((PBYTE)&ipAddr)[3], ((PBYTE)&ipAddr)[2], ((PBYTE)&ipAddr)[1], ((PBYTE)&ipAddr)[0], 9 udpSrcPort, nData, hr); 10 return hr; 11 } 12 ///</AutoGeneratedContent>
在開啟和終止的過程中, 一個UdpProtocol接口的引用必須被設置。
7)端口開啟在SafeOp到Op的過程中被觸發,RegisterReceiver打開一個UDP端口以接收數據
1 HRESULT CModule1::SetObjStateSO() 2 { 3 HRESULT hr = S_OK; 4 //START EDITING 5 if (SUCCEEDED(hr) && m_spUdpProt.HasOID()) 6 { 7 m_Trace.Log(tlInfo, FLEAVEA "Register UdpProt"); 8 if (SUCCEEDED_DBG(hr = m_spSrv->TcQuerySmartObjectInterface(m_spUdpProt))) 9 { 10 m_Trace.Log(tlInfo, FLEAVEA "Server: UdpProt listen to Port: %d", 10000); 11 if (FAILED(hr = m_spUdpProt->RegisterReceiver(10000, 12 THIS_CAST(ITcIoUdpProtocolRecv)))) 13 { 14 m_Trace.Log(tlError, FLEAVEA "Server: UdpProtRegisterReceiver failed on Port: %d", 10000); 15 m_spUdpProt = NULL; 16 } 17 } 18 } 19 20 // If following call is successful the CycleUpdate method will be 21 called, 22 // eventually even before method has been left. 23 hr = FAILED(hr) ? hr : AddModuleToCaller(); 24 // Cleanup if transition failed at some stage 25 if ( FAILED(hr) ) 26 { 27 if (m_spUdpProt != NULL) 28 m_spUdpProt->UnregisterReceiver(10000); 29 m_spUdpProt = NULL; 30 RemoveModuleFromCaller(); 31 } 32 //END EDITING 33 m_Trace.Log(tlVerbose, FLEAVEA "hr=0x%08x", hr); 34 return hr; 35 }
8) 停止操作在Op到SafeOp的過程中被執行
1 HRESULT CModule1::SetObjStateOS() 2 { 3 m_Trace.Log(tlVerbose, FENTERA); 4 HRESULT hr = S_OK; 5 6 if (m_spUdpProt != NULL) 7 m_spUdpProt->UnregisterReceiver(10000); 8 m_spUdpProt = NULL; 9 m_Trace.Log(tlVerbose, FLEAVEA "hr=0x%08x", hr); 10 return hr; 11 }
至此,TCP/UDP RT模塊實例化和配置完成。
