#include "StdAfx.h" #include "FsProxy.h" #include "ChanExten.h" #include "ChanTrunk.h" #include "Session.h" #include "OperationReactor.h" #include "../../IVR/IvrSysInc.h" SINGLETON_IMPLEMENT(CFsProxy) CFsProxy::CFsProxy(void) : m_Gateway(this), m_pEventHandler(NULL) { } CFsProxy::~CFsProxy(void) { } /***************************************************************** **【函数名称】 __transLogicState2CtiState **【函数功能】 将逻辑状态转换为CTI识别的状态 **【参数】 **【返回值】 ****************************************************************/ UINT CFsProxy::__transLogicState2CtiState( DEV_RES_TYPE ChanType, CHAN_LOGIC_STATE State ) { if(ChanType == DEV_RES_TYPE_EXT) { UINT HoldMask = State & HELD_STATE_IND_MASK; switch(State & HELD_STATE_FILTER_MASK) { case CHAN_LOGIC_STATE_DISABLED: return INNER_STATE_DISABLED; // 不可用 case CHAN_LOGIC_STATE_FREE: return INNER_STATE_FREE | HoldMask; // 空闲 case CHAN_LOGIC_STATE_INIT: return INNER_STATE_INIT | HoldMask; // 摘机等待拨号 case CHAN_LOGIC_STATE_DIALING: return INNER_STATE_DIALING | HoldMask; // 拨号 case CHAN_LOGIC_STATE_RING_BACK: return INNER_STATE_RING_BACK | HoldMask; // 呼出振铃 case CHAN_LOGIC_STATE_ALERTING: return INNER_STATE_ALERTING | HoldMask; // 来电振铃 case CHAN_LOGIC_STATE_TALKING: return INNER_STATE_TALKING | HoldMask; // 通话中 default: ASSERT(FALSE); return INNER_STATE_DISABLED; } } else { switch(State) { case CHAN_LOGIC_STATE_DISABLED: return TRUNK_STATE_DISABLED; // 不可用 case CHAN_LOGIC_STATE_FREE: return TRUNK_STATE_FREE; // 空闲 case CHAN_LOGIC_STATE_DIALING: return TRUNK_STATE_DIALING; // 拨号 case CHAN_LOGIC_STATE_RING_BACK: return TRUNK_STATE_RING_BACK; // 呼出振铃 case CHAN_LOGIC_STATE_ALERTING: return TRUNK_STATE_ALERTING; // 来电振铃 case CHAN_LOGIC_STATE_STANDBY: case CHAN_LOGIC_STATE_TALKING: return TRUNK_STATE_TALKING; // 通话中 default: ASSERT(FALSE); return TRUNK_STATE_UNKNOWN; } } } /***************************************************************** **【函数名称】 __addExten **【函数功能】 添加分机 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::__addExten( UINT ExtenNo ) { CChanExten* pExten = new CChanExten(this, ExtenNo); m_MapChanExt.SetAt(ExtenNo, pExten); pExten->regist(); } /***************************************************************** **【函数名称】 delExten **【函数功能】 删除分机 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::__delExten( UINT ExtenNo ) { CChanExten* pExten = NULL; if(m_MapChanExt.Lookup(ExtenNo, pExten)) { if(pExten->isFree()) { m_MapChanExt.RemoveKey(ExtenNo); FS_LINK_DELETE(pExten); } else pExten->discard(true); } } /***************************************************************** **【函数名称】 __freeExten **【函数功能】 清空分机 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::__freeExten( void ) { UINT ExtenNo = 0; CChanExten* pExten = NULL; POSITION Pos = m_MapChanExt.GetStartPosition(); while(Pos != NULL) { m_MapChanExt.GetNextAssoc(Pos, ExtenNo, pExten); FS_LINK_DELETE(pExten); } m_MapChanExt.RemoveAll(); } /***************************************************************** **【函数名称】 __initTrunkChan **【函数功能】 初始化中继通道 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::__initTrunkChan( void ) { for(int i = 1; i <= CConfig::trunkCount(); ++i) { CChanTrunk* pTrunk = new CChanTrunk(this, i); m_ArrayTrunk.Add(pTrunk); pTrunk->regist(); } } /***************************************************************** **【函数名称】 __freeTrunkChan **【函数功能】 清空中继通道 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::__freeTrunkChan( void ) { CChanTrunk* pTrunk = NULL; for(int i = 0; i < m_ArrayTrunk.GetCount(); ++i) { pTrunk = m_ArrayTrunk[i]; FS_LINK_DELETE(pTrunk); } m_ArrayTrunk.RemoveAll(); } /***************************************************************** **【函数名称】 __getSession **【函数功能】 查找会话 **【参数】 **【返回值】 ****************************************************************/ CSession* CFsProxy::__getSession( PCHAN_EVENT_NOTIFY pNotify, bool NewWhenNull /*= false*/ ) { CSession* pSession = __getSession(pNotify->CallId); if(pSession == NULL && NewWhenNull) { pSession = new CSession(this, pNotify->CallId); pSession->prepare(pNotify); m_MapSession.SetAt(pNotify->CallId, pSession); std::cout << "m_MapSession.SetAt" << pNotify->CallId << endl; std::cout << "m_MapSession.SetAt" << pSession->id() << endl; LOGGER(LOG_LEVEL_NORMAL,_T("{CFsProxy}:Add Session[%s],ChanId[%s],EventId[%d],CallID[%s]"), pSession->id(), pNotify->ChanId, pNotify->EventId,pNotify->CallId); } return pSession; } /***************************************************************** **【函数名称】 __getSession **【函数功能】 查找会话 **【参数】 **【返回值】 ****************************************************************/ CSession* CFsProxy::__getSession( LPCTSTR SessionId ) { CSession* pSession = NULL; m_MapSession.Lookup(SessionId, pSession); std::cout << "__getSession" << SessionId << endl; return pSession; } /***************************************************************** **【函数名称】 __delSession **【函数功能】 删除会话 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::__delSession( LPCTSTR SessionId ) { if (SessionId == NULL) return; CSession* pSession = NULL; if(m_MapSession.Lookup(SessionId, pSession)) { m_MapSession.RemoveKey(SessionId); LOGGER(LOG_LEVEL_NORMAL, _T("{FsProxy}: Delete Session[%s]"), pSession->id()); FS_LINK_DELETE(pSession); } } /***************************************************************** **【函数名称】 __freeSession **【函数功能】 清空会话 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::__freeSession( void ) { CString SessionId = 0; CSession* pSession = NULL; POSITION Pos = m_MapSession.GetStartPosition(); while(Pos != NULL) { m_MapSession.GetNextAssoc(Pos, SessionId, pSession); FS_LINK_DELETE(pSession); } m_MapSession.RemoveAll(); } /***************************************************************** **【函数名称】 __kill **【函数功能】 挂断通道 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::__kill( LONG JobId, LPCTSTR ChanId ) { CString EslCmd; if(JobId == FS_LINK_JOBID_INVALID) EslCmd.Format(_T("bgapi uuid_kill %s"), ChanId); else EslCmd.Format(_T("bgapi uuid_kill %s\r\n%s: %ld"), ChanId, ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 init **【函数功能】 初始化 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::init( IEslEventHandler* pEventHandler ) { ASSERT(pEventHandler != NULL); m_pEventHandler = pEventHandler; __initTrunkChan(); if(m_Gateway.start()) { m_Gateway.hangupAll(); if(m_Gateway.scanExten()) { LOGGER(LOG_LEVEL_NORMAL, _T("{FsProxy}: FreeSWITCH代理初始化成功")); return true; } else { LOGGER(LOG_LEVEL_ERROR, _T("{FsProxy}: FreeSWITCH代理初始化失败, 扫描分机资源失败")); return false; } } else { LOGGER(LOG_LEVEL_ERROR, _T("{FsProxy}: FreeSWITCH代理初始化失败, ESL网关启动失败")); m_pEventHandler = NULL; return false; } } /***************************************************************** **【函数名称】 release **【函数功能】 释放资源 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::release( void ) { // by 临时添加的esl_handle_t句柄 esl_handle_t m_EslHandle; esl_disconnect(&m_EslHandle); m_Gateway.stop(); __freeSession(); __freeExten(); __freeTrunkChan(); } /***************************************************************** **【函数名称】 onChanRegist **【函数功能】 通道注册的处理函数 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::onChanRegist( DEV_RES_TYPE ChanType, UINT ChanNo, CHAN_LOGIC_STATE ChanState ) { CDevControl::GetInstance().onEventResDetail(ChanType, ChanNo); CDevControl::GetInstance().onEventResState(ChanType, ChanNo, __transLogicState2CtiState(ChanType, ChanState)); } /***************************************************************** **【函数名称】 onExtenDestroy **【函数功能】 分机销毁的处理函数 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::onExtenDestroy( UINT ExtenNo ) { CDevControl::GetInstance().onEventResState(DEV_RES_TYPE_EXT, ExtenNo, INNER_STATE_REMOVE); } /***************************************************************** **【函数名称】 onExtenDirectOp **【函数功能】 分机直接操作启动 **【参数】 OpType 启动的操作类型 pHostChan 触发事件的通道 pNotify 触发操作事件内容 **【返回值】 ****************************************************************/ void CFsProxy::onExtenDirectOp( DEV_OP OpType, CVirtualChan* pHostChan, PCHAN_EVENT_NOTIFY pNotify ) { switch(OpType) { case DEV_OP_CALL_OUT: { if(!COperationReactor::GetInstance().onExtenCallFromDev(pHostChan, pNotify)) __kill(FS_LINK_JOBID_INVALID, pHostChan->chanId()); } break; } // end switch } /***************************************************************** **【函数名称】 onChanStateUpdate **【函数功能】 通道状态更新处理 **【参数】 **【返回值】 *****************************************************************/ void CFsProxy::onChanStateUpdate( LONG OpInstance, CVirtualChan* pChan ) { if(OpInstance != FS_LINK_INSTANCE_INVALID) m_pEventHandler->onEslEvtChanState(OpInstance, pChan); CDevControl::GetInstance().onEventResState(pChan->type(), pChan->no(), __transLogicState2CtiState(pChan->type(), pChan->state()), pChan->callerNum(), pChan->calleeNum()); } /***************************************************************** **【函数名称】 onChanPoor **【函数功能】 空闲通道枯竭处理 **【参数】 **【返回值】 *****************************************************************/ void CFsProxy::onChanPoor( CSession* pSession, PCHAN_EVENT_NOTIFY pNotify ) { __kill(FS_LINK_JOBID_INVALID, pNotify->ChanId); } /***************************************************************** **【函数名称】 onTrunkCallIn **【函数功能】 中继呼入 **【参数】 **【返回值】 *****************************************************************/ void CFsProxy::onTrunkCallIn( CChanTrunk* pTrunk ) { CDevControl::GetInstance().onEventDevOperation(pTrunk->no(), DEV_OP_CALL_IN, pTrunk->callerNum(), pTrunk->calleeNum()); } /***************************************************************** **【函数名称】 onEslDisconnect **【函数功能】 ESL连接中断处理 **【参数】 **【返回值】 *****************************************************************/ void CFsProxy::onEslDisconnect( void ) { CDevControl::GetInstance().onEventDevDown(); } /***************************************************************** **【函数名称】 onEslSipReg **【函数功能】 分机注册的处理函数 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::onEslExtenReg( UINT ExtenNo ) { CChanExten* pExten = getExten(ExtenNo); if(pExten == NULL) { __addExten(ExtenNo); } else { if(pExten->isVoid()) // 若被丢弃,则改变丢弃状态 pExten->discard(false); } #ifdef _DEBUG LOGGER(LOG_LEVEL_NORMAL, _T("{FsProxy}: 分机[%u]注册"), ExtenNo); #endif } /***************************************************************** **【函数名称】 onEslExtenUnreg **【函数功能】 分机注销的处理函数 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::onEslExtenUnreg( UINT ExtenNo ) { __delExten(ExtenNo); #ifdef _DEBUG LOGGER(LOG_LEVEL_NORMAL, _T("{FsProxy}: 分机[%u]注销"), ExtenNo); #endif } /***************************************************************** **【函数名称】 onEslGwExten **【函数功能】 网关分机的处理函数 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::onEslGwExtenReg(LPCTSTR ExtenNo, LPCTSTR Ip) { m_MapAddrGwExt.SetAt(ExtenNo, Ip); return; } void CFsProxy::onEslGwExtenUnreg(LPCTSTR ExtenNo) { CString t_ip; if (m_MapAddrGwExt.Lookup(ExtenNo, t_ip)) { t_ip.Empty(); m_MapAddrGwExt.RemoveKey(ExtenNo); } } /***************************************************************** **【函数名称】 onEslEvtBgJobDone **【函数功能】 后台任务执行结束事件处理 **【参数】 **【返回值】 *****************************************************************/ void CFsProxy::onEslEvtBgJobDone( PBG_JOB_NOTIFY pNotify ) { ASSERT(m_pEventHandler != NULL); m_pEventHandler->onEslEvtBgJobDone(pNotify); } /***************************************************************** **【函数名称】 onEslEvtChannel **【函数功能】 通道事件处理 **【参数】 **【返回值】 *****************************************************************/ void CFsProxy::onEslEvtChannel( PCHAN_EVENT_NOTIFY pNotify ) { ASSERT(pNotify != NULL); if (pNotify == NULL) return; CSession* pSession = __getSession(pNotify, true); ASSERT(pSession != NULL); if (pSession == NULL) { LOGGER(LOG_LEVEL_WARNING, _T("{FsProxy}: 通道事件,没有找到会话,EslEventId=%d,ChanId[%s],CallId[%s],Caller[%s],Callee[%s]"), pNotify->EventId, pNotify->ChanId, pNotify->CallId, pNotify->Caller, pNotify->Callee); return; } else { std::cout << "m_MapSession.SetAt" << pNotify->CallId << endl; std::cout << "m_MapSession.SetAt" << pSession->id() << endl; } __try { pSession->onChanEvent(pNotify); if (pSession->isVoid()) __delSession(pSession->id()); } __except (EXCEPTION_EXECUTE_HANDLER) { LOGGER(LOG_LEVEL_WARNING, _T("{FsProxy}: 处理通道事件时异常[%u],EslEventId=%d,ChanId[%s],CallId[%s],Caller[%s],Callee[%s]"), GetExceptionCode(), pNotify->EventId, pNotify->ChanId, pNotify->CallId, pNotify->Caller, pNotify->Callee); __delSession(pSession->id()); } } /***************************************************************** **【函数名称】 onEslEvtDtmf **【函数功能】 DTMF事件处理 **【参数】 **【返回值】 *****************************************************************/ void CFsProxy::onEslEvtDtmf( PDTMF_NOTIFY pNotify ) { ASSERT(pNotify != NULL); CSession* pSession = __getSession(pNotify->CallId); ASSERT(pSession != NULL); if(pSession != NULL) pSession->onChanDtmf(pNotify); } /***************************************************************** **【函数名称】 onEslEvtHold **【函数功能】 保持事件处理 **【参数】 **【返回值】 *****************************************************************/ void CFsProxy::onEslEvtHold( PHOLD_NOTIFY pNotify ) { ASSERT(pNotify != NULL); CSession* pSession = __getSession(pNotify->CallId); ASSERT(pSession != NULL); if(pSession != NULL) pSession->onChanHold(pNotify); } /***************************************************************** **【函数名称】 getExten **【函数功能】 查找分机 **【参数】 **【返回值】 ****************************************************************/ CChanExten* CFsProxy::getExten( UINT ExtenNo ) { CChanExten* pExten = NULL; m_MapChanExt.Lookup(ExtenNo, pExten); return pExten; } /***************************************************************** **【函数名称】 getTrunk **【函数功能】 查找中继 **【参数】 **【返回值】 ****************************************************************/ CChanTrunk* CFsProxy::getTrunk( UINT TrunkNo ) { if (TrunkNo <= 0 || TrunkNo > (UINT)m_ArrayTrunk.GetCount()) return NULL; --TrunkNo; return m_ArrayTrunk[TrunkNo]; } /***************************************************************** **【函数名称】 delChan **【函数功能】 删除通道 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::delChan( CVirtualChan* pChan ) { if(pChan->type() == DEV_RES_TYPE_EXT) __delExten(pChan->no()); } /***************************************************************** **【函数名称】 getFreeTrunk **【函数功能】 查找空闲中继 **【参数】 **【返回值】 ****************************************************************/ CChanTrunk* CFsProxy::getFreeTrunk( void ) { // 当前轮循到的索引 static int PosStatic = 0; int CurPos = PosStatic; int BusyTruckCount = 0; // 保证遍历一轮 for(int i = 0; i < m_ArrayTrunk.GetCount(); ++i) { // 保证POS是有效的 if(PosStatic >= m_ArrayTrunk.GetCount()) PosStatic = 0; CChanTrunk* pTrunk = m_ArrayTrunk[PosStatic++]; ASSERT(pTrunk != NULL); // 当前外线是否空闲 if (pTrunk->isFree()) { return pTrunk; } else { BusyTruckCount++; } } LOGGER(LOG_LEVEL_WARNING, _T("{FsProxy}: 没有空闲中继通道,起始索引=%d,当前索引=%d,中继总数量=%d,忙通道数量=%d"), CurPos, PosStatic, m_ArrayTrunk.GetCount(), BusyTruckCount); //2019.3.7 PosStatic = CurPos; for (int i = 0; i < m_ArrayTrunk.GetCount(); ++i) { // 保证POS是有效的 if (PosStatic >= m_ArrayTrunk.GetCount()) PosStatic = 0; CChanTrunk* pTrunk = m_ArrayTrunk[PosStatic++]; ASSERT(pTrunk != NULL); // 当前外线是否空闲 if (pTrunk->state() == CHAN_LOGIC_STATE_FREE) { if (pTrunk->currOp() != NULL) { COperationReactor::GetInstance().releaseOpResult(pTrunk->currOp()); pTrunk->releaseOp(pTrunk->currOp()); LOGGER(LOG_LEVEL_WARNING, _T("{FsProxy}:没有空闲中继通道,通道[%u],释放操作."), pTrunk->no()); } return pTrunk; } } LOGGER(LOG_LEVEL_WARNING, _T("{FsProxy}: 没有空闲中继通道,起始索引=%d,当前索引=%d"), CurPos, PosStatic); return NULL; } /***************************************************************** **【函数名称】 getAssoChanInSession **【函数功能】 获取会话中关联通道 **【参数】 **【返回值】 ****************************************************************/ CVirtualChan* CFsProxy::getAssoChanInSession( CVirtualChan* pChan ) { CSession* pSession = __getSession(pChan->sessionId()); if(pSession != NULL) return pSession->getAssoChan(pChan); else return NULL; } /***************************************************************** **【函数名称】 getBusyChan **【函数功能】 获取忙通道 **【参数】 **【返回值】 *****************************************************************/ CVirtualChan* CFsProxy::getBusyChan( LPCTSTR ChanId ) { CVirtualChan* pBusyChan = NULL; m_MapBusyChan.Lookup(ChanId, pBusyChan); return pBusyChan; } /***************************************************************** **【函数名称】 regBusyChan **【函数功能】 登记忙通道 **【参数】 **【返回值】 *****************************************************************/ void CFsProxy::regBusyChan( CVirtualChan* pChan ) { m_MapBusyChan.SetAt(pChan->chanId(), pChan); } /***************************************************************** **【函数名称】 unregBusyChan **【函数功能】 取消登记忙通道 **【参数】 **【返回值】 *****************************************************************/ void CFsProxy::unregBusyChan( CVirtualChan* pChan ) { m_MapBusyChan.RemoveKey(pChan->chanId()); } /***************************************************************** **【函数名称】 ExtenCall **【函数功能】 分机呼叫 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::ExtenCall( LONG JobId, CVirtualChan* pChan, LPCTSTR CallerNum, LPCTSTR CalleeNum ) { CString EslCmd; EslCmd.Format(_T("bgapi originate {origination_caller_id_number=%lu}user/%lu %s XML %s\r\n%s: %ld"), pChan->no(), pChan->no(), CalleeNum, CConfig::extContext(), ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd); } bool CFsProxy::ExtenCall(CVirtualChan* pChan) { CString EslCmd; EslCmd.Format(_T("bgapi originate user/1014 &echo\n\n")); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 PredictionCall **【函数功能】 预测外呼 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::PredictionCall( LONG JobId, CString CallerNum, CString CalleeNum ) { CString CallString; if(!m_CallStringMaker.makeCallStringWithDefault(CallerNum, CalleeNum, CallString)) return false; CString EslCmd; EslCmd.Format(_T("bgapi originate {origination_caller_id_number=%s,%s=%ld}%s %s XML %s\r\n%s: %ld"), CallerNum, ESL_VAR_OP_INSTANCE, JobId, CallString, CalleeNum, CConfig::pCallContext(), ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 answer **【函数功能】 应答通道 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::answer( LONG JobId, CVirtualChan* pChan ) { return false; } /***************************************************************** **【函数名称】 kill **【函数功能】 挂断通道 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::kill( LONG JobId, CVirtualChan* pChan ) { return __kill(JobId, pChan->chanId()); } /***************************************************************** **【函数名称】 consult **【函数功能】 协商呼叫 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::consult( LONG JobId, CVirtualChan* pChan, CString DestNum ) { CString CallerNum; CString CallString; if(!m_CallStringMaker.makeCallString(CallerNum, DestNum, CallString)) return false; CString EslCmd; EslCmd.Format(_T("bgapi uuid_broadcast %s att_xfer::%s\r\n%s: %ld"), pChan->chanId(), CallString, ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 insert **【函数功能】 强插 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::insert( LONG JobId, CVirtualChan* pChan, LPCTSTR DestSessionId ) { CString EslCmd; EslCmd.Format(_T("bgapi originate {origination_caller_id_number=%lu}user/%lu &three_way(%s)\r\n%s: %ld"), pChan->no(), pChan->no(), DestSessionId, ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 intercept **【函数功能】 强截 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::intercept( LONG JobId, CVirtualChan* pChan, LPCTSTR DestChanId ) { CString EslCmd; EslCmd.Format(_T("bgapi originate {origination_caller_id_number=%lu}user/%lu &intercept(%s)\r\n%s: %ld"), pChan->no(), pChan->no(), DestChanId, ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 listen **【函数功能】 监听 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::listen( LONG JobId, CVirtualChan* pChan, LPCTSTR DestChanId ) { CString EslCmd; EslCmd.Format(_T("bgapi originate {origination_caller_id_number=%lu}user/%lu &eavesdrop(%s)\r\n%s: %ld"), pChan->no(), pChan->no(), DestChanId, ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd); } // ych std::string CFsProxy::EslCommand(std::string strFsIp, int FsPort, std::string strName, std::string strPsWd, std::string strCommand) { if (!m_EslHandle.connected) { esl_connect(&m_EslHandle, strFsIp.c_str(), FsPort, strName.c_str(), strPsWd.c_str()); } if (m_EslHandle.connected) { esl_send_recv_timed(&m_EslHandle, strCommand.c_str(), 2000); if (m_EslHandle.last_sr_event && m_EslHandle.last_sr_event->body) { std::string strRet = m_EslHandle.last_sr_event->body; return strRet; } } return ""; } /***************************************************************** **【函数名称】 meeting **【函数功能】 会议 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::meeting( LONG JobId, CString CallerNum, CString DestNum, LPCTSTR MeetingId ) { bool bMatchPrefix = m_CallStringMaker.isMatchPrefix(DestNum); TRUNK_MATCH* pMatch = NULL; CString CallString; if (!m_CallStringMaker.makeCallString(CallerNum, DestNum, CallString, pMatch)) return false; if (pMatch != NULL) { SIP_ACCOUNT* pAccount = CConfig::sipAccount().getAccount(pMatch->TrunkItemId); if (bMatchPrefix && pAccount != NULL && pAccount->IsDynamicGw) // 外线且动态网关 { std::string strSipAccount = pAccount->Account; std::string strGwIp = EslCommand(CConfig::fsAddr(), CConfig::fsPort(), "", CConfig::fsPwd(), "api sofia_contact " + strSipAccount); if (!strGwIp.empty()) { size_t Index = strGwIp.find_first_of('@'); if (Index != std::string::npos) { strGwIp = strGwIp.substr(Index); CallString = "sofia/internal/"; CallString += DestNum; CallString += strGwIp.c_str(); } } } } LOGGER(LOG_LEVEL_NORMAL, _T("{FsProxy}: 三方会议呼叫字符串: %s"), CallString); CString EslCmd; EslCmd.Format(_T("bgapi originate {origination_caller_id_number=%s,%s=%ld}%s %s XML %s\r\n%s: %ld"), CallerNum, ESL_VAR_OP_INSTANCE, JobId, CallString, MeetingId, CConfig::meetingContext(), ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 muteOn **【函数功能】 静音 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::muteOn( LONG JobId, CVirtualChan* pChan ) { CString EslCmd; EslCmd.Format(_T("bgapi uuid_audio %s start write mute -4\r\n%s: %ld"), pChan->chanId(), ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 muteOff **【函数功能】 取消静音 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::muteOff( LONG JobId, CVirtualChan* pChan ) { CString EslCmd; EslCmd.Format(_T("bgapi uuid_audio %s stop write mute -4\r\n%s: %ld"), pChan->chanId(), ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 holdon **【函数功能】 保持 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::holdon( LONG JobId, CVirtualChan* pChan ) { // by 屏蔽了uuid_hold命令以及sendCmd函数 CString EslCmd; EslCmd.Format(_T("bgapi uuid_hold %s\r\n%s"), pChan->chanId(),ESL_HEADER_JOB_UUID); return m_Gateway.sendCmd(EslCmd); /*EslCmd.Format(_T("bgapi uuid_hold %s\r\n%s: %ld"), pChan->chanId(), ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd);*/ } /***************************************************************** **【函数名称】 takeBack **【函数功能】 接回 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::takeBack( LONG JobId, CVirtualChan* pChan ) { CString EslCmd; EslCmd.Format(_T("bgapi uuid_hold off %s\r\n%s: %ld"), pChan->chanId(), ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 record **【函数功能】 录音 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::record( LONG JobId, CVirtualChan* pChan, LPCTSTR RcdFile ) { CString EslCmd; EslCmd.Format(_T("bgapi uuid_record %s start %s\r\n%s: %ld"), pChan->chanId(), RcdFile, ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 transfer **【函数功能】 呼叫转移 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::transfer( LONG JobId, CVirtualChan* pChan, LPCTSTR DestNum ) { CString EslCmd; EslCmd.Format(_T("bgapi uuid_transfer %s %s xml %s\r\n%s: %ld"), pChan->chanId(), DestNum, CConfig::extContext(), ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 transfer2Context **【函数功能】 转移通道至其它Context **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::transfer2Context( LONG JobId, LPCTSTR DestChanId, LPCTSTR Exten, LPCTSTR Context, bool BothSide /*= false*/ ) { CString EslCmd; if(BothSide) EslCmd.Format(_T("bgapi uuid_transfer %s -both %s xml %s\r\n%s: %ld"), DestChanId, Exten, Context, ESL_HEADER_JOB_UUID, JobId); else EslCmd.Format(_T("bgapi uuid_transfer %s %s xml %s\r\n%s: %ld"), DestChanId, Exten, Context, ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 cancel **【函数功能】 取消当前执行的应用 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::cancel( CChanTrunk* pChan ) { ASSERT(pChan != NULL); return m_Gateway.Execte2(ESL_APP_BREAK,NULL,pChan->chanId()); } /***************************************************************** **【函数名称】 hangup **【函数功能】 挂机 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::hangup( CChanTrunk* pChan ) { ASSERT(pChan != NULL); return m_Gateway.execute(pChan->m_pEslHandle,ESL_APP_HANGUP,NULL); } /***************************************************************** **【函数名称】 leaveWord **【函数功能】 留言 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::leaveWord( CChanTrunk* pChan, LPCTSTR FileName, UINT LimitTime, TCHAR FinishKey ) { ASSERT(pChan != NULL); CString Param; Param.Format(_T("playback_terminators=%c"), FinishKey); m_Gateway.Execte2(ESL_APP_SET,Param,pChan->chanId()); Param.Format(_T("%s %lu"), FileName, LimitTime); return m_Gateway.Execte2(ESL_APP_LEAVEWORD,Param,pChan->chanId()); } /***************************************************************** **【函数名称】 playAndDtmf **【函数功能】 放音收按键 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::playAndDtmf( CChanTrunk* pChan, PlayVoiceContent* pPlayContent ) { ASSERT(pChan != NULL); ASSERT(pPlayContent != NULL); if(pChan==NULL||pPlayContent==NULL) return false; TCHAR AudioFile[MAX_PATH] = { 0 }; // 如果使用了TTS,进行TTS转换 switch(pPlayContent->nTts) { default: case PLAY_CONTENT_AUDIO: // 不使用TTS lstrcpy(AudioFile, pPlayContent->szFileName); break; case PLAY_CONTENT_TTS_STR: // TTS文本 { ITtsInterface::getInstance().setTTSParam(pPlayContent->nTtsDigitMode, pPlayContent->nTtsSpeed, pPlayContent->nTtsVolume); if(!ITtsInterface::getInstance().string2Audio(pPlayContent->szFileName, AudioFile, MAX_PATH)) { LOGGER(LOG_LEVEL_WARNING, _T("{FsProxy}: 中继通道[%lu]放音时TTS文本转换失败"), pChan->no()); return false; } } break; case PLAY_CONTENT_TTS_FILE: // TTS文件 { ITtsInterface::getInstance().setTTSParam(pPlayContent->nTtsDigitMode, pPlayContent->nTtsSpeed, pPlayContent->nTtsVolume); if(!ITtsInterface::getInstance().file2Audio(pPlayContent->szFileName, AudioFile, MAX_PATH)) { LOGGER(LOG_LEVEL_WARNING, _T("{FsProxy}: 中继通道[%lu]放音时TTS文件转换失败, File = %s"), pChan->no(), pPlayContent->szFileName); return false; } } break; } CString Param; if(pPlayContent->nModel == PVM_PLAY_ONLY && pPlayContent->cDtmfEnd == 0) { Param.Format(_T("playback_terminators=none")); m_Gateway.Execte2(ESL_APP_SET,Param,pChan->chanId()); //m_Gateway.execute(pChan->m_pEslHandle,ESL_APP_SET,Param); Param.Format(_T("%s"), AudioFile); //return m_Gateway.Execte2(ESL_APP_PLAY,Param,pChan->chanId()); return m_Gateway.execute(pChan->m_pEslHandle,ESL_APP_PLAY,Param); } else { Param.Format(_T("%lu %lu 1 %lu %c %s slience_stream:0 %s"), pPlayContent->nDtmfCount, pPlayContent->nDtmfCount, pPlayContent->nDtmfCount * pPlayContent->nDtmfPeriod * 1000, pPlayContent->cDtmfEnd, AudioFile, ESL_VAR_DTMF_KEY); return m_Gateway.execute(pChan->m_pEslHandle,ESL_APP_PLAY_DTMF,Param); } } //by 2019-09-11 bool CFsProxy::ExtenPlayback(LONG JobId, CChanExten* pChan, PlayVoiceContent* pPlayContent) { CString EslCmd; EslCmd.Format(_T("bgapi uuid_playback %s start %s\r\n%s: %ld"), pChan->chanId(), pPlayContent->szFileName, ESL_HEADER_JOB_UUID, JobId); return m_Gateway.sendCmd(EslCmd);; } bool CFsProxy::playAndDtmf(CChanExten* pChanExten, PlayVoiceContent* pPlayContent) { ASSERT(pChan != NULL); ASSERT(pPlayContent != NULL); if (pChanExten == NULL || pPlayContent == NULL) return false; TCHAR AudioFile[MAX_PATH] = { 0 }; // 如果使用了TTS,进行TTS转换 switch (pPlayContent->nTts) { default: case PLAY_CONTENT_AUDIO: // 不使用TTS lstrcpy(AudioFile, pPlayContent->szFileName); break; case PLAY_CONTENT_TTS_STR: // TTS文本 { ITtsInterface::getInstance().setTTSParam(pPlayContent->nTtsDigitMode, pPlayContent->nTtsSpeed, pPlayContent->nTtsVolume); if (!ITtsInterface::getInstance().string2Audio(pPlayContent->szFileName, AudioFile, MAX_PATH)) { LOGGER(LOG_LEVEL_WARNING, _T("{FsProxy}: 中继通道[%lu]放音时TTS文本转换失败"), pChanExten->no()); return false; } } break; case PLAY_CONTENT_TTS_FILE: // TTS文件 { ITtsInterface::getInstance().setTTSParam(pPlayContent->nTtsDigitMode, pPlayContent->nTtsSpeed, pPlayContent->nTtsVolume); if (!ITtsInterface::getInstance().file2Audio(pPlayContent->szFileName, AudioFile, MAX_PATH)) { LOGGER(LOG_LEVEL_WARNING, _T("{FsProxy}: 中继通道[%lu]放音时TTS文件转换失败, File = %s"), pChanExten->no(), pPlayContent->szFileName); return false; } } break; } CString Param; if (pPlayContent->nModel == PVM_PLAY_ONLY && pPlayContent->cDtmfEnd == 0) { Param.Format(_T("playback_terminators=none")); m_Gateway.Execte2(ESL_APP_SET, Param, pChanExten->chanId()); Param.Format(_T("%s"), AudioFile); return m_Gateway.Execte2(ESL_APP_PLAY_DTMF, Param, pChanExten->chanId()); } else { /*Param.Format(_T("playback_terminators=none")); m_Gateway.Execte2(ESL_APP_SET, Param, pChanExten->chanId());*/ Param.Format(_T("%s"), AudioFile); return m_Gateway.Execte2(ESL_APP_PLAY_DTMF, Param, pChanExten->chanId()); } } /***************************************************************** **【函数名称】 **【函数功能】 桥接通道 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::bridge( CChanTrunk* pChan, CString CallerNum, CString CalleeNum ) { CString CallString; CallerNum = "0000000000"; CalleeNum = "1030"; if(!m_CallStringMaker.makeCallString(CallerNum, CalleeNum, CallString)) return false; CString Param; Param.Format(_T("ringback=${us-ring}")); m_Gateway.Execte2(ESL_APP_SET,Param,pChan->chanId()); return m_Gateway.Execte2(ESL_APP_BRIDGE,CallString,pChan->chanId()); /*CString EslCmd; EslCmd.Format(_T("bgapi uuid_transfer %s %s xml"), pChan->chanId(), CalleeNum, "TurnOut.xml"); return m_Gateway.sendCmd(EslCmd);*/ } bool CFsProxy::origine(CChanExten* pChan,CString CallerNum, CString CalleeNum,LONG JobId) { CString EslCmd; EslCmd.Format(_T("bgapi originate user/%s &echo"),CalleeNum/*,pChan->chanId(), ESL_HEADER_JOB_UUID, JobId*/); //EslCmd.Format(_T("originate{ origination_uuid = %s }user /1017"), pChan->chanId()); return m_Gateway.sendCmd(EslCmd); } bool CFsProxy::origine(CString ExtenID) { CString EslCmd; EslCmd.Format(_T("bgapi originate user/%s &park"), ExtenID); return m_Gateway.sendCmd(EslCmd); }