罗 捷,潘江峰,耿修堂
(西北机电工程研究所,陕西 咸阳 712099)
随着信息技术的迅猛发展,武器装备的信息化程度亟待提高。在某武器系统信息化工程中,指挥控制指令的下达、空情信息的传达、武器终端的状态上报等信息的传递,由一个共同遵循的协议进行约定。协议中详细规定了所交换的报文及其相关信息。如何能够快速、准确地对报文进行编解码,是实现该协议重点需要解决的问题。
针对该协议定义,笔者设计了一种编解码模块的实现方案,并在某项目上成功进行了应用。
设计的编解码模块应满足如下要求:
1)满足协议对实时性的要求。
2)向高层提供编码和解码2个接口函数。
3)出现异常情况时,能自动提供异常信息。
4)具有校验功能。
实现协议报文的编解码主要有两方面的工作,一是进行协议转换,二是编解码程序的设计实现,其实现过程如图1所示。
1.2.1 协议转换
协议是在一种抽象层次上来表示数据结构信息,在实际应用中,需要将协议描述的数据结构翻译转换成具体语言(如C、C++等)的数据结构表示形式[1]。需要对协议描述进行分析、总结,提炼出统一的C语言数据结构,将其作为与高层的数据接口。
1.2.2 编解码程序的设计实现
编码的过程是将高层提供的待编码的报文信息(转换后的C语言数据结构),经过编码函数处理后,转换成二进制码流提供给高层。
解码的过程是将高层提供的待解码的二进制码流,经过解码函数处理后,解析成报文信息(C语言数据结构)提供给高层。
在对编解码模块进行设计时,需要着重考虑软件的可维护性和健壮性等[2-5]。
该协议详细定义了所交换的报文的结构、报文中每个字段的含义及字段长度、校验方式等。
报文由报头、报文类型字、信息字段、报文校验字组成。报头由一些地址信息和控制类信息组成。报文类型字决定了报文的类型,从而确定了本报文所包含的信息字段及其排列顺序。报文校验字采用累加校验和的方式。
从协议描述到C语言数据结构的转换需要经过字段类型的转换、报文结构的转换、数据接口的转换3个步骤。
纵观整个协议,各个字段的字长从1 ~32 bit不等,有的带有符号位,有的没有符号位。在将协议描述转换成C语言数据结构时,转换规则见表1。
表1 类型转换规则
每个报文都有一个报头,其结构是固定的,所以将报头定义为一个C语言的结构体类型。
typedef struct
{
报头字段1
报头字段2
……
} HEAD; //报头
对于一个指定的报文,其包含的信息字段及其排列顺序是固定的,所以将每一个报文的信息字段定义为一个C语言的结构体类型,这样定义了多个C语言结构体类型与每个报文的信息字段对应。
typedef struct
{
报文1的信息字段1
报文1的信息字段2
……
} BWXX1; //报文1的信息字段
typedef struct
{
报文2的信息字段1
报文2的信息字段2
……
} BWXX2; //报文2的信息字段
……
每个报文的报头和报文类型字的结构是固定的。
报文的校验功能在编解码模块中实现,只需把校验的结果反馈给高层即可。
从结构上看,各个报文的不同之处在报文的信息字段部分,故将各个报文信息字段定义为一个C语言的共用体类型,从而实现向高层提供一个统一的数据接口,定义示意如下。
typedef struct
{
struct HEAD head; //报头
unsigned char type; //报文类型字
union
{
struct BWXX1 bw1;
struct BWXX2 bw2;
……
}bwxx;//报文信息
}MESSAGE;
编解码模块从功能上可以分为核心模块、校验模块、异常处理模块、比特流处理模块。编解码模块的结构如图2所示。
核心模块负责实现对报文的编解码功能,并向高层提供编码和解码2个接口。
每一个C语言结构体类型都有一个对应的编码函数,负责对结构中的所有成员进行编码。在实现对报文的编码时,先打包报头、报文类型字,再根据报文类型字判断是哪条报文,继而调用该报文信息字段的编码函数。
每一个C语言结构体类型都有一个对应的解码函数,负责对结构中的所有成员进行解码。在实现对报文的解码时,先解包报头、报文类型字,再根据报文类型字判断是哪条报文,继而调用该报文信息字段的解码函数。
协议采用校验和的方式进行校验,即编码后的数据流按字节累加,其结果的低8位作为校验和,放在报文的最后一个字节。为了简化高层的处理,将校验功能放到编解码模块里来实现。编码时,需要按照规定在编码后的比特流后添加校验和。解码时,需要根据校验和进行判断,以确定是否正确接收到了报文。
在编解码过程中还要对难以预料的一些问题进行异常处理,比如数据越界、不支持的报文类型字、待解包的字符串过短解不出正确的数据、由校验和判断出传输中出现错误等等,并将发现的问题向高层报告。
协议中的数据类型从1~32 bit不等,而且出现的顺序是随机的,没有规律可循,如何能够实现从字节中的任何位置开始打包、解包任何长度(32 bit内)的数据,同时保证具有良好的扩展性是重点需要解决的问题,这也是编解码模块实现的难点。
对于编码过程,采用以下3个层次进行处理:
3.4.1 基础编码函数
基础编码函数是将1~8 bit数据打包到比特流中,需要全面考虑待打包数据从当前字节的第几个比特开始打包、当前字节是否放得下、是否需要跨字节等情况,从而使得基础编码函数能够适用于所有可能的情况。这是进行编码的基础,它的正确性、健壮性决定了编码模块的正确性、健壮性。
3.4.2 扩展编码函数
扩展编码函数是将9 ~32 bit数据打包到比特流中,这里采用了一个巧妙的方法将问题进行转化。首先,对待编码数据进行分解,分解成n+8、n+8+8、n+8+8+8位,其中1≤n≤8,n代表待编码数据最高字节占有的比特数;其次,针对待编码数据的每个字节调用一次基础编码函数,从高字节到低字节依次进行编码。这样复杂的编码问题转换成了简单的加法问题。
3.4.3 带符号位的数据的编码函数
对于带符号位的数据来说,区分正负数分别对待。对于正数,直接调用相关的基础编码函数、扩展编码函数即可。对于负数,先调用基础编码函数将符号位进行打包,再根据负数的存储形式取其数据位[6],之后再调用相关的基础编码函数、扩展编码函数。
与编码过程类似,解码过程也是采用基础解码函数、扩展解码函数、带符号位的数据的解码函数3个层次进行处理。
笔者所设计并实现的编解码模块具有良好的框架,使得具有较高的可维护性。当因协议需要而增加新报文或者当因协议修改而需要修改某报文的信息字段时,只需要对相关结构定义、报文信息字段的编解码函数等进行修改即可。本编解码模块具有良好的健壮性。对基本编码解码函数的遍历测试,保证了对比特流处理的正确性;对各种可能出现的异常情况进行异常处理,保证了本编解码模块的健壮性。本编解码模块能够满足实时性的要求。经测试,完成一条报文的编码解码需要的时间是几个毫秒,完全满足系统实时性的需要。
目前该编解码模块已经在某系统的多个设备中进行了应用,并取得了较好的效果。
参考文献(References)
[1] 李小文,冉靖.LTE协议栈中ASN.1模块的设计与实现[J].计算机工程,2011,37(8):252-255.
LI Xiao-wen, Ran Jing. Design and realization of ASN.1 module in LTE protocol stack[J].Computer Engineering,2011,37(8):252-255. (in Chinese)
[2] 何国伟,王玮.软件可靠性[M].北京:国防工业出版社,1998:218-348.
HE Guo-wei, WANG Wei. Software reliability[M]. Bejing:National Defense Industry Press,1998:218-348. (in Chinese)
[3] 邓良松,刘海岩,陆丽娜.软件工程[M].2版.西安:西安电子科技大学出版社,2004:83-93.
DENG Liang-song, LIU Hai-yan, LU Li-na. Software engineering[M].2nd ed. Xi’an: Xi’an Electronic and Science University Press,2004:83-93. (in Chinese)
[4] 普雷斯曼 R S.软件工程[M].郭肇德,郑少仁,译.北京:国防工业出版社,1988:248-269.
Prysmian R S.Software engineering[M].GUO Zhao-de,ZHENG Shao-ren,translated. Bejing:National Defense Industry Press,1988:248-269. (in Chinese)
[5] 郑人杰,殷仁昆.实用软件工程[M].北京:清华大学出版社,1991:202-220.
ZHENG Ren-jie, YIN Ren-kun. Practical software engineering[M]. Beijing: Tsinghua University Press,1991:202-220. (in Chinese)
[6] 谭浩强.C程序设计[M].3版.北京:清华大学出版社,2005:40-41.
TAN Hao-qiang. C programming design[M].3rd ed. Beijing: Tsinghua University Press,2005:40-41. (in Chinese)