实验14 基于彩铃平台开发个性化彩铃业务功能
e-Bridge 电信业务平台实验指导书
知识准备
1(熟悉SDK开发包的使用;
1(对数据库操作比较熟悉。
《SDK开发包帮助文档》
实验目的
1(对电话彩铃业务流程的了解;
2(熟练掌握数据库的操作;
3(熟练掌握SDK包的使用。
e-Bridge 电信业务平台实验指导书
训练内容
14.3.1硬件、软件准备
1、搭建好MyEclipse开发环境的pc机器一台和搭建好Linux测试环境的pc机器一台(可以使用虚拟机操作系统环境);
2、电话机一台,及电信业务开发平台硬件环境需要,彩铃铃音文件若干; 3、电信业务开发平台彩铃平台中已有的AIP_SMP模块,电信业务开发平台CSDP_ADAPT模块。
AIP_SMP模块:AIP_SMP模块为彩铃业务管理配置模块,在AIP_SMP管理模块中主要用来对彩铃平台的统一管理及基础数据的配置,还包括用户的管理和一些查询统计功能。 CSDP_ADAPT模块:CSDP_ADAPT模块为电信业务集成开发模块,主要提供API接口,方便我们程序调用,然后来控制语音设备(具体接口请参照《SDK开发包帮助文档》)。 14.3.2数据准备
在配置有测试环境的pc电脑上,创建并初始化彩铃平台所使用到的AIP_SCP数据库,配置AIP_SMP模块中铃音文件存放地址。
通过AIP_SMP模块平台,先进行电话号码开户的操作,及在现有平台的彩铃业务基础之上,为开户的电话号码设置个性化彩铃业务,为后面实验测试做准备。
实验设备及实验网络拓扑
无
实验步骤
e-Bridge 电信业务平台实验指导书
上图给出的是一个完整的两个电话号码用户之间从拔号到通话结束这段操
作的通信流程。而我们开发的的功能为在用户拔号时,被叫用户未摘机所释
放的铃音给主叫用户听。
从上面的电话流程图可以看出,我们要监听两个主被叫之间的连接及通话状态,并控制两个号码之间的连接状态。
在开发环境中,我们打开开发工具,然后导入aip_scp工程。
入口函数类:
package com.xunfang.aip.scp;
import com.xunfang.aip.scp.communication.SendMsgThread; import com.xunfang.aip.scp.communication.SocketComm;
e-Bridge 电信业务平台实验指导书 import com.xunfang.aip.scp.communication.RecvMsgThread;
import com.xunfang.aip.scp.config.Parse; import com.xunfang.csdp.sdk.agent.SdkService; import com.xunfang.csdp.sdk.logging.Log; import com.xunfang.csdp.sdk.xms.XMSProvider;
/**
*
Title: 讯方电信业务平台彩铃业务
*
Description: 系统启动
*
create: 2009-11-05
*
Copyright: Copyright (c) 2009
*
Company: 深圳市讯方通信技术有限公司
* @author 申毅杰
* @version 2.2.0.0
*/
public class ScpServer {
/**
* 配置文件解析方法
*/
public static void initConfig() {
// 解析配置文件
Parse parse = new Parse();
parse.init();
}
/**
* sdk服务
*/
static SdkService service = null;
/**
* 主函数
*
* @param args
*/
public static void main(String[] args) {
// 初始sdk服务
service = SdkService.getInstance();
// 解析配置文件
initConfig();
// 记录启动日志
System.out.println("彩铃业务平台V2.0.0.10 SCP系统启动");
e-Bridge 电信业务平台实验指导书
Log.debug("彩铃业务平台V2.0.0.10 SCP系统启动");
// 启动消息发送线程
new SendMsgThread().start();
// 启动接收消息处理线程
new RecvMsgThread().start();
// 打开设备
openXMS();
// 系统退出执行的操作
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
Log.debug("系统正在退出....");
}
});
// 主线程不退出
while (true) {
try {
Thread.sleep(10 * 60 * 1000);
Log.debug("主线程运行正常");
} catch (Exception e) {
Log.error(e);
}
}
}
/**
* 打开设备消息
*/
public static void openXMS() {
// 获得socket通信服务类
SocketComm comm = SocketComm.getInstance();
// 打开多媒体设备命令
byte[] openMsg = XMSProvider.xms_open("11866");
comm.sendMsg(openMsg);
}
}
通过流程图我们能够得到,AIP_SCP模块程序只与CSDP_ADAPT模块程序相互通信,所以我们可以将两个模块之间的通信划分为发送与接收,并且用两个独立的通信端口来实现两模
e-Bridge 电信业务平台实验指导书 块之间的信息发送与接收。
AIP_SCP接收CSDP_ADAPT消息线程:
package com.xunfang.aip.scp.communication;
import com.xunfang.aip.scp.business.MessageHandle;
import com.xunfang.csdp.sdk.logging.Log; import com.xunfang.csdp.sdk.socket.SDKMessage;
/**
* Title: 讯方电信业务平台彩铃业务
* Description: 接收消息线程处理类
* create: 2009-10-31
* Copyright: Copyright (c) 2009
* Company: 深圳市讯方通信技术有限公司
* @author 申毅杰
* @version 2.2.0.0
*/
public class RecvMsgThread extends Thread {
/**
* 通信服务类
*/
private SocketComm comm = null;
/**
* 接收消息线程
*/
public void run() {
comm = SocketComm.getInstance();
while (true) // 不停地接收信息进行处理
{
try {
int depth = comm.recvQueueDepth();
if(depth>0)
{
SDKMessage msg = comm.receiveMsg();
// 对数据信息解析并处理
if ( msg != null )
{
//启动业务处理线程,对消息进行解析并进行相应处理
new MessageHandle(msg).start();
continue;
}
}
e-Bridge 电信业务平台实验指导书
Thread.sleep(20);
} catch (Exception e) {
Log.error("接受消息处理线程", e);
}
}
}
}
AIP_SCP发送CSDP_ADAPT消息线程:
package com.xunfang.aip.scp.communication;
import com.xunfang.csdp.sdk.logging.Log;
/**
* Title: 讯方电信业务平台彩铃业务
* Description: 数据发送线程
* create: 2009-10-31
* Copyright: Copyright (c) 2009
* Company: 深圳市讯方通信技术有限公司
* @author 申毅杰
* @version 2.2.0.0
*/
public class SendMsgThread extends Thread {
/**
* socket通信类变量
*/
private SocketComm comm = null;
/**
* 线程函数
*/
public void run() {
/* 获取socket客户端 */
comm = SocketComm.getInstance();
while (true) {
try {
if(MessageCache.sendCache.getLength()==0)
{
Thread.sleep(20);
continue;
}
e-Bridge 电信业务平台实验指导书
byte[] message = (byte[])MessageCache.sendCache.getContent();
int sendLen = comm.sendMsg(message);
Log.debug("发送消息[" + sendLen + "]"+ new String(message));
} catch (Exception e) {
Log.error("发送消息错误", e);
Log.error(comm);
}
}
}
}
通过接收信息线程获取CSDP_ADAPT模块发送来的消息,并对该消息进行协议解析,消息解析后再根据消息报文的类型进行消息处理。
……省略
case 21:// 终端呼入
TrunkStruct trunk = MessageCache.initTrunk( callerorg, null, null, null, callerAddr, null, calleeorg,
calleeorg,null, null, calleeAddr, null, trunkNo, Constant.CALLIN, TrunkState.callin, recode);
MessageCache.displayTrunk("终端呼入, ", trunk);
new BusinessHandle( trunk ).start();
break;
……省略
当我们得到CSDP_ADAPT模块返回电话呼入状态后,那么我们调用一个业务处理类,对呼入业务进行处理,判断主叫号码与被叫号码是状态是否正常,即是否在我们电话号码在我们平台中有过开户状态(可通过查询平台数据库,来验证平台是否有号码开户),再根据双方的通道状态信息进行判断,当主叫号码与被叫号码双方建立了通道信息后,那么被号可以给主叫播放铃音了,在播放铃音前,我们要先判断被叫的铃音业务是什么:
……省略
/**
* 获取铃声路径
*/
public String getRingUrl( TrunkStruct trunk )
{
String caller = trunk.getCaller();
String calleeArea = trunk.getCalleeArea();
String callee = trunk.getCallee();
String nowDate = Tools.getNowDate();
long nowDateTime=Long.parseLong(Tools.getNow());
String ringurl = Parse.getConfig().getBasicRing();// 默认基本铃声路径
List
list=null;
try {
//根据被叫号码查询彩铃信息,并以彩铃优先级为条件降序排序
String sql = new StringBuffer().append(" select a.typepriority,a.typeid,b.phonenum,b.caller,b.comdate,b.starttime,b.endtime,c.realpath from
ec_ringtype a, ec_phoneringset b, ec_ringfile c ")
e-Bridge 电信业务平台实验指导书
.append(" where c.fileid=b.fileid and b.typeid = a.typeid and
b.phonenum='").append(callee+"'")
.append(" order by typepriority desc ").toString();
list = db.getList(sql);
for(String temp[]:list){
//0:基本彩铃 1:时间段彩铃 2:主叫彩铃 3:纪念日彩铃
String typeid=temp[1];
if("0".equals(typeid)){
ringurl=temp[7];
return ringurl;
}else if("1".equals(typeid)){
long startime=Long.parseLong(temp[5]);
long endtime=Long.parseLong(temp[6]);
if(nowDateTime>=startime&&nowDateTime<=endtime){
ringurl=temp[7];
return ringurl;
}
}else if("2".equals(typeid)){
String callerTemp=temp[3];
if(caller.equals(callerTemp)){
ringurl=temp[7];
return ringurl;
}
}else if("3".equals(typeid)){
String comdate=temp[4];
if(nowDate.equals(comdate)){
ringurl=temp[7];
return ringurl;
}
}
}
} catch (Exception ex) {
Log.error("增加主叫彩铃出错:", ex);
}
return ringurl;
}
……省略
以上为在AIP_SMP管理平台中对被叫电话号码进行个性化铃音设置,然后当主叫号码拔打被叫号码时,被叫号码则根据不同的铃音业务播放给主叫号码相应的铃音。在此我们可以设置一个播放铃音等待通话的时长,然后再根据此来进行是否建立双向通话通道连接,一旦建立通话通话,那么:
……省略
/**
* 处理类
e-Bridge 电信业务平台实验指导书
*/
public void deal() {
try {
// 初始化数据库连接池
db = new DBService();
db.setAutoCommit(false);
phonedb = new PhoneOperateDao(db); // 电话信息获取数据库服务
ringdb = new RingOperateDao(db); //彩铃信息获取数据库服务
// 判断主被叫号码及获取通话时长是否正常
if (!getConTime()) {
return;
}
// ----- 获得主叫铃音信息 -----
String callerRing = ringdb.getRingUrl( trunk );
PackMsg.playVoice( trunk.getTrunkNo(), "0", "0", "0", callerRing);
Log.info(trunk.getCallerorg()+ "|" + trunk.getCalleeorg());
String caller = trunk.getCallerorg();
String callee = trunk.getCalleeorg();
PackMsg.callOut(caller, trunk.getCallerAddr(), callee, trunk.getCalleeAddr() );
//初始化通道
trunkcallee = MessageCache.initTrunk( trunk.getCalleeorg(), trunk.getCallee(), trunk.getCalleeArea(), trunk.getCalleephy(),
trunk.getCalleeAddr(), null, trunk.getCallerorg(), trunk.getCaller(), trunk.getCallerArea(), trunk.getCallerphy(),
trunk.getCallerAddr(), null, -1, Constant.CALLOUT, TrunkState.callout, "000");
// 获取振铃是否成功,对应的通道号
MessageCache.displayTrunk("----start----waiting结果, ", trunkcallee.getTrunkNo(), trunkcallee.getCallerorg(), trunkcallee.getCalleeorg());
trunkcallee = MessageCache.waitforCallee(trunk.getCalleephy(), Constant.CALLOUT, trunk.getCallerorg(), TrunkState.calloutresult, trunk);
MessageCache.displayTrunk("----end----waiting结果, ", trunkcallee.getTrunkNo(), trunkcallee.getCallerorg(), trunkcallee.getCalleeorg());
if( trunkcallee.getState()!=TrunkState.calloutresult || trunk.getState()==TrunkState.hangup)//呼出没有返回结果
{
Log.info("呼出没有结果!" + trunkcallee.getState()+trunkcallee.getCaller()+" "+trunkcallee.getCallee());
if(trunkcallee.getState()== TrunkState.alerting&&trunk.getState()!= TrunkState.hangup){
trunkcallee.setState(TrunkState.hangup);
e-Bridge 电信业务平台实验指导书
}
Log.info("呼出没有结果!---被叫状态为" + trunkcallee.getState()+" 主叫状态为"+trunk.getState());
if( trunk.getState()== TrunkState.hangup){
Log.debug("呼出没有结果,主叫挂机,平台挂被叫:" + trunkcallee.getTrunkNo());
PackMsg.shutcall(trunkcallee);
}else if(trunkcallee.getState()== TrunkState.hangup){
Log.debug("呼出没有结果,被叫挂机,平台挂主叫:" + trunk.getTrunkNo());
PackMsg.shutcall(trunk);
}
return;
}
//根据返回码判断呼出
Log.info("呼出返回通道号:" + trunkcallee.getTrunkNo());
// 连接主、被叫号码
daulcon = daulLink();
Log.info("双向连接结果:" + daulcon);
if (daulcon) {
Log.debug(trunk.getTrunkNo() + "主叫连接L:" + trunk.getLinkNo() + "||||" +
trunkcallee.getTrunkNo() + "连接L:" + trunkcallee.getLinkNo());
long start = System.currentTimeMillis();
long now = start;
Log.debug("通话开始时间:" + start + ", 通话费用: " + connFee + ", 可通话时长:" + connTime);
MessageCache.displayTrunk("??????", trunk);
MessageCache.displayTrunk("??????", trunkcallee);
// 如果有电话挂机,则挂掉另外一个终端,并且完成计时
while (true) {
try {
Thread.sleep(200);
} catch (Exception e) {
Log.error("通话等待错误", e);
}
now = System.currentTimeMillis();
if (now - start < connTime * 1000) {// 允许通话时间内
if ( trunk.getState()==TrunkState.hangup ) {// 如果主叫通道挂机,则向被叫发送挂机命令,并跳出
Log.debug("主叫挂机,通话时长:" + (now - start));
PackMsg.shutcall(trunkcallee);
break;
}
e-Bridge 电信业务平台实验指导书
if ( trunkcallee.getState() == TrunkState.hangup ) {// 如果被叫通道挂机,则向主叫发送挂机命令,并跳出
Log.debug("被叫挂机,通话时长:" + (now - start));
PackMsg.shutcall(trunk);
break;
}
} else {// 否则挂机,并跳出
Log.debug("%%%%%%%%%%%%%%%%%%%%%%%%%%%主叫余额不足,通话结束时间:" + now);
PackMsg.shutcall(trunkcallee);
PackMsg.shutcall(trunk);
break;
}
}
// 记录数据库通话信息/
recodeCon(start, now, Tools.formatDate2(start), Tools.formatDate2(now));
Log.debug("记录通话信息:" + "开始时间:" + start + ",结束时间" + now);
} else {// 建立双向通道失败
if( trunk.getState()== TrunkState.hangup){
Log.debug("连接未成功,主叫挂机,平台挂被叫:" + trunkcallee.getTrunkNo());
PackMsg.shutcall(trunkcallee);
}else if(trunkcallee.getState()== TrunkState.hangup){
Log.debug("连接未成功,被叫挂机,平台挂主叫:" + trunk.getTrunkNo());
PackMsg.shutcall(trunk);
}
}
// 数据库提交
db.commit();
} catch (Exception e) {
Log.error("彩铃业务异常:", e);
db.rollback();
} finally {
if (db != null) {
db.close();
}
}
}
……省略
以上为一个彩铃业务控制的基本流程,它实现了如何控制主叫与被叫之间的连接,以达到两者电话间是否能够正常连通。需要完成以上实验步骤,那么需要对《SDK开发包帮助文档》很了解,及对整个呼叫业务流程的熟悉。
e-Bridge 电信业务平台实验指导书
实验验证
那么我们现在来验证一下,是否我们的程序有没有达到我们程序设计前的预期效果呢~ 首先,我们在测试环境中部署AIP_SMP程序模块,通过此模块平台,我们进行开户操作,增加两个电话用户号码。然后再为电话号码设置个性化彩铃业务。最近将我们编写的AIP_SCP模块程序进行打包处理,与CSDP_ADAPT程序模块一起,并按照配置文件说明进行配置,再上传至测试环境中。
然后按照流程顺序启动模块程序,先启动CSDP_ADAPT模块程序,再启动我们部署好的AIP_SCP模块程序。最近通过开户好的电话号码进行相互拔打测试,一个当主叫号码,一个当被叫号码,当主叫号码拔打被叫号码时,主叫号码所听到的振铃是否为被叫号码所设置的个性化彩铃铃音,此次验证通过后,我们再通过AIP_SMP来对被叫号码的个性化彩铃业务进行重新更改设置,然后再进行主被叫的电话测试,听铃音验证。
思考题
1、为什么在我们平台每次进行电话测试时在被叫号码前加特殊数字,
2、思考在除现有的个性化彩铃业务当中,还可以增加哪些彩铃业务,
3、在AIP_SCP模块中,为何要建立两个独立的通信与CSDP_ADAPT?