徐 嵩, 孙秀霞, 董文瀚, 李湘清, 李大东
(空军工程大学工程学院航空自动控制工程系,陕西西安710038)
小型无人机系统由于结构简单、造价低廉、生存能力强、机动性好,被广泛运用于科研试验、教学实验中。无人机地面站作为整个系统的用户接口及服务器与无人机之间必须建立并保持可靠高效的数据通信,目前普遍采用通过串口连接的无线数据传输模块实现[1]。无人机地面站是一个具有多个功能模块的复杂系统,要求各功能模块在设计上既满足良好的封装性又不能影响模块间交互。COM组件化设计方法具备良好的封装性、语言无关性和软件的易扩展性[2],近年来被引入无人机地面站的设计与开发[3]。然而,各类串口通信的组件设计还局限在对串口的特性设置与收发字节或字符串等基本功能的封装上[3-5],多个客户端的协调及通信中的翻译与编码等功能仍在组件外实现,没有实现完整封装。本文通过基于分布式COM(DCOM)的设计方法,将地面站-无人机通信中所需的串口管理、遥测遥控及参数调整中的解析、翻译、编码等功能封装在一个COM组件中并实现本地与远程访问。针对本组件分析了COM线程模式的适用性[6],在对第三方提供的串口类CSerialPort进行了修改与扩展的基础上设计了实现串口管理和数据帧收发的接口,并设计了通过调用该接口实现接收及发送数据信息的状态遥控遥测接口与飞控系统参数调整接口,满足了地面站串口通信需求。
组件化软件设计就是将一个大的应用程序划分成多个模块,每一个模块保持功能独立性,模块间通过规范的接口完成实际的任务,其中的模块称为组件。COM(component object model)为软件的组件化设计提供了包含规范与实现两部分的标准。一个设计良好的应用系统往往被分成多个可以单独开发的组件,最终通过主程序对各个组件的调用与管理来实现整个系统。COM中的核心是接口,接口是客户端与COM对象通信的抽象协议,客户端只能通过接口调用COM对象[6],COM 中的智能指针类为接口的使用提供支持。与 C++对象的封装不同,C++对象是源代码级的语义封装,COM对象是二进制级的封装,COM接口的实现对调用它的程序是隐蔽的[7-8]。目前COM接口采用IDL语言定义,与具体实现语言无关,从而保证接口与实现之间的解耦;IDL具备丰富的数据类型支持及扩展,方便了模块间的通信及组件接口调用[8]。
DCOM(Distributed COM)是对COM的一种扩展,支持不同计算机上COM对象间的联网通信[9]。由于DCOM是COM的无缝衍生,因而当前设计的进程外COM组件服务 (COM EXE Service)无需修改即可通过配置(DCOMCNFG)来实现客户对组件的远程访问。DCOM隐藏了网络协议细节,还具备可伸缩性、平台独立、安全性、可配置性等特性[10],使我们可以集中精力设计组件而避免各种通信问题。
DCOM采用SCM(service control manager)进行COM对象的远程激活,并采用ORPC(objectremoteprocedurecalls)进行通信,通信采用代理-存根(Proxy-Stub)结构[9],该结构由IDL文件经 MIDL自动编译得到,无需开发人员设计。COM 运行时(Runtime)支持客户和组件的面向对象服务,采用RPC及安全规定并依据 DCOM 协议标准生成标准网络包[11-12]。具体的DCOM通信结构见图1[13]。
图1 DCOM通信结构
COM线程模型的选用直接影响组件服务与客户端程序的设计方法与运行性能。套间(Apartment)是COM线程模型中重要的逻辑概念,是一个用来隔离并发访问的假想边界,在不同套间中的同一个COM对象将由于套间边界的隔离而必须进行接口调度,这一方面阻止了不安全线程并发访问所引发的错误,但也增加了运行开销。套间分为STA(单线程套间)、MTA(多线程套间)、NA(中性套间),对于组件对象而言创建时可以选择相应的套间支持类型:Apartment(支持STA)、Free(支持MTA)、Both(支持STA与MTA),对于NA的支持必须配置组件的注册。只有当线程所属套间类型与组件支持的套间类型相匹配才能实现直接调用,否则将建立相应的宿主套间(HostSTA/MTA/NA)进行间接调用[6]。这里比较STA与MTA的特点与适用性:
一个进程可以拥有多个STA,每个STA可具备消息循环并只含有一个线程,同一个COM对象在不同的STA中调用时须接口调度过程并增加开销。一个进程只能有一个MTA,MTA中可有多个线程,同一个COM对象在MTA的多个线程间被调用时无需接口调度,但在COM对象设计中必须考虑线程安全问题。需要平凡I/O或处于多个线程中并发运行的组件适合采用MTA(Free或Both)以提高效率[6]。本文设计的通信组件由于不需要窗口及消息机制且须在多个线程中(接收、发送等)并发运行,因而适合采用MTA。
Windows环境下已具备成熟的串口通信支持,可利用VC++运行时标准通信函数[14]、WindowsAPI函数、MSComm控件、第三方串口类进行串口通信设计开发。当前普遍采用后3种方式[3,15]。由于MSComm控件存在诸如内存泄露、无法发送16进制数等缺陷,也有文献采用WindowsAPI自行开发串口组件[3]。本文在修改并扩充第三方类(CSerialPort)的基础上设计实现串口通信组件的串口通信管理接口。
图2中地面站与无人机间的通信由与RS232串口相连的无线数据传输模块实现,地面站包含地面监控软件与参数调整软件等。无人机具有导航模块、飞行控制模块、舵机扩展模块和其他自定义模块,其中只有导航模块能与地面设备直接通信。串口通信组件须实现地面软件与无人机的实时通信以及远程计算机调用通信组件与无人机通信。通信组件之间不直接进行远程通信,各地面软件与远程的通信组件进行交互。多个地面软件均由串口通信方式与无人机通信,该组件的设计符合软件设计的复用性。
图2 地面站串口通信需求
无人机的通信协议已由无人机自动驾驶仪制造方提供,数据帧结构如图3所示,串口通信组件需要完成接收数据时的帧解析与翻译以及发送数据时将待发送信息按照协议编码为帧。
图3 无人机-地面站数据帧结构
地面监控软件与参数调整软件与无人机的数据通信均有明显的异步特性,须采用多线程循环等待的方式实现,因而需要设计异步的COM接口方法(一般的COM接口方法只能采用同步阻塞的方式调用[4])。数据异步传输及多线程会带来数据丢失与访问冲突等问题,需要采用缓冲区及线程同步方法。此外,串口通信组件需要频繁开关串口与翻译编码过程,对内存资源必须进行可靠管理以防泄漏。
本文所设计的组件有3个接口类,其中IComPortAdm管理串口并收发数据帧,IGCSCommUAV编码发送遥控数据与监听翻译遥测数据,CADJCommUAV编码发送参数设置指令、发送参数回复请求与接收翻译参数回复数据。后两者通过接口调度(查询)实现对IComPortAdm中的各种方法的调用,并通过全局线程及对象实现依据抢占设置的串口使用协调。此外所有接口方法均可返回错误,客户端程序可通过try-catch将其捕获用以报错或纠错处理。如图4所示。
图4 串口通信组件整体结构
3.2.1 异步缓冲区模板类的设计
为提高数据传输可靠性与解决接口异步调用和线程冲突问题,须将各种结构类型的数据通过具有异步特性与线程同步的缓冲区进行传输,因而本文设计了模板类:
同步操作函数与一般循环队列相同,异步操作函数将缓冲区的存储状态与信号量(利用Semaphore的计数器Tokens)结合起来,图5中粗实框由m_BufferSync防止冲突。
3.2.2 简易线程管理器的设计
组件服务与客户端都存在需要反复开启与关闭的线程,退出线程函数后只有关闭线程句柄才能销毁线程对象,如未关闭句柄而再次创建线程则该句柄指向新创建的线程造成原线程对象资源泄漏。可采用线程池来管理线程,这里为了精简程序采用了自定义的线程管理器(类)来管理线程。
线程管理器的作用过程:首先创建管理器对象并将其启动StartThreadManager,创建自定义线程前注册线程句柄地址RegisterThread;退出线程前采用CloseThreadHandle发送线程退出事件使管理器关闭对应的线程句柄 (管理器线程采用循环等待流程);当管理器对象生命周期结束时由析构函数将管理器线程退出并关闭句柄,通过CheckThreadClosed可检查线程的关闭情况。串口的使用与协调也采用了类似的处理方法即:注册(COM对象指针)→启动→关闭(通过对象指针),如果串口已被占用但允许抢占,可在注册时(通过对象指针)让当前使用该串口的COM对象关闭串口。
图5 异步缓冲区工作流程
3.3.1 对CSerialPort类的修改与扩展:
CSerialPort是一个被广泛应用的串口类[3,15],它以成员函数的形式提供串口设置及写数据接口,以消息方式提供读取数据接口和部分串口状态事件通知。而 MTA下的组件并不支持消息机制,本文去除了该类中有关消息机制的代码并利用异步缓冲区实现了一个异步数据读取接口(函数):
添加成员变量CDataBuffer
3.3.2 接口IComPortAdm的实现
在类CComPortAdm中添加成员变量CSerialPortm_Port与CDataBuffer
添加线程管理器CThreadManager m_ThreadManager与数据分发线程 static DWORD WINAPI DataDistribute(LPVOID pParam),在StartReceiveData中注册并启动线程DataDistribute中采用m_Port.GetRcvByte并依据数据帧的格式实现循环等待的处理流程并将数据帧存入m_DataFrameBuffer,其他数据存入m_NonFrameStrBuffer(AsynEnterBuffer)以供其他两个接口调用:如本接口的一个方法:
RcvNonFrameStr方法的实现与其类似 (将m_DataFrame-Buffer替换为m_NonFrameStrBuffer),其余方法均为类CSerial-Port中方法的组合。
3.4.1 接口调度(查询)的实现
这两个接口都经IComPortAdm获取并发送数据帧,因而必须获取该接口的指针,COM提供了一个灵活的接口查询函数QueryInterface实现该过程,将IComPortAdm指针赋给这两个接口实现类中的成员变量IComPortAdm*m_IComPortAdm:
QueryInterface会使被调用的接口对象的引用数加1,而由于实现类中不能采用智能指针,当退出(析构)这两个接口时须通过m_IComPortAdm->Release()显式地将引用数减1,从而使组件进程正常退出。
3.4.2 翻译-编码与数据传递的实现
翻译时根据数据帧的特点可采用联合的数据结构对帧进行解析,利用解析结果将数据帧翻译为最终需要的结构体数据;编码采用相反过程。翻译与编码对帧的基本处理(校验、合成与分解)一致,可将其作为基类,而将其他的翻译与编码功能的实现作为若干派生类,提高代码复用率。
几乎每一帧翻译结果或待编码数据的结构类型都不相同,IDL提供了丰富的数据类型支持[5,8],本文利用其对结构体和联合体的支持设计一种参数结构便于传递:
这种参数结构使我们能通过帧标识符(命令字)来选择数据类型。在此基础上添加数据接收与命令上传线程:
并添加线程管理器,与缓冲区:
实现整个遥控遥测接口(见图6)。
图6 IGCSCommUAV接口实现流程
两个线程调用了COM对象,必须在进入线程时将其放入MTA:::CoInitializeEx(NULL,COINIT_MULTITHREADED);,退出线程时必须使线程退出MTA(::CoUninitialize();)调参接口的实现采用同样的方法,区别在于发送参数回复请求时无需进行编码(直接发送命令字)。
各地面软件都可作为组件的客户端本地或远程调用该串口通信组件。本地调用与调用一般的进程内或进程外 COM组件一致,远程调用需要通过DCOMCNFG对已注册的相应组件进行设置,包括身份信息验证方式、运行应用程序(组件)的位置、对各类用户激活及访问该组件的权限的设置[8]。为便于客户端编程,本文在将安全级别降到最低的情况下在局域网内实现了地面监控软件及调参软件对通信组件的远程调用:
定义智能指针:IComPortAdmPtr m_pComPortAdm;
CoCreateInstanceEx(__uuidof(ComPortAdm),NULL,CLSCTX_SERVER,&si,1,&qi);
m_pComPortAdm=IComPortAdmPtr(qi.pItf);
其中si.pwszName可以设为计算机名(同一个工作组内),或组件所在IP地址,qi.pIID=&IID_IComPortAdm。其他两个接口的调用方法与之相同,在客户端设计多个工作线程并将其置入MTA,循环调用组件的异步接口方法。如果提高了组件的安全性设置则需要通过 COSERVERINFO结构来设置服务器信息机及提供客户端的身份识别信息,DCOM的配置不是本文所涉及的内容,可参看MSDN。
采用VS2005进行调试,其报告中未发现资源泄漏,在正常运行时可以反复开关串口及遥控遥测与调参过程,通过调试可验证当所有客户端对该COM服务器中的COM对象的引用数减为0时,在COM服务器的消息机制作用下组件进程将顺利退出。
COM作为实现良好的软件组件化设计的标准与方法已被广泛应用,它的扩展DCOM也为分布式系统的设计提供了一个便利途径。本文在分析地面站串口通信需求的基础上,综合串口基本管理、无人机遥控遥测数据通信以及无人机参数调整通信等功能,设计了串口通信组件。采用自定义的异步缓冲区模板类实现了对COM接口方法的异步调用及解决多线程并发运行时的冲突问题,采用接口调度(查询)实现了组件中有关接口对象的相互调用,并设计了线程管理类器有效防止资源泄漏,保证了无人机地面站串口通信的可靠性,增强了软件功能模块的复用性,同时也为通信模块组件化设计提供一种可行的新方案。
[1]龚晓莉,李健.小型无人机数据采集与通信系统的设计与实现[J].微计算机信息,2007,23(12):100-101.
[2]吴云,宋人杰.基于COM技术的交互图形开发平台的设计与实现[J].计算机工程与设计,2009,30(9):2307-2310.
[3]马力.无人直升机开放控制平台控制组件与数据通信[D].南京:南京航空航天大学硕士学位论文,2008:36-44.
[4]李比翼,陈特放.异步COM技术在串口通信组件的应用[J].长沙航空职业技术学院学报,2006,6(3):40-42.
[5]杨启亮,邢建春,王平.基于DCOM的I/O驱动程序封装关键技术研究[J].计算机工程,2005,31(14):231-233.
[6]徐名昆,张英海.COM服务器组件线程模型的选择[J].计算机工程与设计,2004,25(12):3338-3339.
[7]周振红,周洞汝,杨国录.基于COM的软件组件[J].计算机应用,2001,21(3):6-8.
[8]Dale Rogerson.COM技术内幕[M].杨秀章,译.北京:清华大学出版社,1999:202-212.
[9]邱岩.组件技术及其分析比较[J].计算机工程与设计,2003,24(7):13-17.
[10]吴立伟,陈进,孙卫祥.DCOM在设备远程监测与故障诊断系统中的应用[J].计算机工程与应用,2006,21(4):183-185.
[11]汪维华,葛君伟,解绍词.基于DCOM的分布式Web模型研究[J].计算机应用研究,2005,22(6):202-203.
[12]史建华,吴旭光,高云迪,等.基于DCOM技术的三层分布式网络的实现[J].计算机测量与控制,2009,17(7):1359-1361.
[13]赵靖,李石君,刘海青.基于对象的分布式系统比较[J].武汉大学学报(理学版),2005,51(S2):151-154.
[14]钟文,王益.串行端口底层通信机理研究及相应实现[J].计算机工程,2005,31(19):225-227.
[15]阮锋,沈进棋,诸静.移动机器人中通信的串口解决方案[J].计算机工程,2004,30(21):186-189.