刘铁华 尹俊勋
【摘要】RFID中间件是RFID系统的神经中枢。在深入研究EPCglobal后,文章借鉴EPCglobal的中间件标准ALE,提出了一种嵌入式RFID中间件设计的具体思路,以及较为详细的数据结构设计和程序流程。该中间件结构紧凑,可以直接运行在读写器上,既可以为运行在读写器上的应用程序服务,也可以通过网络为运行在PC上的应用程序服务。
【关键词】RFID 中间件 EPC ALE 嵌入式
1 前言
传统RFID中间件运行在PC服务器上,同时管理多台读写器。读写器读到的标签,要先通过网络返回给运行中间件的服务器,中间件对返回的标签数据进行整理,把结果返回给客户端,客户端根据结果做进一步的处理。随着RFID应用领域的扩展,这种体系结构已经不能满足一些应用的需要,这些应用要求对读写器的结果进行快速、实时的处理,例如基于RFID的自动化管理、控制系统。同时,手持式读写器的应用越来越广泛,却无法使用运行在PC机上的中间件。对此,本文设计了一种嵌入式RFID中间件,该中间件遵循EPCglobal的中间件标准ALE,直接运行在读写器上。
2 EPCglobal ALE标准
EPC(Electronic Product Code,产品电子代码)是美国麻省理工学院的自动识别中心(Auto-ID中心)提出的,其主要思想是为每一个参加互联网的产品分配一个电子标签,该标签存储了一个唯一的EPC码。当产品通过阅读器时,利用RFID技术来读取数据,得到对应的EPC码,再通过与互联网相连的服务器完成相应的EPC码的解析。EPC系统主要由EPC编码、EPC标签、EPC读写器、EPC中间件、PML(物理标记语言)服务器和ONS(对象名解析服务)服务器组成。系统协议主要由RFID通信协议、应用事件管理(ALE,Application Level Event)协议等组成。
图1 ALE在EPCglobal体系结构中的位置
ALE是EPCglobal的中间件标准,是阅读器模块和客户应用程序之间的接口协议。该协议定义了客户可以如何过滤和整合来自读写器的EPC标签,并面向不同的企业应用程序和阅读器定义了统一的接口。这样即使后端应用程序增加或由其他软件取代,或者RFID读写器的种类有所改变,其它部件都不需要做修改。ALE的处理过程是:接收来自一个或多个数据源的EPC标签码;根据企业应用程序要求以一定的时间间隔整合数据,过滤重复和不感兴趣的EPC码;根据企业应用程序要求以不同的形式打包发送报告。
3 嵌入式中间件的设计
本文所述系统硬件平台:ARM9内核、32Mbyte内存、16Mbyte Flash和10M网口。软件系统平台:嵌入式Linux系统。
3.1 嵌入式中间件的工作原理
依照ALE,发送到中间件的事件请求都表示为Event Circle Specification(ECSpec),它包含请求的工作方式和报表的产生方式,能够同时处理多个事件请求。从中间件返回的数据都表示为ECReports(Event Circle Reports)。ECSpec和ECReports是两个标准XML片断实例,以一种结构化和统一的方式在EPC信息的读取、存储和传输过程中对其进行描述,使得对标签信息的理解、存储和传送更容易。
中间件支持两种异步模式:订阅方式(subscribe)和轮询方式(poll)。异步模式中客户端可以订阅一个事件,当事件发生时,ALE会异步地将数据交付给客户端。中间件同时提供了一个同步模式,即立即返回模式(immediate)。
ECSpec有四种状态:Undefined(未定义),Unrequested(未请求),Requested(请求),Active(活动)。
ECSpec的状态转 移图
当ECSpec请求到达中间件时,ECSpec进入“Unrequested”状态;ECSpec进入“Requested”状态后,会等待中间件的处理;当ECSpec进入“Active”状态,中间件根据ECSpec中指定的开始触发条件,在条件满足的情况下收集来自读写器的原始EPC数据,当ECSpec中指定的停止触发条件满足时返回查询报告ECReports。
3.2 中间件的系统结构
图3 中间件的系统结构
整个嵌入式中间件作为一个独立的进程运行,以消息队列作为与其它进程通信的接口。中间件的消息接收队列与消息发送队列,是两个不同的消息队列。考虑到中间件进程可能同时为多个应用进程提供服务,中间件使用固定的消息接收队列,所有应用进程共享该消息队列向中间件进程发消息。中间件通过某种机制与每个应用进程协商,从而获得一个只被中间件进程与该应用进程使用的消息队列,中间件通过该消息队列向该应用进程发消息。换言之,中间件的消息接收队列是固定的、唯一的,而中间件的消息发送队列有多条,每条对应一个应用进程。这种机制,将使中间件能并行地为上层应用提供服务,大大提高中间件的效能。
中间件包含五个模块:消息接收模块,XML数据解析模块,命令处理模块,XML数据构造模块,消息发送模块。
其中,XML数据解析模块基于Linux上开源的XML解析器Expat。Expat是一款基于事件、非验证的XML解析器,快速且轻巧,适合用在系统资源相对较小的嵌入式系统。Expat被编译成动态的链接库,要实现本文所需的功能,只要用到它的五个函数:XML_ParserCreate(),XML_SetElementHandler(),XML_SetCharacterDataHandler(),XML_ParserFree(),XML_Parser()。
4 嵌入式RFID中间件的软件实现
4.1 消息队列设计
消息队列是中间件与读写器上应用进程的通信接口。消息队列设计的关键是消息结构和应用进程与中间件进层的协商机制,后者用来建立中间件到对应应用进程的消息发送队列(对于应用进程自身来说,为消息接收队列)。
(1)消息结构
typedef struct msg_text{
long intmsg_type;
unsigned intale_cmd;
charxml_text[MAX_SIZE];
} msg_text;
该结构体中包括三个变量:msg_type标识消息的类型,接收端可以根据该变量接收或忽略该消息;ale_cmd标识具体的指令类型,命令处理模块根据该变量调用相应的处理函数;xml_text数组保存消息的数据。
在消息队列的发送端调用Linux的库函数msgget()获得消息队列,调用msgsnd()发送消息;在接收端调用msgget()函数获得消息队列,调用msgrcv()函数提取消息[1]。
(2)中间件与应用进程的协商机制
应用进程通过getpid()函数获得自身的进程标识号,生成一个消息,把消息的ale_cmd置为0,把进程标识号写入xml_text数组,通过中间件的消息接收队列往中间件发消息。中间件收到ale_cmd为0的消息,知道有一个应用进程请求建立消息队列。应用进层与中间件同时以该进程标识号作为键值,调用msgget()函数,获得消息队列标识号。
这种机制利用了系统中进程号的唯一性,以进程号作为键值建立消息队列。
4.2 XML数据解析
XML数据的解析基于开源的XML解析器Expat,被编译成共享库,这里需要调用其函数。
图4 XML数据解析流程
图4为基于Expat的XML格式数据解析流程图。p为XML_ParserCreate()函数的返回值。start、end和cdata的函数定义由Expat规定,函数体由用户自己实现以实现其应用。当解析器遇到XML元素的开始标记就会执行start函数,遇到XML元素的结束标记就会执行end函数。具体的函数定义及细则可以参考Expat的官方文档。
4.3 命令处理模块
(1)命令处理模块的数据结构
命令处理模块是嵌入式中间件的核心,是中间件接口API的内部实现。嵌入式中间件可为网络用户服务,也可为读写器上的嵌入式应用程序服务。网络用户用其URL或IP地址标识,本地用户即嵌入式应用程序用其消息接收队列标识号标识。URL/IP地址、队列标识号统一为URI。
在调用这个模块之前,先在内存中创建一个全局的指针变量,指向ECSpec_t链表。链表的节点包括ECSpec数据本身和相应状态信息及控制数据。ECSpec_t链表保存了中间件中所有已注册的ECSpec的所有信息,命令处理函数的实现主要基于对该链表的操作。链表节点的结构定义如下:
typedef struct ECSpec_t_node
{
ECSpec*p_ECSpec;//指向ECSpec结构体的指针
Subscriber *firstSub;//订阅者链表的头指针
Poll*firstPoll;//查阅者链表的头指针
int ECSpecState;//ECSpec的状态
pthread_tthreadid;//工作线程标识符
pthread_mutex_tecspec_state;//互斥量,用于线程同步
pthread_cont_tecspec_con;//条件变量,用于线程同步
ECSpec_t_node*nextECSpec_t;//下一个ECSpec_t_node的指针
}ECSpec_t_node;
依照ALE,节点中的ECSpec[2]是ALE事件周期的描述,它包含了事件周期的工作方式和事件周期后报表的产生方式。
订阅者链表结构如图5:
图5订阅者链表
一个subscriber代表一个订阅者,字符串指针uri指向订阅者的地址(URL/IP地址或消息队列标识符),next指针指向下一个订阅者,ECSpec的报表发送给每一个订阅者。最后一个订阅者的next指针指向空。
查阅者链表的结构与订阅者链表相似。
(2)命令处理模块的函数定义
依照ALE,本命令处理模块实现了九个函数。这些函数的类型定义及程序流程如下:
a)ale_define(specName:string,spec:ECSpec):void
往全局链表中增加一个该ECSpec_t的节点,表示向中间件“注册”了一个ECSpec;该ECSpec增加一个工作线程,即每个ECSpec对应一个工作线程。工作线程按照ECSpec的要求开始事件周期,获取读写器返回的原始标签数据,产生报表。
b)ale_undefine(specName:string):void
根据提供的参数specName,向中间件注销一个ECSpec,即从ECSpec_t链表中删除该节点,并终止该ECSpec对应的工作线程。
c)ale_getECSpec(specName:string):ECSpec
根据提供的参数specName,在ECSpec_t链表中查找该ECSpec对应的节点。若该节点存在,返回该ECSpec。
d)ale_getECSpecNames():List(String)
遍历ECSpec_t链表,返回中间件中注册的所有ECSpec的名字。
e)ale_subscribe(specName:string,notificationURI:string):void
为指定的ECSpec增加一个订阅者,即在订阅者链表中插入一个新的订阅者,并根据图2修改ECSpec的状态。
f)ale_unsubscribe(specName:string,notificationURI:string):void
为指定的ECSpec取消一个订阅者,即在订阅者链表中删除指定的订阅者,并根据图2修改ECSpec的状态。
g)ale_getSubscribers(specName:String):void
返回参数指定的ECSpec的所有订阅者的URI。
h)ale_immediate(spec:ECSpec):ECReports
注册并订阅ECSpec,在第一个事件周期结束并返回报表后取消订阅,注消ECSpec。
i)ale_poll(specName:string,notificationURI:string):ECReports
为指定的ECSpec增加一个查阅者,即在查阅者链表中插入一个新的查阅者,并改变ECSpec的状态。在第一个事件周期结束并返回报表后取消查阅,即在查阅者链表中删除该查阅者。
参考文献
[1]Neil Matthew, Richard Stones,著. 陈健,宋健健,译.Linux程序设计(第3版)[M]. 北京:人民邮电出版社,2007.
[2]EPCglobal Inc. The Application Level Events (ALE) Specification Version1.0[S]. http://www.epcglobalinc.org/standards/ale/ale_1_0-standard-20050915.pdf.
[3]Finkenzeller K,著. 陈大才,译. 射频识别(RFID)技术(第2版)[M]. 北京: 电子工业出版社,2001.
[4]孟和,赵政,薛桂香,等. EPCglobal应用层事件引擎设计与实现[J]. 计算机工程,2008(6): 12.★
【作者简介】
刘铁华:华南理工大学电子与信息学院硕士研究生,主要研究领域为RFID中间件。
尹俊勋:华南理工大学电子与信息学院博士生导师,主要研究领域为通信与音视频处理。