void voice_card_control::cti_callout(boost::shared_ptr<cti_call_out_param> cti_call_out_param_) { std::size_t chID; ciaMessage& msg_ = cti_call_out_param_->m_ch_msg->m_procbuffer_msg; try { chID = m_channel_queue.take(); } catch (std::out_of_range) { //BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << msg_.transid() << " 获取通道失败, 通道全部繁忙"; //继续下一次呼叫 cti_callout_again(cti_call_out_param_); return; } int ch_state = SsmGetChState(chID); if (ch_state != 0 && ch_state != 123) //空闲或本地闭塞 { BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << msg_.transid() << " , 获取到得通道状态为:" << ch_state << ", 不可用,将此通道重新放回。通道号码:" << chID; m_channel_queue.put(chID); cti_callout_again(cti_call_out_param_); return; } //BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << msg_.transid() << ", 获取到的通道号码:" << chID; SsmSetTxCallerId(chID, msg_.authcode().c_str()); boost::shared_ptr<trunk> t = m_trunk_vector.at(chID); boost::unique_lock<boost::mutex> unique_lock_(t->m_trunk_mutex); if (SsmAutoDial(chID, msg_.pn().c_str()) == 0){ BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << msg_.transid() << " 已发送请求, 已将此通道对应状态清空, 通道号码:" << chID; t->reset_trunk(cti_call_out_param_); } else { show_error(); unique_lock_.unlock(); m_channel_queue.put(chID); //上一次呼叫失败,继续呼叫 if (cti_call_out_param_->m_repeat_call_out) { cti_call_out_param_->m_repeat_call_out = false; BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << msg_.transid() << "上一次呼叫失败,继续呼叫, 失败原因向上查询SsmGetLastErrMsg"; cti_callout_again(cti_call_out_param_); } //已经连续两次呼叫失败, 直接返回失败 else { BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << msg_.transid() << "已经连续两次呼叫失败, 直接返回失败, 失败原因向上查询SsmGetLastErrMsg"; std::string transid_ = msg_.transid(); msg_.Clear(); msg_.set_type(CIA_CALL_RESPONSE); msg_.set_transid(transid_); msg_.set_status(CIA_CALL_FAIL); cti_call_out_param_->m_base_client->do_write(cti_call_out_param_->m_ch_msg); return; } } }
void voice_card_control::init_cti() { BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "读取语音卡配置文件"; if (SsmStartCti("ShConfig.ini", "ShIndex.ini") != 0) { show_error(); BOOST_LOG_SEV(cia_g_logger, Critical) << "读取语音卡配置文件失败"; throw std::runtime_error("读取语音卡配置文件失败"); } else{ BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "语音卡配置文件读取成功"; } //设置驱动程序抛出事件的模式 EVENT_SET_INFO EventMode; EventMode.dwWorkMode = EVENT_CALLBACK; // 事件回调模式 EventMode.lpHandlerParam = (LPVOID)voice_card_control::cti_callback; // 注册回调函数 SsmSetEvent(0xffff, -1, true, &EventMode); // 启动事件触发模式 //如果要在程序中获取SS7消息, 则需要同时注意以下两点 //配置文件 ShConfig.ini 中, GetMsuOnAutoHandle = 1 //程序中: SsmSetEvent(E_RCV_Ss7Msu, -1, true, &EventMode); SsmSetEvent(E_RCV_Ss7Msu, -1, true, &EventMode); BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "正在初始化语音卡通道, 预期耗时10秒"; while (SsmSearchIdleCallOutCh(160, 0x1E0000) < 0){ // 循环等待, 直到能够获取语音卡空闲通道号, 语音卡初始化完毕 boost::this_thread::sleep_for(boost::chrono::milliseconds(500)); } BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "语音卡通道初始化完毕"; for (std::size_t i = 0; i < m_numChannelCount; i++){ m_channel_queue.put(i); SsmBlockRemoteCh(i); // 设置通道远端堵塞(禁止来话,但仍可以呼出) } }
int voice_card_control::cti_hangUp(std::size_t channelID, std::string status) { int retVal = 0; boost::shared_ptr<trunk> t = m_trunk_vector.at(channelID); boost::unique_lock<boost::mutex> unique_lock_(t->m_trunk_mutex, boost::defer_lock); if (!unique_lock_.try_lock()) { BOOST_LOG_SEV(cia_g_logger, Warning) << "cti_hangUp函数尝试锁定通道失败, 放弃挂机, 当前通道Step值为:" << t->m_step; return -1; } else { switch (t->m_step) { case TRK_IDLE: BOOST_LOG_SEV(cia_g_logger, Warning) << "依据当前通道状态判断可能出现重复挂机, 请在后续版本调查原因, 通道号码:" << channelID; return -1; case TRK_SLEEP: BOOST_LOG_SEV(cia_g_logger, Warning) << "当前通道正在延迟挂机, 通道号码:" << channelID; return -1; default: break; } if (!t->m_call_out_param->m_hungup_by_echo_tone) { BOOST_LOG_SEV(cia_g_logger, CalloutMsg) << "由于设置非响一声挂断, 未挂断本次呼叫请求, 等待超时挂断, 通道号码:" << channelID; return -1; } retVal = SsmHangup(channelID); if (retVal == -1){ show_error(); BOOST_LOG_SEV(cia_g_logger, Warning) << "挂机失败, 请在后续版本调查原因, 通道号码:" << channelID; } else{ BOOST_LOG_SEV(cia_g_logger, CalloutMsg) << "已挂断本次呼叫请求, 通道号码:" << channelID; } ciaMessage& msg_ = t->m_call_out_param->m_ch_msg->m_procbuffer_msg; std::string transid_ = msg_.transid(); msg_.Clear(); msg_.set_type(CIA_CALL_RESPONSE); msg_.set_transid(transid_); msg_.set_status(status); if (t->m_call_out_param == nullptr) { BOOST_LOG_SEV(cia_g_logger, Critical) << "严重异常: 挂机时发现客户端socket为空"; } else { LOG_MSG_QUEUE.put(boost::make_shared<LOG_MSG>(Debug, "呼叫返回数据:\n" + t->m_call_out_param->m_ch_msg->m_procbuffer_msg.DebugString())); t->m_call_out_param->m_base_client->do_write(t->m_call_out_param->m_ch_msg); } t->realseTrunk(); unique_lock_.unlock(); m_channel_queue.put(channelID); } return retVal; }
void voice_card_control::cti_callout(boost::shared_ptr<cti_call_out_param> cti_call_out_param_) { std::size_t chID; try { chID = m_channel_queue.take(); } catch (std::out_of_range) { BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << cti_call_out_param_->m_transId << " 获取通道失败, 通道全部繁忙"; //继续下一次呼叫 cti_callout_again(cti_call_out_param_); return; } BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << cti_call_out_param_->m_transId << " 获取到得通道状态为:" << SsmChkAutoDial(chID) << ", 通道号码:" << chID; SsmSetTxCallerId(chID, cti_call_out_param_->m_authCode.c_str()); if (SsmAutoDial(chID, cti_call_out_param_->m_pn.c_str()) == 0){ BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << cti_call_out_param_->m_transId << " 已发送请求, 已将此通道对应状态清空, 通道号码:" << chID; boost::shared_ptr<trunk> t = m_trunk_vector.at(chID); boost::unique_lock<boost::mutex> unique_lock_(t->m_trunk_mutex, boost::defer_lock); if (unique_lock_.try_lock()) { t->m_client_socket = cti_call_out_param_->m_base_client; t->m_transId = cti_call_out_param_->m_transId; t->m_caller_id = cti_call_out_param_->m_authCode; t->m_called_id = cti_call_out_param_->m_pn; t->m_hungup_by_echo_tone = cti_call_out_param_->m_hungup_by_echo_tone; t->m_step = TRK_CALLOUT_DAIL; t->m_callTime.restart(); } else { BOOST_LOG_SEV(cia_g_logger, Critical) << "业务流水:" << cti_call_out_param_->m_transId << ", 严重异常, 被分配的语音通道处于占用状态, 请程序猿通宵解决问题"; } } else { m_channel_queue.put(chID); //上一次呼叫失败,继续呼叫 if (cti_call_out_param_->m_repeat_call_out) { cti_call_out_param_->m_repeat_call_out = false; BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << cti_call_out_param_->m_transId << "上一次呼叫失败,继续呼叫"; cti_callout_again(cti_call_out_param_); } //已经连续两次呼叫失败, 直接返回失败 else { BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << cti_call_out_param_->m_transId << "已经连续两次呼叫失败, 直接返回失败"; ciaMessage msg; msg.set_type(CIA_CALL_RESPONSE); msg.set_transid(cti_call_out_param_->m_transId); msg.set_status(CIA_CALL_FAIL); cti_call_out_param_->m_base_client->do_write(chat_message(msg)); return; } } }
int voice_card_control::cti_hangUp(std::size_t channelID, std::string status) { int retVal = 0; boost::shared_ptr<trunk> t = m_trunk_vector.at(channelID); boost::unique_lock<boost::mutex> unique_lock_(t->m_trunk_mutex, boost::defer_lock); if (!unique_lock_.try_lock()) { BOOST_LOG_SEV(cia_g_logger, Warning) << "业务流水:" << t->m_transId << "cti_hangUp函数尝试锁定通道失败, 放弃挂机, 当前通道Step值为:" << t->m_step; return -1; } else { if (!t->m_hungup_by_echo_tone) { BOOST_LOG_SEV(cia_g_logger, CalloutMsg) << "业务流水:" << t->m_transId << " 由于设置非响一声挂断, 未挂断本次呼叫请求, 通道号码:" << channelID; return -1; } switch (t->m_step) { case TRK_IDLE: BOOST_LOG_SEV(cia_g_logger, Warning) << "业务流水:" << t->m_transId << " 依据当前通道状态判断可能出现重复挂机, 请在后续版本调查原因, 通道号码:" << channelID; return -1; case TRK_SLEEP: BOOST_LOG_SEV(cia_g_logger, Warning) << "业务流水:" << t->m_transId << " 当前通道正在延迟挂机, 通道号码:" << channelID; return -1; default: break; } retVal = SsmHangup(channelID); if (retVal == -1){ show_error(); BOOST_LOG_SEV(cia_g_logger, Warning) << "业务流水:" << t->m_transId << " 挂机失败, 请在后续版本调查原因, 通道号码:" << channelID; } else{ BOOST_LOG_SEV(cia_g_logger, CalloutMsg) << "业务流水:" << t->m_transId << " 已挂断本次呼叫请求, 通道号码:" << channelID; } ciaMessage msg; msg.set_type(CIA_CALL_RESPONSE); msg.set_transid(t->m_transId); msg.set_status(status); t->m_client_socket->do_write(chat_message(msg)); t->realseTrunk(); m_channel_queue.put(channelID); } return retVal; }
int voice_card_control::deal_e_proc_auto_dial(int nReference, DWORD dwParam, DWORD dwUser) { int channelID = nReference; if (m_trunk_vector.at(channelID)->m_call_out_param == nullptr) { BOOST_LOG_SEV(cia_g_logger, Critical) << "严重:触发deal_e_proc_auto_dial的通道没有关联呼叫请求对象, 通道号码" << channelID; return 1; } std::string trans_id_ = m_trunk_vector.at(nReference)->m_call_out_param->m_ch_msg->m_procbuffer_msg.transid(); switch (dwParam) { case DIAL_STANDBY: // 通道空闲,没有执行AutoDial任务 break; case DIAL_DIALING: { BOOST_LOG_SEV(cia_g_logger, Debug) << "业务流水:" << trans_id_ << " DIAL_DIALING 事件: 正在发送被叫号码. 外呼通道号:" << channelID; boost::shared_ptr<trunk> t = m_trunk_vector.at(nReference); boost::unique_lock<boost::mutex> unique_lock_(t->m_trunk_mutex); t->m_step = TRK_CALLOUT_DAIL; t->m_callTime.start(); break; } case DIAL_ECHOTONE: // TUP/ISUP通道:表明驱动程序收到对端交换机的地址齐消息(ACM) BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << trans_id_ << " 在判断ACM后, 接收到 DIAL_ECHOTONE 事件, 挂机. 通道号码:" << channelID; BOOST_LOG_SEV(cia_g_logger, CalloutMsg) << "业务流水:" << trans_id_ << " 本次从呼叫到检测振铃, 共用时: " << m_trunk_vector.at(nReference)->elpased(); { boost::shared_ptr<trunk> t = m_trunk_vector.at(nReference); if (t->m_step != TRK_CALLOUT_DAIL && t->m_step != TRK_WAIT_CONNECT) { BOOST_LOG_SEV(cia_g_logger, Critical) << "严重:触发振铃时通道不是TRK_CALLOUT_DAIL 或 TRK_WAIT_CONNECT状态, 通道的状态:" << t->m_step << " 流水号 : " << trans_id_ << "通道号码:" << channelID; return 1; } if (m_use_strategy) { if (t->elpased() <= m_cti_warning_elapse) { BOOST_LOG_SEV(cia_g_logger, CalloutMsg) << "业务流水:" << trans_id_ << " 本次呼叫触发延迟策略"; if (t->m_trunk_mutex.try_lock()) { t->m_step = TRK_SLEEP; t->m_trunk_mutex.unlock(); m_sleep_channel_queue.put(channelID);//放入需要做延迟挂机处理的队列中 } else { BOOST_LOG_SEV(cia_g_logger, Critical) << "业务流水:" << trans_id_ << "将通道号码放入延迟队列前,尝试改变通道状态, 但试图锁定通道失败"; } return 1; } } cti_hangUp(channelID, CIA_CALL_SUCCESS); } break; case DIAL_NO_DIALTONE: BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << trans_id_ << " 没有在线路上检测到拨号音,AutoDial 任务失败. 通道号码:" << channelID; cti_hangUp(channelID, CIA_CALL_FAIL); break; case DIAL_BUSYTONE: BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << trans_id_ << " DIAL_BUSYTONE 事件,被叫用户忙. 依然返回成功, 因为用户已经可以看到验证码, 通道号码:" << channelID; cti_hangUp(channelID, CIA_CALL_SUCCESS); break; case DIAL_ECHO_NOVOICE: // 拨号完成后,线路上先是出现了回铃音,然后保持静默。AutoDial任务终止。只适用于模拟中继线通道 case DIAL_NOVOICE: // 拨号完成后,线路上没有检测到回铃音,一直保持静默。AutoDial任务终止。只适用于模拟中继线通道 BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << trans_id_ << " 拨号完成, 线路保持沉默, 通道号码:" << channelID; break; case DIAL_VOICE: // 被叫用户摘机,AutoDial任务完成 case DIAL_VOICEF1: // 被叫用户摘机(检测到F1频率的应答信号),AutoDial任务完成。只适用于模拟中继线通道 case DIAL_VOICEF2: // 被叫用户摘机(检测到F2频率的应答信号),AutoDial任务完成。只适用于模拟中继线通道 BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << trans_id_ << " E_PROC_AutoDial事件,板卡接通挂机, 通道号码:" << channelID; cti_hangUp(channelID, CIA_CALL_SUCCESS); break; case DIAL_NOANSWER: // 被叫用户在指定时间内没有摘机,AutoDial失败。不适用于IP卡SIP通道 BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << trans_id_ << " DIAL_NOANSWER 事件,被叫用户在指定时间内没有摘机,AutoDial失败, 通道号码:" << channelID; cti_hangUp(channelID, CIA_CALL_FAIL); break; case DIAL_FAILURE: // AutoDial任务失败。失败原因可以通过函数SsmGetAutoDialFailureReason获得 { int fail_reason = SsmGetAutoDialFailureReason(channelID); if (fail_reason == ATDL_NULL) { return 1; } BOOST_LOG_SEV(cia_g_logger, Critical) << "业务流水:" << trans_id_ << " SsmGetAutoDialFailureReason 函数返回错误代码: " << SsmGetAutoDialFailureReason(channelID) << "DIAL_FAILURE 事件, AutoDial失败, 通道号码:" << channelID; cti_hangUp(channelID, CIA_CALL_FAIL); } break; case DIAL_INVALID_PHONUM: BOOST_LOG_SEV(cia_g_logger, RuntimeInfo) << "业务流水:" << trans_id_ << " 被叫号码无效, 通道号码:" << channelID; cti_hangUp(channelID, CIA_CALL_FAIL); break; case DIAL_SESSION_PROCEEDING: // IP卡SIP通道收到18X消息(180除外) case DIAL_ISDN_PROGRESS: // ISDN通道收到对端交换机的PROGRESS消息。详细的消息内容可通过函数SsmISDNGetProgressMsg获得 BOOST_LOG_SEV(cia_g_logger, Critical) << "业务流水:" << trans_id_ << " 触发其他类AutoDial事件, 请注意观察, 通道号码:" << channelID; break; } return 1; }