#include "StdAfx.h" #include "TapiControl.h" #include "DevControl.h" #include "TapiLine.h" #include "LineFactory.h" #include "TapiLineIvr.h" #include "TapiLineExten.h" #include "TapiLineFax.h" #include "TrunkBase.h" #include "TrunkContainer.h" SINGLETON_IMPLEMENT(CTapiControl) CTapiControl::CTapiControl(void) : m_hLineApp(NULL) { } CTapiControl::~CTapiControl(void) { } /***************************************************************** **【函数名称】 __tapiLineCallback **【函数功能】 TAPI应用主控回调函数 **【参数】 **【返回值】 ****************************************************************/ void CALLBACK CTapiControl::__tapiLineCallback( DWORD dwDevice, DWORD nMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2, DWORD dwParam3 ) { switch(nMsg) { case LINE_CREATE: break; case LINE_REQUEST: break; default: { // 得到线路指针,将事件委托给线路实体类 CTapiLine* pLine = CTapiControl::GetInstance().getLine(dwInstance); if(NULL != pLine) { pLine->onTapiEvent(dwDevice, nMsg, dwParam1, dwParam2, dwParam3); } } break; } // end switch } /***************************************************************** **【函数名称】 __openValidLines **【函数功能】 打开所有可控线路 **【参数】 DevCounts 设备资源总数 **【返回值】 设备可控线路数 ****************************************************************/ UINT CTapiControl::__openValidLines( int DevCounts ) { UINT ExtCount = 0; // 可控分机总数 for(int i = 0; i < DevCounts; ++i) { // 版本号协商(每个设备资源都有一个可用版本号) LINEEXTENSIONID lineExtID; DWORD ApiVersion = 0; // 设备资源版本号 HRESULT hRet = ::lineNegotiateAPIVersion(m_hLineApp, (DWORD)i, TAPI_LO_VERSION, TAPI_HI_VERSION, &ApiVersion, &lineExtID); DWORD ExtVersion = 0; hRet = lineNegotiateExtVersion(m_hLineApp, (DWORD)i, ApiVersion, TAPI_LO_VERSION, TAPI_LO_VERSION, &ExtVersion); // 得到当前资源Caps LINEDEVCAPS* pDevCaps = NULL; hRet = __loopLineGetDevCaps(i, ApiVersion, ExtVersion, pDevCaps); // 如果设备可用 if( (S_OK == hRet) && (pDevCaps->dwBearerModes & LINEBEARERMODE_VOICE) && (pDevCaps->dwMediaModes & LINEMEDIAMODE_INTERACTIVEVOICE) && (pDevCaps->dwLineFeatures & LINEFEATURE_MAKECALL) ) { // 得到设备提供者 CString strProvider = __getTapiString(pDevCaps, pDevCaps->dwProviderInfoSize, pDevCaps->dwProviderInfoOffset); // 只处理AVAYA设备 if(strProvider.CompareNoCase(CONST_TAPI_DEST_DEVICE) == 0) { // 打开设备并得到设备分机号 CString ExtID; HLINE LineHandle = __openDev(ExtID, i, ApiVersion, ExtVersion, pDevCaps); if(LineHandle != CONST_DEV_ID_INVALID) // 打开设备成功 { CTapiLine* pLine = CLineFactory::makeTapiLine(i, LineHandle, ApiVersion, m_hLineApp, ExtID); if(pLine != NULL) { ExtCount++; m_MapLineByDevID.SetAt(i, pLine); // 添加到哈希表 m_MapLineByExtID.SetAt(ExtID, pLine); pLine->regist(); // 线路注册 } } // end if(打开设备) } // end if(Is Avaya) } // end if delete[] pDevCaps; // 释放内存 } // end for return ExtCount; } /***************************************************************** **【函数名称】 __openDev **【函数功能】 打开TAPI设备并返回设备句柄 **【参数】 DevID 设备ID Version 设备API版本号 **【返回值】 HLINE 设备句柄 ExtID 设备分机号 ****************************************************************/ HLINE CTapiControl::__openDev( CString& ExtID, DWORD DevID, DWORD ApiVersion, DWORD ExtVersion, LINEDEVCAPS* pDevCaps ) { HLINE LineHandle = CONST_DEV_ID_INVALID; // 对设备执行打开操作,得到线路句柄 HRESULT hRet = ::lineOpen(m_hLineApp, DevID, &LineHandle, ApiVersion, 0, DevID, LINECALLPRIVILEGE_OWNER, LINEMEDIAMODE_INTERACTIVEVOICE, 0); if(hRet != S_OK) return CONST_DEV_ID_INVALID; // 得到线路分机号 for(DWORD i=0; idwNumAddresses; i++) { // 得到线路的Caps LINEADDRESSCAPS *pAddressCaps = NULL; __loopLineGetAddressCaps(DevID, i, ApiVersion, pAddressCaps); // 得到线路分机号 ExtID = __getTapiString(pAddressCaps, pAddressCaps->dwAddressSize, pAddressCaps->dwAddressOffset); // 释放内存 delete[] pAddressCaps; } // end if return LineHandle; } /***************************************************************** **【函数名称】 __loopLineGetDevCaps **【函数功能】 得到设备功能 **【参数】 DevID 设备ID ApiVersion 设备API版本号 **【返回值】 操作结果 pDevCaps 设备功能指针 ****************************************************************/ HRESULT CTapiControl::__loopLineGetDevCaps( DWORD DevID, DWORD ApiVersion, DWORD ExtVersion, LINEDEVCAPS* &pDevCaps ) { HRESULT hRet = S_OK; size_t nCurrentSize = 512; // Starting value - usually big enough for(;;) { // Allocate some memory for the call pDevCaps = (LINEDEVCAPS *) new BYTE[nCurrentSize]; ZeroMemory(&pDevCaps[0], nCurrentSize); pDevCaps->dwTotalSize = nCurrentSize; // Ask TAPI for some information hRet = ::lineGetDevCaps(m_hLineApp, DevID, ApiVersion, ExtVersion, pDevCaps); // Cope with variable length structures if(hRet == LINEERR_STRUCTURETOOSMALL) { if(pDevCaps->dwNeededSize <= 0) break; nCurrentSize = pDevCaps->dwNeededSize; delete [] pDevCaps; pDevCaps = NULL; } else { break; // 获取成功,退出循环 } } // end for return hRet; } /***************************************************************** **【函数名称】 __loopLineGetAddressCaps **【函数功能】 得到线路控制能力 **【参数】 DevID 设备ID AddressID 设备Address ID ApiVersion 设备API版本号 **【返回值】 操作结果 pAddressCaps 设备Address能力 ****************************************************************/ HRESULT CTapiControl::__loopLineGetAddressCaps( DWORD DevID, DWORD AddressID, DWORD ApiVersion, LINEADDRESSCAPS*& pAddressCaps ) { HRESULT hRet = S_OK; size_t nCurrentSize = 512; // Starting value - usually big enough for (;;) { // Allocate some memory for the call pAddressCaps = (LINEADDRESSCAPS *) new BYTE[nCurrentSize]; ZeroMemory(&pAddressCaps[0], nCurrentSize); pAddressCaps->dwTotalSize = nCurrentSize; // Ask TAPI for some information hRet = ::lineGetAddressCaps(m_hLineApp, DevID, AddressID, ApiVersion, 0, pAddressCaps); // Cope with variable length structures if (hRet == LINEERR_STRUCTURETOOSMALL) { if (pAddressCaps->dwNeededSize <= 0) break; nCurrentSize = pAddressCaps->dwNeededSize; delete [] pAddressCaps; pAddressCaps = NULL; } else { break; // 获取成功,退出循环 } } // end form return hRet; } /***************************************************************** **【函数名称】 __getTapiString **【函数功能】 从TAPI数据结构中得到相关信息 **【参数】 pInfo TAPI数据指针 Size 数据长度 Offset 偏移量 **【返回值】 已获取的信息内容 ****************************************************************/ CString CTapiControl::__getTapiString( void *pInfo, DWORD Size, DWORD Offset ) { CString Result; if(Size > 0) { char *buffer = Result.GetBufferSetLength(Size + 1); memcpy(buffer, &((BYTE *) pInfo)[Offset], Size); buffer[Size] = 0; Result.ReleaseBuffer(); } return Result; } /***************************************************************** **【函数名称】 init **【函数功能】 初始化TAPI控制 **【参数】 **【返回值】 TRUE: 初始化成功 FALSE: 初始化失败 ****************************************************************/ bool CTapiControl::init( void ) { // 连接IPO DWORD dwDevCount = 0; // 保存可用设备资源总数 DWORD dwAPIVersion = TAPI_HI_VERSION; LINEINITIALIZEEXPARAMS params = { sizeof(LINEINITIALIZEEXPARAMS) }; params.dwOptions = LINEINITIALIZEEXOPTION_USEHIDDENWINDOW; HINSTANCE hInst = ::AfxGetInstanceHandle(); LPCTSTR pszAppName = ::AfxGetAppName(); HRESULT hRet = ::lineInitializeEx(&m_hLineApp, hInst, __tapiLineCallback, pszAppName, &dwDevCount, &dwAPIVersion, ¶ms); if(hRet != S_OK) // 连接设备失败 { CDevControl::GetInstance().onEventLog(LOG_LEVEL_WARNING, _T("{TapiCtrl}: IPO设备连接失败")); return false; } // end if // 打开所有可用线路(内线) int ExtCount = __openValidLines((int)dwDevCount); // 设备初始化完成 CDevControl::GetInstance().onEventLog(LOG_LEVEL_NORMAL, _T("{TapiCtrl}: IPO设备连接成功, 可用分机总数 = %d, TAPI版本号 = %08X"), ExtCount, dwAPIVersion); return true; } /***************************************************************** **【函数名称】 release **【函数功能】 释放TAPI连接 **【参数】 **【返回值】 ****************************************************************/ void CTapiControl::release( void ) { m_MapLineByExtID.RemoveAll(); DWORD DevId = 0; CTapiLine* pLine = NULL; POSITION Pos = m_MapLineByDevID.GetStartPosition(); while(Pos != NULL) { m_MapLineByDevID.GetNextAssoc(Pos, DevId, pLine); ASSERT(pLine != NULL); delete pLine; } m_MapLineByDevID.RemoveAll(); } /***************************************************************** **【函数名称】 getLine **【函数功能】 根据设备编号查找线路 **【参数】 DevID 设备ID **【返回值】 指定的CTapiLine指针 ****************************************************************/ CTapiLine* CTapiControl::getLine( DWORD DevID ) { CTapiLine* pLine = NULL; m_MapLineByDevID.Lookup(DevID, pLine); return pLine; } /***************************************************************** **【函数名称】 getLine **【函数功能】 根据分机号查找线路 **【参数】 ExtID 线路分机号 **【返回值】 指定的CTapiLine指针 ****************************************************************/ CTapiLine* CTapiControl::getLine( LPCTSTR ExtID ) { ASSERT(ExtID != NULL); CTapiLine* pLine = NULL; m_MapLineByExtID.Lookup(ExtID, pLine); return pLine; } /***************************************************************** **【函数名称】 getLineByCallId **【函数功能】 根据DevLink CallId 查找线路 **【参数】 DevLinkCallId **【返回值】 指定的CTapiLine指针 ****************************************************************/ CTapiLine* CTapiControl::getLineByCallId( int DevLinkCallId ) { DWORD DevId = 0; CTapiLine* pLine = NULL; POSITION pos = m_MapLineByDevID.GetStartPosition(); while(pos != NULL) { m_MapLineByDevID.GetNextAssoc(pos, DevId, pLine); if(pLine->isAssoDevLinkCallId(DevLinkCallId)) return pLine; } // end while return NULL; } /***************************************************************** **【函数名称】 getIvrLineByAssoTrunk **【函数功能】 根据中继ID查找其当前关联的IVR分机 **【参数】 TrunkId **【返回值】 指定的CTapiLine指针 ****************************************************************/ CTapiLine* CTapiControl::getIvrLineByAssoTrunk( UINT TrunkId ) { DWORD DevId = 0; CTapiLine* pLine = NULL; POSITION pos = m_MapLineByDevID.GetStartPosition(); while(pos != NULL) { m_MapLineByDevID.GetNextAssoc(pos, DevId, pLine); CTapiLineIvr* pIvrLine = dynamic_cast(pLine); if(pIvrLine != NULL && pIvrLine->getAssoTrunkId() == TrunkId) return pIvrLine; } // end while return NULL; } /***************************************************************** **【函数名称】 getFaxLineByAssoTrunk **【函数功能】 根据中继ID查找其当前关联的传真分机 **【参数】 TrunkId **【返回值】 指定的CTapiLine指针 ****************************************************************/ CTapiLine* CTapiControl::getFaxLineByAssoTrunk( UINT TrunkId ) { bool HasFaxLine = false; DWORD DevId = 0; CTapiLine* pLine = NULL; POSITION pos = m_MapLineByDevID.GetStartPosition(); while(pos != NULL) { m_MapLineByDevID.GetNextAssoc(pos, DevId, pLine); CTapiLineFax* pFaxLine = dynamic_cast(pLine); if(pFaxLine == NULL) continue; HasFaxLine = true; if(pFaxLine->getAssoTrunkId() == TrunkId) return pFaxLine; } // end while if(!HasFaxLine) return getIvrLineByAssoTrunk(TrunkId); else return NULL; } /***************************************************************** **【函数名称】 getFreeIvrLine **【函数功能】 查找一条空闲的IVR线路 **【参数】 **【返回值】 空闲的IVR线路指针 ****************************************************************/ CTapiLine* CTapiControl::getFreeIvrLine( void ) { // 当前轮循到的索引 static POSITION PosStaticIvr = m_MapLineByDevID.GetStartPosition(); // 保证遍历一轮 for(int i = 0; i < m_MapLineByDevID.GetCount(); ++i) { DWORD DevId = 0; CTapiLine* pLine = NULL; m_MapLineByDevID.GetNextAssoc(PosStaticIvr, DevId, pLine); // 保证POS是有效的 if(PosStaticIvr == NULL) PosStaticIvr = m_MapLineByDevID.GetStartPosition(); // 当前线路是IVR线路且空闲 if(pLine->isFree() && dynamic_cast(pLine) != NULL) return pLine; } // end if return NULL; } /***************************************************************** **【函数名称】 getFreeFaxLine **【函数功能】 查找一条空闲的Fax线路 **【参数】 **【返回值】 空闲的Fax线路指针 ****************************************************************/ CTapiLine* CTapiControl::getFreeFaxLine( void ) { CTapiLine* pLine = NULL; pLine = getFreeFaxLineOnly(); if(pLine != NULL) return pLine; // 如果没有找到传真线路,直接查找IVR线路 pLine = getFreeIvrLine(); return pLine; } /***************************************************************** **【函数名称】 getFreeFaxLineOnly **【函数功能】 查找一条空闲的Fax线路,不查找IVR线路 **【参数】 **【返回值】 空闲的Fax线路指针 ****************************************************************/ CTapiLine* CTapiControl::getFreeFaxLineOnly( void ) { CTapiLine* pLine = NULL; // 当前轮循到的索引 static POSITION PosStaticFax = m_MapLineByDevID.GetStartPosition(); // 保证遍历一轮 for(int i=0; iisFree() && dynamic_cast(pLine) != NULL) return pLine; } // end if return NULL; } /***************************************************************** **【函数名称】 getErrorIvrLine **【函数功能】 获取正在执行任务,并且未关联外线成功的线路 **【参数】 **【返回值】 线路地址 ****************************************************************/ CTapiLine* CTapiControl::getErrorIvrLine( void ) { POSITION pos = m_MapLineByDevID.GetStartPosition(); while(pos != NULL) { DWORD DevId = 0; CTapiLine* pLine = NULL; m_MapLineByDevID.GetNextAssoc(pos, DevId, pLine); CTapiLineIvr* pIvrLine = dynamic_cast(pLine); CTapiLineFax* pFaxLine = dynamic_cast(pLine); if(pIvrLine == NULL && pFaxLine == NULL) // 排除坐席分机线路 continue; int Status = pLine->status(); UINT AssoTrunkId = pIvrLine == NULL ? pFaxLine->getAssoTrunkId() : pIvrLine->getAssoTrunkId(); if(AssoTrunkId == 0 && Status !=INNER_STATE_DISABLED) return pLine; } // end while return NULL; } /***************************************************************** **【函数名称】 getTrunkAssoIvrId **【函数功能】 得到当前外线关联的IVR线路ID **【参数】 TrunkId 外线ID **【返回值】 ****************************************************************/ UINT CTapiControl::getTrunkAssoIvrId( UINT TrunkId ) { UINT IvrLineId = 0; CTapiLine* pLine = getIvrLineByAssoTrunk(TrunkId); if(pLine != NULL) sscanf_s(pLine->extenID(), _T("%lu"), &IvrLineId); return IvrLineId; } /***************************************************************** **【函数名称】 getTrunkAssoFaxId **【函数功能】 得到当前外线关联的IVR线路ID **【参数】 TrunkId 外线ID **【返回值】 ****************************************************************/ UINT CTapiControl::getTrunkAssoFaxId( UINT TrunkId ) { UINT IvrLineId = 0; CTapiLine* pLine = getFaxLineByAssoTrunk(TrunkId); if(pLine != NULL) sscanf_s(pLine->extenID(), _T("%lu"), &IvrLineId); return IvrLineId; } /***************************************************************** **【函数名称】 onDevLinkEventS **【函数功能】 DEV Link S 事件响应 **【参数】 pInfoS S事件消息内容 **【返回值】 ****************************************************************/ void CTapiControl::onDevLinkEventS( DevLinkInfoS* pInfoS ) { // 根据触发事件的设备类型进行分类处理 if(pInfoS->nResType == DEV_RES_TYPE_EXT) // 分机 { CTapiLine* pLine = getLine(pInfoS->szResId); ASSERT(pLine != NULL); if(pLine != NULL) { if (pInfoS->nFlag == CONST_DEVLINK_HANGUP_BY_B) // 被动端 { CTapiLine* pPeerLine = getLine(pInfoS->szPeerResId); if (pPeerLine != NULL) { // 如果对端线路是监听线路(主动挂机方),不响应事件 if (pPeerLine->listenerFlag()) { if(pPeerLine->status() == INNER_STATE_FREE && pLine->status() == INNER_STATE_TALKING) { pPeerLine->listenerFlag() = false; // 监听线路已挂机,取消线路监听标志 } return; } } pLine->onDevLinkEventS(pInfoS); } else { pLine->onDevLinkEventS(pInfoS); } } } else if(pInfoS->nResType == DEV_RES_TYPE_TRUNK) // 外线 { UINT TrunkId = 0; sscanf_s(pInfoS->szResId, "%lu", &TrunkId); CTrunkBase* pTrunk = CTrunkContainer::getTrunk(TrunkId); ASSERT(pTrunk != NULL); if(pTrunk!= NULL) pTrunk->onDevLinkEventS(pInfoS); } // end if } /***************************************************************** **【函数名称】 onDevLinkEventA **【函数功能】 DEV Link A 事件响应 **【参数】 DevLinkCallId DevLink CallId **【返回值】 ****************************************************************/ void CTapiControl::onDevLinkEventA( int DevLinkCallId ) { // 通知关随的分机 POSITION pos = m_MapLineByDevID.GetStartPosition(); while(pos != NULL) { CTapiLine* pLine = NULL; DWORD DevId = 0; m_MapLineByDevID.GetNextAssoc(pos, DevId, pLine); if(pLine->isAssoDevLinkCallId(DevLinkCallId)) pLine->onDevLinkEventA(DevLinkCallId); } // end while // 通知关联的外线 CTrunkBase* pTrunk = CTrunkContainer::getTrunkByCallId(DevLinkCallId); if(pTrunk != NULL) pTrunk->onDevLinkEventA(DevLinkCallId); } /***************************************************************** **【函数名称】 onDevLinkEventD **【函数功能】 DEV Link D 事件响应 **【参数】 DevLinkCallId DevLink CallId **【返回值】 ****************************************************************/ void CTapiControl::onDevLinkEventD( int DevLinkCallId ) { // 通知关联的分机 POSITION pos = m_MapLineByDevID.GetStartPosition(); while(pos != NULL) { CTapiLine* pLine = NULL; DWORD nDevId = 0; m_MapLineByDevID.GetNextAssoc(pos, nDevId, pLine); if(pLine->isAssoDevLinkCallId(DevLinkCallId)) pLine->onDevLinkEventD(DevLinkCallId); } // end while // 通知关联的外线 CTrunkBase* pTrunk = CTrunkContainer::getTrunkByCallId(DevLinkCallId); if(pTrunk != NULL) pTrunk->onDevLinkEventD(DevLinkCallId); }