linux版本中间件

EslGateway.cpp 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. #include "EslGateway.h"
  2. #include <thread>
  3. #include <regex>
  4. #include "pubdef.h"
  5. #include "Config.h"
  6. #include "Log.h"
  7. #include "Util.h"
  8. #define ESL_CMD_SCAN_CHANNELS "api show channels\n\n"
  9. #define ESL_CMD_SCAN_INTERNAL "api sofia status profile internal reg\n\n"
  10. #define ESL_HEADER_CALLID "Channel-Call-UUID"
  11. #define ESL_HEADER_CCID "variable_cc_member_session_uuid" // freeswitchs自带的callcenter模块转接坐席后,callid产生变化,可通过该通道变量进行关联
  12. #define ESL_HEADER_CHANID "Unique-ID"
  13. #define ESL_HEADER_CALLER "Caller-Caller-ID-Number"
  14. #define ESL_HEADER_CALLEE "Caller-Callee-ID-Number"
  15. #define ESL_HEADER_DEST_NUM "Caller-Destination-Number"
  16. #define ESL_HEADER_DIRECTION "Call-Direction"
  17. #define ESL_HEADER_SUBCLASS "Event-Subclass"
  18. #define ESL_HEADER_EXTEN_NO "username"
  19. // ESL事件头域常量值
  20. #define ESL_HDR_DIRECTION_INBOUND "inbound"
  21. #define ESL_HDR_DIRECTION_OUTBOUND "outbound"
  22. #define ESL_HDR_SUBCLASS_SIP_REG "sofia::register"
  23. #define ESL_HDR_SUBCLASS_SIP_UNREG "sofia::unregister"
  24. EslGateway::EslGateway() :m_Stop(true), m_pEventThread(nullptr), m_trunkTotal(0), m_usedCount(0), m_bIsConn(false)
  25. {
  26. }
  27. EslGateway::EslGateway(int nTrunkCount) : m_Stop(true), m_pEventThread(nullptr), m_trunkTotal(nTrunkCount), m_usedCount(0), m_bIsConn(false)
  28. {
  29. }
  30. EslGateway::~EslGateway()
  31. {
  32. }
  33. bool EslGateway::init(const std::string & strIP, const std::int16_t & nPort, const std::string & strPwd)
  34. {
  35. this->m_strIP = strIP;
  36. this->m_nPort = nPort;
  37. this->m_strPwd = strPwd;
  38. return start();
  39. }
  40. bool EslGateway::start()
  41. {
  42. memset(&m_EslHdl4Listen, '\0', sizeof(esl_handle_t));
  43. memset(&m_EslHdl4Send, '\0', sizeof(esl_handle_t));
  44. LOG_INFO("{EslGateway}: 处理客户端连接, ip = %s,port=%d,pwd=%s", this->m_strIP.c_str(), this->m_nPort, this->m_strPwd.c_str());
  45. esl_connect(&m_EslHdl4Listen, this->m_strIP.c_str(), this->m_nPort, NULL, this->m_strPwd.c_str());
  46. if (!m_EslHdl4Listen.connected)
  47. return false;
  48. LOG_INFO("{EslGateway}: 处理客户端连接m_EslHdl4Listen OK");
  49. esl_events(&m_EslHdl4Listen, ESL_EVENT_TYPE_PLAIN,
  50. ("CHANNEL_CREATE CHANNEL_ANSWER CHANNEL_HANGUP_COMPLETE"));
  51. esl_connect(&m_EslHdl4Send, this->m_strIP.c_str(), this->m_nPort, NULL, this->m_strPwd.c_str());
  52. if (!m_EslHdl4Send.connected) {
  53. esl_disconnect(&m_EslHdl4Listen);
  54. return false;
  55. }
  56. scanUsedCount();
  57. scanExten();
  58. m_Stop = false;
  59. if (m_pEventThread == nullptr) {
  60. m_pEventThread = std::make_unique<boost::thread>(std::bind(&this_type::__eventThread, this));
  61. }
  62. m_bIsConn.store(true);
  63. return true;
  64. }
  65. void EslGateway::stop()
  66. {
  67. m_Stop.store(true);
  68. if (m_pEventThread) {
  69. m_pEventThread->interrupt();
  70. m_pEventThread->join();
  71. m_pEventThread.reset();
  72. }
  73. memset(&m_EslHdl4Listen, '\0', sizeof(esl_handle_t));
  74. memset(&m_EslHdl4Send, '\0', sizeof(esl_handle_t));
  75. }
  76. void EslGateway::scanUsedCount()
  77. {
  78. if (esl_send_recv(&m_EslHdl4Send, ESL_CMD_SCAN_CHANNELS) != ESL_SUCCESS)
  79. return;
  80. if (m_EslHdl4Send.last_sr_event == nullptr || m_EslHdl4Send.last_sr_event->body == nullptr)
  81. return;
  82. std::string body = m_EslHdl4Send.last_sr_event->body;
  83. body = std::regex_replace(body, std::regex(R"(\D)"), "$1");
  84. int n = atoi(body.c_str());
  85. m_usedCount = n;
  86. LOG_INFO("当前线路总数量:%d", m_trunkTotal.load());
  87. LOG_INFO("当前已用数量:%d", m_usedCount.load());
  88. LOG_INFO("当前可用数量:%d",m_trunkTotal.load()-m_usedCount.load());
  89. }
  90. void EslGateway::scanExten()
  91. {
  92. if (esl_send_recv(&m_EslHdl4Send, ESL_CMD_SCAN_INTERNAL) != ESL_SUCCESS)
  93. return;
  94. if (m_EslHdl4Send.last_sr_event == nullptr || m_EslHdl4Send.last_sr_event->body == nullptr)
  95. return;
  96. std::string body = m_EslHdl4Send.last_sr_event->body;
  97. std::list<std::string> rows;
  98. boost::split(rows, body, boost::is_any_of("\n"));
  99. while (!rows.empty()) {
  100. std::string var = rows.front();
  101. rows.pop_front();
  102. std::regex space("\t| ");
  103. var = std::regex_replace(var, space, "");
  104. std::regex reg("IP:(.*)");
  105. if (std::regex_match(var, reg)) {
  106. std::string extenNo, extenIP;
  107. extenIP = std::regex_replace(var, reg, "$1");
  108. while (!rows.empty()) {
  109. var = rows.front();
  110. rows.pop_front();
  111. std::regex space("\t| ");
  112. var = std::regex_replace(var, space, "");
  113. reg = std::regex("Auth-User:(.*)");
  114. if (std::regex_match(var, reg)) {
  115. extenNo = std::regex_replace(var, reg, "$1");
  116. if (!extenNo.empty() && !extenIP.empty()) {
  117. LOG_DEBUG("分机注册:%s %s", extenNo.c_str(), extenIP.c_str());
  118. __addExtenChan(extenNo);
  119. }
  120. break;
  121. }
  122. }
  123. }
  124. }
  125. }
  126. bool EslGateway::execPlay(const std::string & strChanId, const std::string & strFilePath)
  127. {
  128. execte("set", "playback_terminators=none", strChanId);
  129. return execte("playback", strFilePath.c_str(), strChanId);
  130. }
  131. bool EslGateway::excDetectSpeech(const std::string & strChanId)
  132. {
  133. return execte("detect_speech", "unimrcp:mrcpv2 hello hello", strChanId);
  134. }
  135. bool EslGateway::excDetectSpeechPause(const std::string & strChanId)
  136. {
  137. return execte("detect_speech", "pause", strChanId);
  138. }
  139. bool EslGateway::excDetectSpeechResume(const std::string & strChanId)
  140. {
  141. return execte("detect_speech", "resume", strChanId);
  142. }
  143. bool EslGateway::excPlayAndDetectSpeech(const std::string & strChanId, const std::string & strFilePath)
  144. {
  145. return execte("play_and_detect_speech", "ivr/say_yes_or_no.wavdetect:unimrcp {start-input-timers=false,no-input-timeout=5000,recognition-timeout=5000}builtin:grammar/boolean?language=en-US;y=1;n=2", strChanId);
  146. }
  147. bool EslGateway::execte(const std::string & strApp, const std::string & strParam, const std::string & strChanId)
  148. {
  149. esl_status_t Status = esl_execute(&m_EslHdl4Send, strApp.c_str(), strParam.c_str(), strChanId.c_str());
  150. return Status == ESL_SUCCESS;
  151. }
  152. bool EslGateway::sendCmd(const std::string & pCmd)
  153. {
  154. esl_status_t Status = esl_send_recv(&m_EslHdl4Send, pCmd.c_str());
  155. return Status == ESL_SUCCESS;
  156. }
  157. void EslGateway::__eventThread(void)
  158. {
  159. esl_event_t* pEvent = nullptr;
  160. esl_handle_t* pListenHandle = &m_EslHdl4Listen;
  161. // 下面的设置保证执行同时多个APP时,后面的不会抢占前面的
  162. pListenHandle->event_lock = 1;
  163. while (!m_Stop)
  164. {
  165. // esl_recv_event会阻塞线程,其第二个参数为1,表示优先检查内部缓存列表,防止遗漏事件。
  166. auto status = esl_recv_event(pListenHandle, 1, nullptr);
  167. if ((status == ESL_SUCCESS || status == ESL_BREAK) && !m_Stop)
  168. {
  169. pEvent = pListenHandle->last_ievent;
  170. if (pEvent != nullptr)
  171. {
  172. __onEslEvent(pEvent);
  173. }
  174. }
  175. else // 连接中断
  176. {
  177. m_bIsConn.store(false);
  178. // 连接中断
  179. LOG_WARN_S("FS链接断开...");
  180. do
  181. {
  182. LOG_WARN_S("FS 3秒后自动重连...");
  183. std::this_thread::sleep_for(std::chrono::seconds(3));
  184. LOG_WARN_S("FS开始重连...");
  185. } while (!start() && !m_Stop);
  186. }
  187. }
  188. }
  189. void EslGateway::__onEslEvent(esl_event_t * pEvent)
  190. {
  191. switch (pEvent->event_id) {
  192. case ESL_EVENT_CHANNEL_CREATE:
  193. {
  194. m_usedCount++;
  195. }
  196. break;
  197. case ESL_EVENT_CHANNEL_HANGUP_COMPLETE:
  198. {
  199. m_usedCount--;
  200. }
  201. break;
  202. default:
  203. break;
  204. }
  205. }
  206. void EslGateway::__onEslEvtChanEvent(esl_event_t * pEvent)
  207. {
  208. // 获取会话ID
  209. std::string strCallID = "";
  210. if ((strCallID = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_CCID))) == "") {
  211. strCallID = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_CALLID));
  212. }
  213. // 获取通道ID
  214. const auto pChanID = esl_event_get_header(pEvent, ESL_HEADER_CHANID);
  215. const std::string strChanID = std::to_string(pChanID);
  216. // 获取主被叫
  217. std::string strCaller = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_CALLER));
  218. std::string strCallee = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_CALLEE));
  219. if (strCallee == "") {
  220. strCallee = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_DEST_NUM));
  221. }
  222. // 获取通道方向
  223. /*CALL_DIRECTION emDirection;
  224. if (strcmp(esl_event_get_header(pEvent, ESL_HEADER_DIRECTION), ESL_HDR_DIRECTION_INBOUND) == 0)
  225. emDirection = CALL_DIRECTION_INBOUND;
  226. else
  227. emDirection = CALL_DIRECTION_OUTBOUND;*/
  228. switch (pEvent->event_id)
  229. {
  230. case ESL_EVENT_CHANNEL_CREATE:
  231. {
  232. LOG_INFO_S(boost::str(Format("ESL通道创建事件[%s][%s]") % strCallID %strChanID));
  233. }
  234. break;
  235. case ESL_EVENT_CHANNEL_ANSWER:
  236. {
  237. LOG_INFO_S(boost::str(Format("ESL通道接听事件[%s][%s]") % strCallID %strChanID));
  238. }
  239. break;
  240. case ESL_EVENT_CHANNEL_HANGUP_COMPLETE:
  241. {
  242. LOG_INFO_S(boost::str(Format("ESL通道挂机事件[%s][%s]") % strCallID %strChanID));
  243. }
  244. break;
  245. default:
  246. break;
  247. }
  248. }
  249. void EslGateway::__hangupEvent(esl_event_t * pEvent)
  250. {
  251. }
  252. void EslGateway::__detectSpeech(esl_event_t * pEvent)
  253. {
  254. std::string strCallID = "";
  255. auto pCallID = esl_event_get_header(pEvent, ESL_HEADER_CALLID);
  256. strCallID = pCallID == nullptr ? "" : pCallID;
  257. auto pCallCCID = esl_event_get_header(pEvent, ESL_HEADER_CCID);
  258. if (pCallCCID != nullptr) {
  259. strCallID = pCallCCID;
  260. }
  261. // 获取通道ID
  262. std::string strChanID = "";
  263. auto pChanID = esl_event_get_header(pEvent, ESL_HEADER_CHANID);
  264. strChanID = pChanID == nullptr ? "" : pChanID;
  265. char* speechType = nullptr;
  266. speechType = esl_event_get_header(pEvent, "Speech-Type");
  267. if (speechType == nullptr) {
  268. LOG_WARN_S(boost::str(Format("ESL语音识别事件[%s][%s],Speech-Type 空") % strCallID %strChanID));
  269. return;
  270. }
  271. LOG_INFO_S(boost::str(Format("ESL语音识别事件[%s][%s][%s]") % speechType % strCallID %strChanID));
  272. if (strcmp(speechType, "begin-speaking") == 0) {
  273. }
  274. else if (strcmp(speechType, "detected-speech") == 0)
  275. {
  276. excDetectSpeechPause(strChanID); // 暂停识别
  277. char* result = esl_event_get_body(pEvent);
  278. if (result) {
  279. LOG_DEBUG("%s", result);
  280. std::string strRes, err;
  281. parseASRXml(result, strRes, err);
  282. LOG_INFO_S(boost::str(Format("识别结果:\n[%s]") % strRes));
  283. std::wstring wsRes = string2wstring(strRes);
  284. if (strRes == "") { // 没有识别到说的内容
  285. execPlay(pChanID, "E:\\zhinengwav\\empty.wav");
  286. }
  287. /*else if (wsRes.compare(L"人工") == 0) {*/
  288. else if (strRes.find("人工") != std::string::npos) {
  289. execte("set", "trunkCallIn=2", strChanID);
  290. execPlay(pChanID, "E:\\zhinengwav\\turnagent.wav");
  291. }
  292. else {
  293. }
  294. }
  295. }
  296. }
  297. void EslGateway::__onEslEvtChanExecComplete(esl_event_t * pEvent)
  298. {
  299. auto isTrunkCallIn = esl_event_get_header(pEvent, "variable_trunkCallIn");
  300. if (isTrunkCallIn == NULL || strcmp(isTrunkCallIn, "1") == 0 || strcmp(isTrunkCallIn, "2") == 0) return;
  301. auto ChanId = esl_event_get_header(pEvent, ESL_HEADER_CHANID);
  302. const char* pApp = esl_event_get_header(pEvent, "Application");
  303. LOG_INFO("通道[%s]执行APP[%s]完成", ChanId, pApp);
  304. if (strcmp(pApp, "playback") == 0) {
  305. LOG_INFO("通道[%s]放音完成,继续识别", ChanId);
  306. excDetectSpeechResume(ChanId); // 放音完成,继续识别
  307. }
  308. }
  309. void EslGateway::__onEslEvtCustom(esl_event_t * pEvent)
  310. {
  311. const char* pSubClass = esl_event_get_header(pEvent, ESL_HEADER_SUBCLASS);
  312. if (pSubClass == nullptr)return;
  313. auto HeaderValue = esl_event_get_header(pEvent, ESL_HEADER_EXTEN_NO);
  314. if (strcmp(pSubClass, ESL_HDR_SUBCLASS_SIP_REG) == 0) { // 分机注册
  315. if (HeaderValue == nullptr) return;
  316. __addExtenChan(std::to_string(HeaderValue));
  317. }
  318. else if (strcmp(pSubClass, ESL_HDR_SUBCLASS_SIP_UNREG) == 0) { // 分机取消注册
  319. if (HeaderValue == nullptr) return;
  320. __delExtenChan(std::to_string(HeaderValue));
  321. }
  322. }
  323. void EslGateway::__onEslEvtPark(esl_event_t * pEvent)
  324. {
  325. // 获取通道ID
  326. auto ChanId = esl_event_get_header(pEvent, ESL_HEADER_CHANID);
  327. if (ChanId == NULL)return;
  328. // 获取通道方向
  329. auto Direct = esl_event_get_header(pEvent, ESL_HEADER_DIRECTION);
  330. if (!Direct || strcmp(Direct, ESL_HDR_DIRECTION_INBOUND) != 0) return;
  331. auto isTrunkCallIn = esl_event_get_header(pEvent, "variable_trunkCallIn");
  332. if (isTrunkCallIn == NULL || strcmp(isTrunkCallIn, "1") != 0) return;
  333. auto strCaller = esl_event_get_header(pEvent, ESL_HEADER_CALLER);
  334. execte("set", "trunkCallIn=0", ChanId);
  335. excDetectSpeech(ChanId);
  336. //excPlayAndDetectSpeech("E:\\zhinengwav\\welcome.wav",ChanId);
  337. LOG_INFO("来电[%s]进入智能语音系统", strCaller);
  338. }
  339. void EslGateway::__addExtenChan(const std::string & strNo)
  340. {
  341. LOG_INFO_S(boost::str(Format("分机[%s]注册") % strNo));
  342. }
  343. void EslGateway::__delExtenChan(const std::string & strNo)
  344. {
  345. LOG_INFO_S(boost::str(Format("分机[%s]注册取消成功") % strNo));
  346. }
  347. std::string EslGateway::__createJson(const std::string & strType, const bool & bOk, const std::string&strDesc)
  348. {
  349. Json::Value root;
  350. root["Type"] = strType;
  351. root["Result"] = bOk;
  352. root["Desc"] = strDesc;
  353. Json::StreamWriterBuilder builder;
  354. return Json::writeString(builder, root);
  355. }