www.pudn.com > CP_IVR.zip > TapiObj.cpp
/*********************************************************************
* TapiObj.cpp:
* implementation of the
* CTapiObj,
* CLine,
* CPhone classes
* WARNINGS:
* - Just 1 and only 1 instance of these class must be
* created.
*
* Auther: Hamed.M.
* eMail : HamedMosavi @ hotmail.com
* HamedMosavi @ gmail.com
********************************************************************/
#include "stdafx.h"
#include "TapiObj.h"
#include "HSettings.h"
#include "HDevices.h"
#include "HErrLogger.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
/*********************************************************************
* Globals
********************************************************************/
/* To post event messages to central manager */
extern unsigned long g_lMngrThread;
/* To load some initialdata from a prefrences list */
extern CHSettings g_settings;
/* To store a list of telephony devices */
extern CHDevices g_devices;
/*********************************************************************
* Construction: Parent class
********************************************************************/
CTapiObj::CTapiObj()
{
/* Our application name. CP stands for Code Project ;) */
m_appName = _T("CP_IVR");
/* This application is going to use TAPI 2.1 */
/* For more information about versionning look at MSDN */
m_version = 0x00020001;
/* We are going to place a communication of the type voice */
m_mediaMode = LINEMEDIAMODE_AUTOMATEDVOICE;
}
/*********************************************************************
* Destruction: Parent class
********************************************************************/
CTapiObj::~CTapiObj()
{
}
/*********************************************************************
* Construction: Exception class, to be thrown!
********************************************************************/
CTapiObj::TEx::TEx(int id, CString res, CString dt, CString tm)
{
code=id;
result=res;
date=dt;
time=tm;
}
/*********************************************************************
* CLine class
*********************************************************************
* This class impelements a line and defines main operations on it *
* A line will be automatically answered (picked up), and app waits *
* and detects codes that user presses at the other end of the line *
* CallerID of the user will be receiver. *
* It informs the outside world of what's happenning at any time by *
* posting a message to any hread, whoes handle is placed in *
* 'g_lMngrThread' global variable. *
********************************************************************/
/*********************************************************************
*Constructor: CLine class (Up to line # 810)
********************************************************************/
CLine::CLine()
{
/* To receive device capabilities info. */
m_pLineDevCaps = NULL;
/* To keep the ID of the current device, it can be used later to
* to open other devices, in a multiline application. Line class
* Starts searching ID's greater than this ID, So after Finding
* the first line ID, you can give this 'that value +1' to search
* for the next device. */
m_lBossThreadID = 0;
m_ID = 0;
m_cid = _T("");
/* The line has not been initialized yet */
m_bInitialized = FALSE;
/* The line has not been opened yet */
m_bOpened = FALSE;
/* Thread should not stop after starting */
m_bContinueEventThread = TRUE;
/* TAPI variables */
m_hLineApp = NULL;
m_hLine = NULL;
m_hCall = NULL;
m_hLineEvent = NULL;
m_hLineMsgThread = NULL;
m_hWOut = NULL;
memset(&m_extID,NULL,sizeof(LINEEXTENSIONID));
m_dwApiVersion = 0;
m_dwWaveOutID = 0;
m_dwRingCnt = 0;
/* Will be filled by the number of available TAPI devices */
m_nLineCnt = 0;
}
/*********************************************************************
* Destructor: CLine class
********************************************************************/
CLine::~CLine()
{
ShutDown();
}
/*********************************************************************
* Initialize the line, (~ 200 lines)
********************************************************************/
long CLine::Init()
{
PostThreadMessage(g_lMngrThread,WM_STATUS_CHNGD,0,ST_INIT);
long ret = -1;
/* To be able to control loops. It will keep track of the numbers
a loop continued, so after a maximum, we are in trouble=> break */
int patience = 0;
/* Working with memory in TAPI is a hell! */
BOOL noMem;
/* Allocate memory for line parameters */
LINEINITIALIZEEXPARAMS *lineParams =
(LINEINITIALIZEEXPARAMS *) calloc(1,
sizeof(LINEINITIALIZEEXPARAMS));
memset(lineParams,NULL,sizeof(LINEINITIALIZEEXPARAMS));
/* We are going to receive TAPI events using an event thread */
lineParams->dwOptions = LINEINITIALIZEEXOPTION_USEEVENT;
lineParams->dwTotalSize = sizeof(LINEINITIALIZEEXPARAMS);
/* Try to initilize line */
/* We need a loop untill needed memory is alocated */
do {
/* We have not memory shortage yet */
noMem = FALSE;
/* First TAPI call in the sequence */
ret = lineInitializeEx(
&m_hLineApp,
AfxGetInstanceHandle(),
NULL,
m_appName,
&m_nLineCnt,
&m_version,
lineParams );
/* Didn't Itell you about memory hell? Here it starts
If more memory is needed, Realocate */
/* Re alocate memory if more is needed */
if ( lineParams->dwNeededSize > lineParams->dwTotalSize ) {
int needed = lineParams->dwNeededSize;
noMem = TRUE;
free(lineParams);
lineParams = NULL;
lineParams = (LINEINITIALIZEEXPARAMS *) calloc(1,needed);
if (!lineParams) {
ThrowErr(0,
_T("TapiObj.cpp, Ln 186, can't alocate memory"));
}
memset(lineParams,NULL,needed);
lineParams->dwOptions = LINEINITIALIZEEXOPTION_USEEVENT;
lineParams->dwTotalSize = needed;
}
/* If this loop ran 1000 times!!, there must be a problem */
if (++patience> 1000) {
if (lineParams) free(lineParams);
ThrowErr(1,_T(
"TapiObj.cpp, Ln 199, Recovered from unlimited loop"));
};
/* Sleep 5 mili seconds and re run the loop again */
Sleep(5);
/* Repeat untill everythings go right */
} while ( (ret==LINEERR_REINIT) || (noMem==TRUE) );
/* Get a handle for thread waking up */
m_hLineEvent = (lineParams->Handles.hEvent);
/* Free unused memory */
if (lineParams) free(lineParams);
/* Catch errors */
if (ret != 0) {
CString str;
str.Format(_T("%d"),ret);
ThrowErr(ret,_T("TapiObj.cpp, Ln 217, ret=")+str);
}
/* Create an array of devices */
g_devices.Reset();
/* Place to hold the true device id */
int trueDeviceId = 0;
/* Time to negotiate! for a good device */
for (int i=m_ID;(unsigned)idwTotalSize =
sizeof(LINECALLINFO)+1024;
lineGetCallInfo(m_hCall, lpCallInfo);
if (lpCallInfo->dwTotalSize <
lpCallInfo->dwNeededSize)
{
lpCallInfo = (LINECALLINFO *)
realloc(
lpCallInfo,
lpCallInfo->dwNeededSize);
lineGetCallInfo(m_hCall, lpCallInfo);
}
/* Find Caller ID */
m_cid.Format(_T("%s"),(LPCTSTR)
((LPBYTE)lpCallInfo +
lpCallInfo->dwCallerIDOffset));
/* Inform parent that we got CID */
PostThreadMessage(
g_lMngrThread,
WM_CALLERID,
0,
0);
if (lpCallInfo)
free(lpCallInfo);
}
break;
/* Close the line */
case LINE_CLOSE:
ShutDown(); // UNDONE: should we shutdown?
break;
case LINE_LINEDEVSTATE :
/* Ringing, Please answer the call */
if (stLineMsg.dwParam1==LINEDEVSTATE_RINGING) {
/* Hey, dady! It's ringing */
PostThreadMessage(
g_lMngrThread,
WM_STATUS_CHNGD,
0,
ST_RING);
/* Should I pick the phone Up? */
if ( stLineMsg.dwParam3 >= m_dwRingCnt ) {
/* Check errors */
ret = lineAnswer(m_hCall,NULL,0);
if (!(ret>0)) {
ThrowErr(ret,_T("TapiObj.cpp, Ln 597,")
_T("Err: Unable to Answer the line."));
return;
}
}
}
break;
/* Touch tone digit arrived! */
case LINE_MONITORDIGITS :
/* Process digits */
if ( stLineMsg.dwParam2 == LINEDIGITMODE_DTMF ) {
OnDetectDTMF( LOBYTE(stLineMsg.dwParam1) );
}
break;
/* Tone arrived, any computer is trying to connect? */
case LINE_MONITORTONE :
// ?
break;
/* Any detectable error(s)? */
case LINE_REPLY :
// Possibly an error occured
// UNDONE: Handle errors, and stop later clls or so...
break;
default :
st.Format(L" [%x] ",stLineMsg.dwMessageID);
ThrowErr(0,st);
break;
}
}
/*********************************************************************
* Tries to get the ID of the wave out device
********************************************************************/
long CLine::GetWaveID()
{
VARSTRING *pVs;
long ret=0;
DWORD dwWaveDev;
DWORD dwSize;
int patience = 0;
/* allocate memory for structure */
pVs = (VARSTRING *) calloc (1, sizeof(VARSTRING));
/* set structure size */
pVs->dwTotalSize = sizeof(VARSTRING);
do {
/* We have not more patience, stop the loop */
if (++patience > 1000) {
ThrowErr(2153,_T("TapiObj.cpp, Ln 649,")
_T("Err: Recovered from unlimited loop."));
return -1;
}
/* Get wave/out ID */
ret = lineGetID(
m_hLine,
0L,
m_hCall,
LINECALLSELECT_CALL,
pVs,
_T("wave/out"));
/* check errors */
if (ret) {
free (pVs);
return -1;
}
/* reallocate and try again */
if (pVs->dwTotalSize < pVs->dwNeededSize) {
dwSize = pVs->dwNeededSize;
free (pVs);
pVs = (VARSTRING *) calloc(1, dwSize);
pVs->dwTotalSize = dwSize;
continue;
}/* end if (need more space) */
break; /* success */
} while (TRUE);
/* copy wave id */
dwWaveDev = (DWORD) *((DWORD *)((LPSTR)pVs + pVs->dwStringOffset));
free (pVs);
m_dwWaveOutID = dwWaveDev;
return dwWaveDev;
}
/*********************************************************************
* Restart the line
********************************************************************/
void CLine::Restart()
{
/* Stop the thread */
m_bContinueEventThread = FALSE;
Sleep(500); /* Sleep while thread exits */
/* Notify parent of a restart event */
PostThreadMessage(g_lMngrThread,WM_STATUS_CHNGD,0,ST_RESTART);
/* Drop the line */
lineDrop(m_hCall, NULL, 0);
/* TAPI cleanup */
lineDeallocateCall (m_hCall);
m_hCall = NULL;
m_bInitialized = TRUE;
m_bOpened = FALSE;
/* Sleep 100 more mili seconds, to make sure hardware is ready*/
Sleep(200);
/* Re open the line */
Open();
}
/*********************************************************************
* Shutting down the line
********************************************************************/
void CLine::ShutDown()
{
/* Notify parent of a shutdown event */
PostThreadMessage(g_lMngrThread,WM_STATUS_CHNGD,0,ST_SHUTDN);
if (m_bInitialized) {
/* Stop the thread */
m_bContinueEventThread = FALSE;
/* Sleep while thread exits */
if ( WaitForSingleObject(m_hLineMsgThread,10000) ==
WAIT_TIMEOUT) {
TerminateThread(m_hLineMsgThread, 0);
}
/* Drop the line */
lineDrop(m_hCall, NULL, 0);
/* Close the line */
lineClose(m_hLine);
m_hLine = NULL;
/* TAPI cleanup */
lineDeallocateCall (m_hCall);
m_hCall = NULL;
/* Shutdown TAPI */
lineShutdown(m_hLineApp);
m_hLineApp = NULL;
m_bInitialized = FALSE;
}
if (m_pLineDevCaps) {
free (m_pLineDevCaps);
}
m_pLineDevCaps = NULL;
}
/*********************************************************************
* Thread to get tapi events
********************************************************************/
DWORD WINAPI CLine::EventThread(LPVOID pParam)
{
/* Get callers pointer */
CLine* pLine = (CLine*) pParam;
if (!pLine) return 0;
/****************************************************************
* Every 10 mili seconds check to make sure we have to continue,
* if so, check to see weather we have any events or not, if so
* process event otherwise goto first place!
****************************************************************/
while(pLine->m_bContinueEventThread) {
switch(WaitForSingleObject(
(HANDLE)(pLine->m_hLineEvent),
5))
{
/* Have we got any events? */
case WAIT_OBJECT_0:
/* I did not catch errors here */
try {
pLine->ProcessEvent();
} catch(CTapiObj::TEx e) {
CString sz;
sz.Format(_T("Line thread Err: [code:%d],")
_T("[date:%s],")
_T("[time:%s],")
_T("[reason:%s]\n"),e.code,e.date,e.time,e.result);
CHErrLogger::LogError(sz);
} catch(...) {
}
break;
/* Sure we have nothing to process now */
case WAIT_TIMEOUT:
break;
/* Go on waiting and processing */
default: continue;
}
}
return 0;
}
/*********************************************************************
* Will be called to process a key pressed by the user
********************************************************************/
void CLine::OnDetectDTMF(char tone)
{
switch (tone) {
case '1': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_1);
break;
case '2': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_2);
break;
case '3': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_3);
break;
case '4': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_4);
break;
case '5': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_5);
break;
case '6': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_6);
break;
case '7': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_7);
break;
case '8': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_8);
break;
case '9': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_9);
break;
case '0': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_0);
break;
case 'A': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_A);
break;
case 'B': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_B);
break;
case 'C': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_C);
break;
case 'D': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_D);
break;
case '*': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_STAR);
break;
case '#': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_SHARP);
break;
default : PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_NONE);
break;
}
}
/*********************************************************************
* Starts processing a line
********************************************************************/
BOOL CLine::Start()
{
try {
Init();
Open();
} catch(CTapiObj::TEx e) {
CString sz;
sz.Format(_T("Line Err: [code:%d],")
_T("[date:%s],")
_T("[time:%s],")
_T("[reason:%s]\n"),e.code,e.date,e.time,e.result);
CHErrLogger::LogError(sz);
} catch (...) {
}
return TRUE;
}
/*********************************************************************
* Returns caller ID
********************************************************************/
CString CLine::GetCallerID()
{
return m_cid;
}
/*********************************************************************
* Returns wave device ID
********************************************************************/
DWORD CLine::GetDevWavID()
{
return m_dwWaveOutID;
}
/*********************************************************************
* Set a thread to receive DTMF messages
********************************************************************/
void CLine::SetBossMessenger(unsigned long pThreadID)
{
m_lBossThreadID = pThreadID;
}
/*********************************************************************
* Set number of rings to pickup the line after
********************************************************************/
void CLine::SetRingCount(UINT nRingCnt)
{
m_dwRingCnt = nRingCnt;
/* Pickup after ? rings */
lineSetNumRings (m_hLine, 0, m_dwRingCnt);
}
/*********************************************************************
* End of CLine impelementation
********************************************************************/
/*********************************************************************
* CPhone class
*********************************************************************
* This class impelements a phone and defines main operations on it *
* phone devices are to control mic/spk jacks behind your modem or *
* through the sound card. They also make it possible to work with *
* the phone socket (socket simmilar to RJ-45) of the modem. *
* *
* This impelementation covers Speaker/phone capability of modems. *
* *
********************************************************************/
/*********************************************************************
* Constructor CPhone class
********************************************************************/
CPhone::CPhone()
{
m_ID = 0;
m_bInitialized = FALSE;
m_bOpened = FALSE;
m_bContinueEventThread = TRUE;
m_hPhoneApp = NULL;
m_hPhone = NULL;
m_hPhoneEvent = NULL;
m_hPhoneMsgThread = NULL;
m_pPhoneDevCaps = NULL;
m_dwApiVersion = 0;
}
/*********************************************************************
* Distructor CPhone class
********************************************************************/
CPhone::~CPhone()
{
ShutDown();
}
/*********************************************************************
* Initialize the phone (~ 120 lines)
*
* very similar to line initialization above
********************************************************************/
long CPhone::Init()
{
int ret = -1;
// Varibles
int patience = 0;
DWORD nPhoneCnt=0;
BOOL noMem;
PHONEINITIALIZEEXPARAMS *phoneParams =
(PHONEINITIALIZEEXPARAMS *)
calloc(1,sizeof(PHONEINITIALIZEEXPARAMS));
memset(phoneParams,NULL,sizeof(PHONEINITIALIZEEXPARAMS));
phoneParams->dwOptions = PHONEINITIALIZEEXOPTION_USEEVENT;
phoneParams->dwTotalSize = sizeof(PHONEINITIALIZEEXPARAMS);
/* Try to initilize phone */
do {
/* We have not memory shortage */
noMem = FALSE;
ret = phoneInitializeEx( &m_hPhoneApp,
AfxGetInstanceHandle(),
NULL,
m_appName,
&nPhoneCnt,
&m_version,
phoneParams );
/* If more memory is needed, Realocate */
if ( phoneParams->dwNeededSize > phoneParams->dwTotalSize ) {
int needed = phoneParams->dwNeededSize;
noMem = TRUE;
free(phoneParams);
phoneParams = NULL;
phoneParams = (PHONEINITIALIZEEXPARAMS *)
calloc(1,needed);
if (!phoneParams) {
ThrowErr(0,_T("TapiObj.cpp, Ln 987,")
_T("can't alocate memory"));
}
memset(phoneParams,NULL,needed);
phoneParams->dwOptions =
PHONEINITIALIZEEXOPTION_USEEVENT;
phoneParams->dwTotalSize = needed;
}
/* looping too much */
if (++patience> 1000) {
if (phoneParams) free(phoneParams);
ThrowErr(1,_T("TapiObj.cpp, Ln 1002,")
_T("Recovered from unlimited loop"));
};
/* Sleep 5 mili seconds */
Sleep(5);
} while ( (ret==PHONEERR_REINIT) || (noMem==TRUE) );
/* Get a handle for thread waking up */
m_hPhoneEvent = (phoneParams->Handles.hEvent);
/* Free unused memory */
if (phoneParams) free(phoneParams);
/* Check errors */
if (ret != 0) {
CString sr;
sr.Format(_T("%d"),ret);
ThrowErr(ret,_T("TapiObj.cpp, Ln 1022, ret=")+sr);
}
memset(&(m_extID),NULL,sizeof(PHONEEXTENSIONID));
/* Time to negotiate! for a good device */
for (UINT i=m_ID;im_bContinueEventThread) {
switch(WaitForSingleObject(
(HANDLE)(phone->m_hPhoneEvent), 10)) {
/* Have we got any events? */
case WAIT_OBJECT_0:
try {
phone->ProcessEvent();
} catch(...) {
CString sz;
sz.Format(_T("Err: PhoneEventThread ,Line 1233\n"));
CHErrLogger::LogError(sz);
}
break;
/* Sure we have nothing to process now */
case WAIT_TIMEOUT:
break;
/* Go on waiting and processing */
default: continue;
}
}
return 0;
}
/*********************************************************************
* Switches a line to Mic/Spk phone device
********************************************************************/
LONG CPhone::SwichToMicSpk()
{
long ret = 0;
ret = phoneSetHookSwitch(
m_hPhone,
PHONEHOOKSWITCHDEV_SPEAKER,
PHONEHOOKSWITCHMODE_MICSPEAKER
);
if (ret<0) {
}
return ret;
}
/*********************************************************************
* Resets a phone device
********************************************************************/
void CPhone::Reset()
{
phoneClose( m_hPhone );
Start();
}
/*********************************************************************
* Starts a phone device operation
********************************************************************/
BOOL CPhone::Start()
{
try {
Init();
Open();
} catch(CTapiObj::TEx e) {
CString sz;
sz.Format(_T("Phone Err: [code:%d],")
_T("[date:%s],")
_T("[time:%s],")
_T("[reason:%s]\n"),e.code,e.date,e.time,e.result);
CHErrLogger::LogError(sz);
} catch (...) {
}
return TRUE;
}
/*********************************************************************
* Misplaced function!
********************************************************************/
void CTapiObj::ThrowErr(int id, CString res)
{
CTime tm;
tm=tm.GetCurrentTime();
CString stdt,sttm;
stdt.Format(_T("%d/%d/%d"),tm.GetYear(),tm.GetMonth(),tm.GetDay());
sttm.Format(_T("%d:%d:%d"),tm.GetHour(),tm.GetMinute(),tm.GetSecond());
throw(CTapiObj::TEx(id,res,stdt,sttm));
}
/*********************************************************************
* Shutdown & Clenup phone device
********************************************************************/
void CPhone::ShutDown()
{
if (m_bInitialized) {
/* Stop the thread */
m_bContinueEventThread = FALSE;
/* Sleep while thread exits */
if ( WaitForSingleObject(m_hPhoneMsgThread,10000) ==
WAIT_TIMEOUT) {
TerminateThread(m_hPhoneMsgThread, 0);
}
/* Drop the line */
phoneClose(m_hPhone);
/* Close the line */
m_hPhone = NULL;
/* Shutdown TAPI */
phoneShutdown(m_hPhoneApp);
m_hPhoneApp = NULL;
m_bInitialized = FALSE;
}
if (m_pPhoneDevCaps) {
free (m_pPhoneDevCaps);
}
m_pPhoneDevCaps = NULL;
}