TwinCAT 3中基于UDP协议通讯的C++实现
因为项目需要,学习了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模块实例化和配置完成。