施月玲,宣 凯,张海平,孙志海
(1.杭州电子科技大学计算机学院,浙江 杭州310018;2.杭州电子科技大学信息工程学院,浙江 杭州310018)
现代公安应急通信系统具有通话频次高、位置相对固定、多方同时接听等特点,对讲机很好地适应了这些特点,并占了绝对地位[1]。但公安集群网络主要覆盖中心城市,覆盖不到偏远地区,出现很多盲区,不能完全满足工作需要。本文利用移动通信公网覆盖范围广的特点,实现对讲机与Android系统融合,利用后台调度系统,将集群网络的盲区用3G 网络来弥补,当终端处于无集群网络信号时,仍然能接收集群网络的对讲语音信息。
终端设备安装Android 主板,采用TI的A8 芯片,软件平台为Android 2.3.4系统,此外加装了公安部专用集群对讲板,支持MPT1327 信令,具有集群对讲功能和常规对讲功能两种;对讲板与Android 主板之间通过20Pin的软排线相连作为实时通信接口。
终端以集群对讲功能为主(常规与3G 对讲功能为辅),由于集群网络范围有限,当终端脱离集群网络时将自动(或手动)切换到3G 对讲模式,通过后台系统实现终端在3G 下与集群网络语音的转发,用户体验如同一直在集群网络状态,当终端检测到集群网络范围时,将自动(或手动)切换回集群(常规)APP。在特殊场合还能启用对讲机的常规对讲功能,实现同频对讲。
终端融合软件层次结构如图1所示,主要包括Linux层应用、SERVICE 程序、集群(常规)APP 三个部分。功能主要为:1)Linux层应用。与对讲板硬件直接交互,通过SERVICE控制集群(常规)APP与3G 对讲APP 进行切换;通过与集群(常规)APP的交互,实现集群拨号,守候组切换等各种控制;当上层APP 崩溃后自启动时,切换相关音频线路与按键控制权,确保产品能作为普通对讲机使用。2)SERVICE程序。负责与Linux层应用、集群(常规)APP以及3G 对讲APP 交互,实现自动或者手动模式切换;当集群(常规)APP 或者3G 对讲APP 崩溃后再次启动APP。3)集群(常规)APP。与Linux层应用交互、显示对讲机状态、设置各种参数以及提供拨号界面并将拨号信息传给Linux层应用操作对讲板拨号。4)3G 对讲APP。当集群网络信号不好,避免丢失语音信息时,替代对讲功能(需要后台系统的支持)。
Android系统在内存资源不足情况下,会结束部分APP以释放内存。然而作为一个定位在公安通信系统领域的产品,其功能绝对不能完全依托于APP(Android 应用程序),所以在设计时采用分层设计,让APP 不直接参与硬件控制,即使在使用中发生上层APP 被关闭,对讲功能仍能正常使用,使用者仍能像操作普通对讲机一样实现正常通信。
APP 整体框架如图2所示,其中基于Linux层的*.so 库文件使用JNI技术进行串口开发,实现上层APP与底层对讲板进行交互,交互方式通过AT 指令集来完成。
图1 融合通信终端架构图
图2 JNI 开发架构图
JNI是Java 本地的调用,可以使Java 同其他编程语言进行交互[1]。Android 编程使用JNI 调用C/C++语言编写的代码,实现同用户专门定制的硬件进行交互,或是执行一些更为复杂、耗时的操作,从而提高程序的执行效率。编程时在程序中加载对应的链接库,然后使用链接库中定义的方法。
本地方法是以库的形式存放(Windows系统中为*.dll 文件的形式,在Unix 机器上是*.so 文件的形式)。调用本地的库文件包含的方法,让Java可以实现与本地机器的紧密联系,从而调用系统级接口方法。
利用Java 同本地已编译的代码进行交互,会影响平台可移植性,但有些情况下必须要这么做。比如,使用一些旧版本的库、同硬件或底层操作系统进行交互,或为了提高程序自身的性能。JNI 能确保本地代码在Java 虚拟机中正常工作。
1)Java层功能。在Java 程序中,首先在类中导入程序执行所需要的链接库,声明所调用的库名称,再对要调用到的方法进行本地声明,关键字为native,只需要进行声明,不需要其具体的实现,然后编译Java 程序,生成class 文件,再使用javah 命令,JNI 就会生成对应的C/C++的头文件。
2)C/C++层功能。对已生成的头文件,C/C++需要把头文件中定义的方法进行具体的实现,然后编译链接成库文件,再把库文件复制到Java 程序的文件目录下面,用Java 调用C/C++所实现的方法。在具体实现的时候,主要有以下两种方法原型:
其中JNIEXPORT和JNICALL 都是JNI的关键字,即该函数将被JNI 调用。jint以JNI为中介,让Java中的int数据类型与本地的int数据类型进行映射,函数的名称则是“Java”加上java 程序的包名路径再加上函数名称组成。
Android NDK的全名是Android Native Development Kit,是Android SDK的补充与完善。Android NDK 用来编译Android 程序的JNI 代码部分,生成Android 程序需要的链接库文件。NDK 提供了很多能帮助开发人员快速开发动态库的工具,能自动将*.so 库文件和Java 代码一起打包成APK 文件。
因为Android是基于Linux的操作系统,要对串口进行控制,就需先开发出一个可以在用户空间执行的程序,使其在Linux系统的文件层对串口设备进行相关的读写、控制等操作。
JNI 程序遵循C/C++的语法,但此程序要作为Android 程序的库文件被Android 应用所调用,所以它的格式需要做一些改变[2]。
2.1.1 打开串口
首先进行波特率通信格式转换,然后打开设备,获取串口句柄,保存全局串口描述符,清除输入输出缓冲区,接着配置设备,把终端属性设置成原始属性,设置输入输出波特率和串行通信格式,创建一个文件描述符供上层使用[3-4],流程如图3所示。
图3 打开串口流程图
2.1.2 关闭串口
首先将输入输出缓冲区清零,然后释放文件描述符,其流程如图4所示。
2.1.3 设置串口数据位,停止位和校验位
数据位为7 或者8,停止位为1 或者2,校验位取值为N/E/O/S。
2.1.4 直接往串口发送数据
首先将数据转为C 字节,然后开辟内存空间,拷贝数据,写入串口,释放数据,其流程如图5所示。
图4 关闭串口流程图
图5 串口发送数据流程图
2.2.1 创建含有本地方法的类
此类中声明了需要调用的本地方法,其中的方法有打开串口、关闭串口、写数据、打开或者关闭对讲板等功能[5]。格式如下:
2.2.2 生成JNI 头文件
使用Java的Javah 命令生成一个*.so 头文件,这个头文件中给出了以上本地方法在C 语言中的声明,代码如下所示:
2.2.3 实现C 程序文件
使用C/C++语言来依次实现以上声明的方法,实现串口设备的打开、初始化、读、写、关闭等操作。
SerialPort.c 中包含了如下内容:
1)波特率通信格式转换的本地函数getBaudrate(jint baudrate);
2)打开串口的函数SerialPort _open (JNIEnv * env,jobject thiz,jstring path,jint baudrate);
3)关闭串口的函数SerialPort_close (JNIEnv * env,jobject thiz);
4)设置串口数据位、停止位和校验位的set_Parity(int fd,int databits,int stopbits,int parity);
5)用于控制底层I/O的Serial Port _GPIO (JNIEnv * env,jobject thiz,jint led_nu,jint on);
6)直接往串口发送数据的SerialPort_write (JNIEnv * env,jobject thiz,jstring at);
7)底层设备控制的SerialPort_writeint (JNIEnv * env,jobject thiz,jint onoff)。
本文开发的样机于2014年8月10日在某市公安局交警大队进行试用,经过2个月的测试运行,用户反映本产品软件界面简洁、使用方便、功能完善,样机方便携带,运行效果良好,解决了多网通信调度障碍问题。
本文结合对讲机与Android系统,利用Android的APP 来控制对讲机,交互界面友好。对讲功能由APP控制,同时与3G 对讲APP 一起实现了多网融合调度,经用户试用反馈良好,具有较强的工程应用价值。
[1]王林林.基于Android平台的集群通信系统终端软件的设计与实现[D].成都:西南交通大学,2012∶6-8.
[2]高海彬.JNI 在Android系统下串口控制的应用[J].信息技术,2013,37(10):173-176.
[3]张译恬,王纯.基于安卓系统JNI 机制的SO 库加固方案设计[J].电信技术,2014,(10):90-93.
[4]唐建东,卢贵主.嵌入式操作系统Linux中的串口应用编程[J].单片机与嵌入式系统应用,2002,(8):82-84.
[5]龚建伟,熊光明.Visual C++/Turbo C 串口通信编程实践[M].北京:电子工业出版社,2004:10-63.