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的解决方案

Grafik aus Coding UDP Echo in C++ (ibter4i2.png)

         2)在Tasks下添加一个task. 然后在TMC Files里面实现一个ITcIoUdpProtocolRecv类型的接口。通过该接口的实现,TC3生成了一个method. 这个method在UDP数据包到达是被调用。

Grafik aus Coding UDP Echo in C++ (iiuh3sj2.png)

TCP/UDP RT模块需要一个接口指针ITcIoUdpProtocol,该指针包含了对TCP/UDP RT对象的引用。

        3)创建一个名字为udpPort 的ITcIoUdpProtocol类型的接口指针

Grafik aus Coding UDP Echo in C++ (i3g5hbp1.png)

      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模块实例化和配置完成。

posted on 2015-09-17 22:43  Curnane  阅读(4301)  评论(0编辑  收藏  举报

导航