多数据源中间件标准版1.0

FsProxy.cpp 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122
  1. #include "StdAfx.h"
  2. #include "FsProxy.h"
  3. #include "ChanExten.h"
  4. #include "ChanTrunk.h"
  5. #include "Session.h"
  6. #include "OperationReactor.h"
  7. #include "../IVR/IvrSysInc.h"
  8. SINGLETON_IMPLEMENT(CFsProxy)
  9. CFsProxy::CFsProxy(void) : m_Gateway(this), m_pEventHandler(NULL)
  10. {
  11. }
  12. CFsProxy::~CFsProxy(void)
  13. {
  14. }
  15. /*****************************************************************
  16. **【函数名称】 __transLogicState2CtiState
  17. **【函数功能】 将逻辑状态转换为CTI识别的状态
  18. **【参数】
  19. **【返回值】
  20. ****************************************************************/
  21. UINT CFsProxy::__transLogicState2CtiState(DEV_RES_TYPE ChanType, CHAN_LOGIC_STATE State)
  22. {
  23. if (ChanType == DEV_RES_TYPE_EXT)
  24. {
  25. UINT HoldMask = State & HELD_STATE_IND_MASK;
  26. switch (State & HELD_STATE_FILTER_MASK)
  27. {
  28. case CHAN_LOGIC_STATE_DISABLED: return INNER_STATE_DISABLED; // 不可用
  29. case CHAN_LOGIC_STATE_FREE: return INNER_STATE_FREE | HoldMask; // 空闲
  30. case CHAN_LOGIC_STATE_INIT: return INNER_STATE_INIT | HoldMask; // 摘机等待拨号
  31. case CHAN_LOGIC_STATE_DIALING: return INNER_STATE_DIALING | HoldMask; // 拨号
  32. case CHAN_LOGIC_STATE_RING_BACK: return INNER_STATE_RING_BACK | HoldMask; // 呼出振铃
  33. case CHAN_LOGIC_STATE_ALERTING: return INNER_STATE_ALERTING | HoldMask; // 来电振铃
  34. case CHAN_LOGIC_STATE_TALKING: return INNER_STATE_TALKING | HoldMask; // 通话中
  35. default:
  36. ASSERT(FALSE);
  37. return INNER_STATE_DISABLED;
  38. }
  39. }
  40. else
  41. {
  42. switch (State)
  43. {
  44. case CHAN_LOGIC_STATE_DISABLED: return TRUNK_STATE_DISABLED; // 不可用
  45. case CHAN_LOGIC_STATE_FREE: return TRUNK_STATE_FREE; // 空闲
  46. case CHAN_LOGIC_STATE_DIALING: return TRUNK_STATE_DIALING; // 拨号
  47. case CHAN_LOGIC_STATE_RING_BACK: return TRUNK_STATE_RING_BACK; // 呼出振铃
  48. case CHAN_LOGIC_STATE_ALERTING: return TRUNK_STATE_ALERTING; // 来电振铃
  49. case CHAN_LOGIC_STATE_STANDBY:
  50. case CHAN_LOGIC_STATE_TALKING: return TRUNK_STATE_TALKING; // 通话中
  51. default:
  52. ASSERT(FALSE);
  53. return TRUNK_STATE_UNKNOWN;
  54. }
  55. }
  56. }
  57. /*****************************************************************
  58. **【函数名称】 __addExten
  59. **【函数功能】 添加分机
  60. **【参数】
  61. **【返回值】
  62. ****************************************************************/
  63. void CFsProxy::__addExten(UINT ExtenNo)
  64. {
  65. std::lock_guard<std::mutex> lock(m_LockMapChanExt);
  66. CChanExten* pExten = new CChanExten(this, ExtenNo);
  67. m_MapChanExt.SetAt(ExtenNo, pExten);
  68. pExten->regist();
  69. }
  70. /*****************************************************************
  71. **【函数名称】 delExten
  72. **【函数功能】 删除分机
  73. **【参数】
  74. **【返回值】
  75. ****************************************************************/
  76. void CFsProxy::__delExten(UINT ExtenNo)
  77. {
  78. std::lock_guard<std::mutex> lock(m_LockMapChanExt);
  79. CChanExten* pExten = NULL;
  80. if (m_MapChanExt.Lookup(ExtenNo, pExten))
  81. {
  82. if (pExten->isFree())
  83. {
  84. m_MapChanExt.RemoveKey(ExtenNo);
  85. FS_LINK_DELETE(pExten);
  86. }
  87. else pExten->discard(true);
  88. }
  89. }
  90. /*****************************************************************
  91. **【函数名称】 __freeExten
  92. **【函数功能】 清空分机
  93. **【参数】
  94. **【返回值】
  95. ****************************************************************/
  96. void CFsProxy::__freeExten(void)
  97. {
  98. std::lock_guard<std::mutex> lock(m_LockMapChanExt);
  99. UINT ExtenNo = 0;
  100. CChanExten* pExten = NULL;
  101. POSITION Pos = m_MapChanExt.GetStartPosition();
  102. while (Pos != NULL)
  103. {
  104. m_MapChanExt.GetNextAssoc(Pos, ExtenNo, pExten);
  105. FS_LINK_DELETE(pExten);
  106. }
  107. m_MapChanExt.RemoveAll();
  108. }
  109. /*****************************************************************
  110. **【函数名称】 __initTrunkChan
  111. **【函数功能】 初始化中继通道
  112. **【参数】
  113. **【返回值】
  114. ****************************************************************/
  115. void CFsProxy::__initTrunkChan(void)
  116. {
  117. std::lock_guard<std::mutex> lock(m_LockArrayTrunk);
  118. for (int i = 1; i <= CConfig::trunkCount(); ++i)
  119. {
  120. CChanTrunk* pTrunk = new CChanTrunk(this, i);
  121. m_ArrayTrunk.Add(pTrunk);
  122. pTrunk->regist();
  123. }
  124. }
  125. /*****************************************************************
  126. **【函数名称】 __freeTrunkChan
  127. **【函数功能】 清空中继通道
  128. **【参数】
  129. **【返回值】
  130. ****************************************************************/
  131. void CFsProxy::__freeTrunkChan(void)
  132. {
  133. std::lock_guard<std::mutex> lock(m_LockArrayTrunk);
  134. CChanTrunk* pTrunk = NULL;
  135. for (int i = 0; i < m_ArrayTrunk.GetCount(); ++i)
  136. {
  137. pTrunk = m_ArrayTrunk[i];
  138. FS_LINK_DELETE(pTrunk);
  139. }
  140. m_ArrayTrunk.RemoveAll();
  141. }
  142. /*****************************************************************
  143. **【函数名称】 __getSession
  144. **【函数功能】 查找会话
  145. **【参数】
  146. **【返回值】
  147. ****************************************************************/
  148. CSession* CFsProxy::__getSession(PCHAN_EVENT_NOTIFY pNotify, bool NewWhenNull /*= false*/)
  149. {
  150. CSession* pSession = __getSession(pNotify->CallId);
  151. if (pSession == NULL && NewWhenNull)
  152. {
  153. pSession = new CSession(this, pNotify->CallId);
  154. pSession->prepare(pNotify);
  155. std::lock_guard<std::mutex> lock(m_LockMapSesssoin);
  156. m_MapSession.SetAt(pNotify->CallId, pSession);
  157. LOGGER(LOG_LEVEL_NORMAL, _T("{CFsProxy}:Add Session[%s],ChanId[%s],EventId[%d]"), pSession->id(), pNotify->ChanId, pNotify->EventId);
  158. }
  159. return pSession;
  160. }
  161. /*****************************************************************
  162. **【函数名称】 __getSession
  163. **【函数功能】 查找会话
  164. **【参数】
  165. **【返回值】
  166. ****************************************************************/
  167. CSession* CFsProxy::__getSession(LPCTSTR SessionId)
  168. {
  169. std::lock_guard<std::mutex> lock(m_LockMapSesssoin);
  170. CSession* pSession = NULL;
  171. m_MapSession.Lookup(SessionId, pSession);
  172. return pSession;
  173. }
  174. /*****************************************************************
  175. **【函数名称】 __delSession
  176. **【函数功能】 删除会话
  177. **【参数】
  178. **【返回值】
  179. ****************************************************************/
  180. void CFsProxy::__delSession(LPCTSTR SessionId)
  181. {
  182. if (SessionId == NULL)
  183. return;
  184. CSession* pSession = NULL;
  185. std::lock_guard<std::mutex> lock(m_LockMapSesssoin);
  186. if (m_MapSession.Lookup(SessionId, pSession))
  187. {
  188. m_MapSession.RemoveKey(SessionId);
  189. LOGGER(LOG_LEVEL_NORMAL, _T("{FsProxy}: Delete Session[%s]"), pSession->id());
  190. FS_LINK_DELETE(pSession);
  191. }
  192. }
  193. /*****************************************************************
  194. **【函数名称】 __freeSession
  195. **【函数功能】 清空会话
  196. **【参数】
  197. **【返回值】
  198. ****************************************************************/
  199. void CFsProxy::__freeSession(void)
  200. {
  201. CString SessionId = 0;
  202. CSession* pSession = NULL;
  203. std::lock_guard<std::mutex> lock(m_LockMapSesssoin);
  204. POSITION Pos = m_MapSession.GetStartPosition();
  205. while (Pos != NULL)
  206. {
  207. m_MapSession.GetNextAssoc(Pos, SessionId, pSession);
  208. FS_LINK_DELETE(pSession);
  209. }
  210. m_MapSession.RemoveAll();
  211. }
  212. /*****************************************************************
  213. **【函数名称】 __kill
  214. **【函数功能】 挂断通道
  215. **【参数】
  216. **【返回值】
  217. ****************************************************************/
  218. bool CFsProxy::__kill(LONG JobId, LPCTSTR ChanId)
  219. {
  220. CString EslCmd;
  221. if (JobId == FS_LINK_JOBID_INVALID)
  222. EslCmd.Format(_T("bgapi uuid_kill %s"), ChanId);
  223. else
  224. EslCmd.Format(_T("bgapi uuid_kill %s\r\n%s: %ld"), ChanId, ESL_HEADER_JOB_UUID, JobId);
  225. return m_Gateway.sendCmd(EslCmd);
  226. }
  227. /*****************************************************************
  228. **【函数名称】 init
  229. **【函数功能】 初始化
  230. **【参数】
  231. **【返回值】
  232. ****************************************************************/
  233. bool CFsProxy::init(IEslEventHandler* pEventHandler)
  234. {
  235. ASSERT(pEventHandler != NULL);
  236. m_pEventHandler = pEventHandler;
  237. __initTrunkChan();
  238. if (m_Gateway.start())
  239. {
  240. m_Gateway.hangupAll();
  241. if (m_Gateway.scanExten())
  242. {
  243. LOGGER(LOG_LEVEL_NORMAL, _T("{FsProxy}: FreeSWITCH代理初始化成功"));
  244. return true;
  245. }
  246. else
  247. {
  248. LOGGER(LOG_LEVEL_ERROR, _T("{FsProxy}: FreeSWITCH代理初始化失败, 扫描分机资源失败"));
  249. return false;
  250. }
  251. }
  252. else
  253. {
  254. LOGGER(LOG_LEVEL_ERROR, _T("{FsProxy}: FreeSWITCH代理初始化失败, ESL网关启动失败"));
  255. m_pEventHandler = NULL;
  256. return false;
  257. }
  258. }
  259. /*****************************************************************
  260. **【函数名称】 release
  261. **【函数功能】 释放资源
  262. **【参数】
  263. **【返回值】
  264. ****************************************************************/
  265. void CFsProxy::release(void)
  266. {
  267. esl_disconnect(&m_EslHandle);
  268. m_Gateway.stop();
  269. __freeSession();
  270. __freeExten();
  271. __freeTrunkChan();
  272. }
  273. /*****************************************************************
  274. **【函数名称】 onChanRegist
  275. **【函数功能】 通道注册的处理函数
  276. **【参数】
  277. **【返回值】
  278. ****************************************************************/
  279. void CFsProxy::onChanRegist(DEV_RES_TYPE ChanType, UINT ChanNo, CHAN_LOGIC_STATE ChanState)
  280. {
  281. CDevControl::GetInstance().onEventResDetail(ChanType, ChanNo);
  282. CDevControl::GetInstance().onEventResState(ChanType, ChanNo, __transLogicState2CtiState(ChanType, ChanState));
  283. }
  284. /*****************************************************************
  285. **【函数名称】 onExtenDestroy
  286. **【函数功能】 分机销毁的处理函数
  287. **【参数】
  288. **【返回值】
  289. ****************************************************************/
  290. void CFsProxy::onExtenDestroy(UINT ExtenNo)
  291. {
  292. CDevControl::GetInstance().onEventResState(DEV_RES_TYPE_EXT, ExtenNo, INNER_STATE_REMOVE);
  293. }
  294. /*****************************************************************
  295. **【函数名称】 onExtenDirectOp
  296. **【函数功能】 分机直接操作启动
  297. **【参数】 OpType 启动的操作类型
  298. pHostChan 触发事件的通道
  299. pNotify 触发操作事件内容
  300. **【返回值】
  301. ****************************************************************/
  302. void CFsProxy::onExtenDirectOp(DEV_OP OpType, CVirtualChan* pHostChan, PCHAN_EVENT_NOTIFY pNotify)
  303. {
  304. switch (OpType)
  305. {
  306. case DEV_OP_CALL_OUT:
  307. {
  308. if (!COperationReactor::GetInstance().onExtenCallFromDev(pHostChan, pNotify))
  309. __kill(FS_LINK_JOBID_INVALID, pHostChan->chanId());
  310. }
  311. break;
  312. } // end switch
  313. }
  314. /*****************************************************************
  315. **【函数名称】 onChanStateUpdate
  316. **【函数功能】 通道状态更新处理
  317. **【参数】
  318. **【返回值】
  319. *****************************************************************/
  320. void CFsProxy::onChanStateUpdate(LONG OpInstance, CVirtualChan* pChan)
  321. {
  322. if (OpInstance != FS_LINK_INSTANCE_INVALID)
  323. m_pEventHandler->onEslEvtChanState(OpInstance, pChan);
  324. CDevControl::GetInstance().onEventResState(pChan->type(), pChan->no(), __transLogicState2CtiState(pChan->type(), pChan->state()), pChan->callerNum(), pChan->calleeNum());
  325. }
  326. /*****************************************************************
  327. **【函数名称】 onChanPoor
  328. **【函数功能】 空闲通道枯竭处理
  329. **【参数】
  330. **【返回值】
  331. *****************************************************************/
  332. void CFsProxy::onChanPoor(CSession* pSession, PCHAN_EVENT_NOTIFY pNotify)
  333. {
  334. __kill(FS_LINK_JOBID_INVALID, pNotify->ChanId);
  335. }
  336. /*****************************************************************
  337. **【函数名称】 onTrunkCallIn
  338. **【函数功能】 中继呼入
  339. **【参数】
  340. **【返回值】
  341. *****************************************************************/
  342. void CFsProxy::onTrunkCallIn(CChanTrunk* pTrunk)
  343. {
  344. CDevControl::GetInstance().onEventDevOperation(pTrunk->no(), DEV_OP_CALL_IN, pTrunk->callerNum(), pTrunk->calleeNum());
  345. }
  346. /*****************************************************************
  347. **【函数名称】 onEslDisconnect
  348. **【函数功能】 ESL连接中断处理
  349. **【参数】
  350. **【返回值】
  351. *****************************************************************/
  352. void CFsProxy::onEslDisconnect(void)
  353. {
  354. CDevControl::GetInstance().onEventDevDown();
  355. }
  356. /*****************************************************************
  357. **【函数名称】 onEslSipReg
  358. **【函数功能】 分机注册的处理函数
  359. **【参数】
  360. **【返回值】
  361. ****************************************************************/
  362. void CFsProxy::onEslExtenReg(UINT ExtenNo)
  363. {
  364. CChanExten* pExten = getExten(ExtenNo);
  365. if (pExten == NULL)
  366. {
  367. __addExten(ExtenNo);
  368. }
  369. else
  370. {
  371. if (pExten->isVoid()) // 若被丢弃,则改变丢弃状态
  372. pExten->discard(false);
  373. }
  374. #ifdef _DEBUG
  375. LOGGER(LOG_LEVEL_NORMAL, _T("{FsProxy}: 分机[%u]注册"), ExtenNo);
  376. #endif
  377. }
  378. /*****************************************************************
  379. **【函数名称】 onEslExtenUnreg
  380. **【函数功能】 分机注销的处理函数
  381. **【参数】
  382. **【返回值】
  383. ****************************************************************/
  384. void CFsProxy::onEslExtenUnreg(UINT ExtenNo)
  385. {
  386. __delExten(ExtenNo);
  387. #ifdef _DEBUG
  388. LOGGER(LOG_LEVEL_NORMAL, _T("{FsProxy}: 分机[%u]注销"), ExtenNo);
  389. #endif
  390. }
  391. /*****************************************************************
  392. **【函数名称】 onEslGwExten
  393. **【函数功能】 网关分机的处理函数
  394. **【参数】
  395. **【返回值】
  396. ****************************************************************/
  397. void CFsProxy::onEslGwExtenReg(LPCTSTR ExtenNo, LPCTSTR Ip)
  398. {
  399. m_MapAddrGwExt.SetAt(ExtenNo, Ip);
  400. return;
  401. }
  402. void CFsProxy::onEslGwExtenUnreg(LPCTSTR ExtenNo)
  403. {
  404. CString t_ip;
  405. if (m_MapAddrGwExt.Lookup(ExtenNo, t_ip))
  406. {
  407. t_ip.Empty();
  408. m_MapAddrGwExt.RemoveKey(ExtenNo);
  409. }
  410. }
  411. /*****************************************************************
  412. **【函数名称】 onEslEvtBgJobDone
  413. **【函数功能】 后台任务执行结束事件处理
  414. **【参数】
  415. **【返回值】
  416. *****************************************************************/
  417. void CFsProxy::onEslEvtBgJobDone(PBG_JOB_NOTIFY pNotify)
  418. {
  419. ASSERT(m_pEventHandler != NULL);
  420. m_pEventHandler->onEslEvtBgJobDone(pNotify);
  421. }
  422. /*****************************************************************
  423. **【函数名称】 onEslEvtChannel
  424. **【函数功能】 通道事件处理
  425. **【参数】
  426. **【返回值】
  427. *****************************************************************/
  428. void CFsProxy::onEslEvtChannel(PCHAN_EVENT_NOTIFY pNotify)
  429. {
  430. ASSERT(pNotify != NULL);
  431. if (pNotify == NULL)
  432. return;
  433. CSession* pSession = __getSession(pNotify, true);
  434. ASSERT(pSession != NULL);
  435. if (pSession == NULL)
  436. {
  437. 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);
  438. return;
  439. }
  440. __try {
  441. pSession->onChanEvent(pNotify);
  442. if (pSession->isVoid())
  443. __delSession(pSession->id());
  444. }
  445. __except (EXCEPTION_EXECUTE_HANDLER)
  446. {
  447. 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);
  448. __delSession(pSession->id());
  449. }
  450. }
  451. /*****************************************************************
  452. **【函数名称】 onEslEvtDtmf
  453. **【函数功能】 DTMF事件处理
  454. **【参数】
  455. **【返回值】
  456. *****************************************************************/
  457. void CFsProxy::onEslEvtDtmf(PDTMF_NOTIFY pNotify)
  458. {
  459. ASSERT(pNotify != NULL);
  460. CSession* pSession = __getSession(pNotify->CallId);
  461. ASSERT(pSession != NULL);
  462. if (pSession != NULL)
  463. pSession->onChanDtmf(pNotify);
  464. }
  465. /*****************************************************************
  466. **【函数名称】 onEslEvtHold
  467. **【函数功能】 保持事件处理
  468. **【参数】
  469. **【返回值】
  470. *****************************************************************/
  471. void CFsProxy::onEslEvtHold(PHOLD_NOTIFY pNotify)
  472. {
  473. ASSERT(pNotify != NULL);
  474. CSession* pSession = __getSession(pNotify->CallId);
  475. ASSERT(pSession != NULL);
  476. if (pSession != NULL)
  477. pSession->onChanHold(pNotify);
  478. }
  479. /*****************************************************************
  480. **【函数名称】 getExten
  481. **【函数功能】 查找分机
  482. **【参数】
  483. **【返回值】
  484. ****************************************************************/
  485. CChanExten* CFsProxy::getExten(UINT ExtenNo)
  486. {
  487. std::lock_guard<std::mutex> lock(m_LockMapChanExt);
  488. CChanExten* pExten = NULL;
  489. m_MapChanExt.Lookup(ExtenNo, pExten);
  490. return pExten;
  491. }
  492. /*****************************************************************
  493. **【函数名称】 getTrunk
  494. **【函数功能】 查找中继
  495. **【参数】
  496. **【返回值】
  497. ****************************************************************/
  498. CChanTrunk* CFsProxy::getTrunk(UINT TrunkNo)
  499. {
  500. std::lock_guard<std::mutex> lock(m_LockArrayTrunk);
  501. if (TrunkNo <= 0 || TrunkNo > (UINT)m_ArrayTrunk.GetCount())
  502. return NULL;
  503. --TrunkNo;
  504. return m_ArrayTrunk[TrunkNo];
  505. }
  506. /*****************************************************************
  507. **【函数名称】 delChan
  508. **【函数功能】 删除通道
  509. **【参数】
  510. **【返回值】
  511. ****************************************************************/
  512. void CFsProxy::delChan(CVirtualChan* pChan)
  513. {
  514. if (pChan->type() == DEV_RES_TYPE_EXT)
  515. __delExten(pChan->no());
  516. }
  517. /*****************************************************************
  518. **【函数名称】 getFreeTrunk
  519. **【函数功能】 查找空闲中继
  520. **【参数】
  521. **【返回值】
  522. ****************************************************************/
  523. CChanTrunk* CFsProxy::getFreeTrunk(void)
  524. {
  525. // 当前轮循到的索引
  526. static int PosStatic = 0;
  527. int CurPos = PosStatic;
  528. int BusyTruckCount = 0;
  529. std::lock_guard<std::mutex> lock(m_LockArrayTrunk);
  530. // 保证遍历一轮
  531. for (int i = 0; i < m_ArrayTrunk.GetCount(); ++i)
  532. {
  533. // 保证POS是有效的
  534. if (PosStatic >= m_ArrayTrunk.GetCount())
  535. PosStatic = 0;
  536. CChanTrunk* pTrunk = m_ArrayTrunk[PosStatic++];
  537. ASSERT(pTrunk != NULL);
  538. // 当前外线是否空闲
  539. if (pTrunk->isFree())
  540. {
  541. return pTrunk;
  542. }
  543. else
  544. {
  545. BusyTruckCount++;
  546. }
  547. }
  548. LOGGER(LOG_LEVEL_WARNING, _T("{FsProxy}: 没有空闲中继通道,起始索引=%d,当前索引=%d,中继总数量=%d,忙通道数量=%d"), CurPos, PosStatic, m_ArrayTrunk.GetCount(), BusyTruckCount);
  549. //2019.3.7
  550. PosStatic = CurPos;
  551. for (int i = 0; i < m_ArrayTrunk.GetCount(); ++i)
  552. {
  553. // 保证POS是有效的
  554. if (PosStatic >= m_ArrayTrunk.GetCount())
  555. PosStatic = 0;
  556. CChanTrunk* pTrunk = m_ArrayTrunk[PosStatic++];
  557. ASSERT(pTrunk != NULL);
  558. // 当前外线是否空闲
  559. if (pTrunk->state() == CHAN_LOGIC_STATE_FREE)
  560. {
  561. if (pTrunk->currOp() != NULL) {
  562. COperationReactor::GetInstance().releaseOpResult(pTrunk->currOp());
  563. pTrunk->releaseOp(pTrunk->currOp());
  564. LOGGER(LOG_LEVEL_WARNING, _T("{FsProxy}:没有空闲中继通道,通道[%u],释放操作."), pTrunk->no());
  565. }
  566. return pTrunk;
  567. }
  568. }
  569. LOGGER(LOG_LEVEL_WARNING, _T("{FsProxy}: 没有空闲中继通道,起始索引=%d,当前索引=%d"), CurPos, PosStatic);
  570. return NULL;
  571. }
  572. /*****************************************************************
  573. **【函数名称】 getAssoChanInSession
  574. **【函数功能】 获取会话中关联通道
  575. **【参数】
  576. **【返回值】
  577. ****************************************************************/
  578. CVirtualChan* CFsProxy::getAssoChanInSession(CVirtualChan* pChan)
  579. {
  580. CSession* pSession = __getSession(pChan->sessionId());
  581. if (pSession != NULL)
  582. return pSession->getAssoChan(pChan);
  583. else
  584. return NULL;
  585. }
  586. /*****************************************************************
  587. **【函数名称】 getBusyChan
  588. **【函数功能】 获取忙通道
  589. **【参数】
  590. **【返回值】
  591. *****************************************************************/
  592. CVirtualChan* CFsProxy::getBusyChan(LPCTSTR ChanId)
  593. {
  594. std::lock_guard<std::mutex> lock(m_LockMapBusyChan);
  595. CVirtualChan* pBusyChan = NULL;
  596. m_MapBusyChan.Lookup(ChanId, pBusyChan);
  597. return pBusyChan;
  598. }
  599. /*****************************************************************
  600. **【函数名称】 regBusyChan
  601. **【函数功能】 登记忙通道
  602. **【参数】
  603. **【返回值】
  604. *****************************************************************/
  605. void CFsProxy::regBusyChan(CVirtualChan* pChan)
  606. {
  607. std::lock_guard<std::mutex> lock(m_LockMapBusyChan);
  608. m_MapBusyChan.SetAt(pChan->chanId(), pChan);
  609. }
  610. /*****************************************************************
  611. **【函数名称】 unregBusyChan
  612. **【函数功能】 取消登记忙通道
  613. **【参数】
  614. **【返回值】
  615. *****************************************************************/
  616. void CFsProxy::unregBusyChan(CVirtualChan* pChan)
  617. {
  618. std::lock_guard<std::mutex> lock(m_LockMapBusyChan);
  619. m_MapBusyChan.RemoveKey(pChan->chanId());
  620. }
  621. /*****************************************************************
  622. **【函数名称】 ExtenCall
  623. **【函数功能】 分机呼叫
  624. **【参数】
  625. **【返回值】
  626. ****************************************************************/
  627. bool CFsProxy::ExtenCall(LONG JobId, CVirtualChan* pChan, LPCTSTR CallerNum, LPCTSTR CalleeNum)
  628. {
  629. CString EslCmd;
  630. //EslCmd.Format(_T("bgapi originate {origination_caller_id_number=%lu,absolute_codec_string='PCMA,PCMU'}user/%lu %s XML %s\r\n%s: %ld"),
  631. // pChan->no(), pChan->no(), CalleeNum, CConfig::extContext(), ESL_HEADER_JOB_UUID, JobId);
  632. try
  633. {
  634. LOGGER(LOG_LEVEL_NORMAL, _T("{CFsProxy}: ExtenCall start"));
  635. CString CallString;
  636. CString Caller, Called;
  637. Called = CalleeNum;
  638. if (!m_CallStringMaker.makeCallStringWithDefault(Caller, Called, CallString))
  639. return false;
  640. EslCmd.Format(_T("bgapi originate {origination_caller_id_number=%s,%s=%lu,absolute_codec_string='PCMA,PCMU'}user/%lu %s XML %s\r\n%s: %ld"),
  641. Called, ESL_VAR_OP_CALLER, pChan->no(), pChan->no(), CalleeNum, CConfig::extContext(), ESL_HEADER_JOB_UUID, JobId);
  642. LOGGER(LOG_LEVEL_NORMAL, _T("{CFsProxy}: ExtenCall end"));
  643. }
  644. catch (const std::exception& e)
  645. {
  646. LOGGER(LOG_LEVEL_NORMAL, _T("{CFsProxy}: ExtenCall 异常[%s]"), e.what());
  647. }
  648. return m_Gateway.sendCmd(EslCmd);
  649. }
  650. /*****************************************************************
  651. **【函数名称】 PredictionCall
  652. **【函数功能】 预测外呼
  653. **【参数】
  654. **【返回值】
  655. ****************************************************************/
  656. bool CFsProxy::PredictionCall(LONG JobId, CString CallerNum, CString CalleeNum)
  657. {
  658. CString CallString;
  659. if (!m_CallStringMaker.makeCallStringWithDefault(CallerNum, CalleeNum, CallString))
  660. return false;
  661. CString EslCmd;
  662. EslCmd.Format(_T("bgapi originate {origination_caller_id_number=%s,%s=%ld}%s %s XML %s\r\n%s: %ld"),
  663. CallerNum, ESL_VAR_OP_INSTANCE, JobId, CallString, CalleeNum, CConfig::pCallContext(), ESL_HEADER_JOB_UUID, JobId);
  664. return m_Gateway.sendCmd(EslCmd);
  665. }
  666. /*****************************************************************
  667. **【函数名称】 answer
  668. **【函数功能】 应答通道
  669. **【参数】
  670. **【返回值】
  671. ****************************************************************/
  672. bool CFsProxy::answer(LONG JobId, CVirtualChan* pChan)
  673. {
  674. return false;
  675. }
  676. /*****************************************************************
  677. **【函数名称】 kill
  678. **【函数功能】 挂断通道
  679. **【参数】
  680. **【返回值】
  681. ****************************************************************/
  682. bool CFsProxy::kill(LONG JobId, CVirtualChan* pChan)
  683. {
  684. return __kill(JobId, pChan->chanId());
  685. }
  686. /*****************************************************************
  687. **【函数名称】 consult
  688. **【函数功能】 协商呼叫
  689. **【参数】
  690. **【返回值】
  691. ****************************************************************/
  692. bool CFsProxy::consult(LONG JobId, CVirtualChan* pChan, CString DestNum)
  693. {
  694. CString CallerNum;
  695. CString CallString;
  696. if (!m_CallStringMaker.makeCallString(CallerNum, DestNum, CallString))
  697. return false;
  698. CString EslCmd;
  699. EslCmd.Format(_T("bgapi uuid_broadcast %s att_xfer::%s\r\n%s: %ld"), pChan->chanId(), CallString, ESL_HEADER_JOB_UUID, JobId);
  700. return m_Gateway.sendCmd(EslCmd);
  701. }
  702. /*****************************************************************
  703. **【函数名称】 insert
  704. **【函数功能】 强插
  705. **【参数】
  706. **【返回值】
  707. ****************************************************************/
  708. bool CFsProxy::insert(LONG JobId, CVirtualChan* pChan, LPCTSTR DestSessionId)
  709. {
  710. CString EslCmd;
  711. EslCmd.Format(_T("bgapi originate {origination_caller_id_number=%lu}user/%lu &three_way(%s)\r\n%s: %ld"),
  712. pChan->no(), pChan->no(), DestSessionId, ESL_HEADER_JOB_UUID, JobId);
  713. return m_Gateway.sendCmd(EslCmd);
  714. }
  715. /*****************************************************************
  716. **【函数名称】 intercept
  717. **【函数功能】 强截
  718. **【参数】
  719. **【返回值】
  720. ****************************************************************/
  721. bool CFsProxy::intercept(LONG JobId, CVirtualChan* pChan, LPCTSTR DestChanId)
  722. {
  723. CString EslCmd;
  724. EslCmd.Format(_T("bgapi originate {origination_caller_id_number=%lu}user/%lu &intercept(%s)\r\n%s: %ld"),
  725. pChan->no(), pChan->no(), DestChanId, ESL_HEADER_JOB_UUID, JobId);
  726. return m_Gateway.sendCmd(EslCmd);
  727. }
  728. /*****************************************************************
  729. **【函数名称】 listen
  730. **【函数功能】 监听
  731. **【参数】
  732. **【返回值】
  733. ****************************************************************/
  734. bool CFsProxy::listen(LONG JobId, CVirtualChan* pChan, LPCTSTR DestChanId)
  735. {
  736. CString EslCmd;
  737. EslCmd.Format(_T("bgapi originate {origination_caller_id_number=%lu}user/%lu &eavesdrop(%s)\r\n%s: %ld"),
  738. pChan->no(), pChan->no(), DestChanId, ESL_HEADER_JOB_UUID, JobId);
  739. return m_Gateway.sendCmd(EslCmd);
  740. }
  741. // ych
  742. std::string CFsProxy::EslCommand(std::string strFsIp, int FsPort, std::string strName, std::string strPsWd, std::string strCommand)
  743. {
  744. if (!m_EslHandle.connected)
  745. {
  746. esl_connect(&m_EslHandle, strFsIp.c_str(), FsPort, strName.c_str(), strPsWd.c_str());
  747. }
  748. if (m_EslHandle.connected)
  749. {
  750. esl_send_recv_timed(&m_EslHandle, strCommand.c_str(), 2000);
  751. if (m_EslHandle.last_sr_event && m_EslHandle.last_sr_event->body)
  752. {
  753. std::string strRet = m_EslHandle.last_sr_event->body;
  754. return strRet;
  755. }
  756. }
  757. return "";
  758. }
  759. /*****************************************************************
  760. **【函数名称】 meeting
  761. **【函数功能】 会议
  762. **【参数】
  763. **【返回值】
  764. ****************************************************************/
  765. bool CFsProxy::meeting(LONG JobId, CString CallerNum, CString DestNum, LPCTSTR MeetingId)
  766. {
  767. bool bMatchPrefix = m_CallStringMaker.isMatchPrefix(DestNum);
  768. TRUNK_MATCH* pMatch = NULL;
  769. CString CallString;
  770. if (!m_CallStringMaker.makeCallString(CallerNum, DestNum, CallString, pMatch))
  771. return false;
  772. if (pMatch != NULL)
  773. {
  774. SIP_ACCOUNT* pAccount = CConfig::sipAccount().getAccount(pMatch->TrunkItemId);
  775. if (bMatchPrefix && pAccount != NULL && pAccount->IsDynamicGw) // 外线且动态网关
  776. {
  777. std::string strSipAccount = pAccount->Account;
  778. std::string strGwIp = EslCommand(CConfig::fsAddr(), CConfig::fsPort(), "", CConfig::fsPwd(), "api sofia_contact " + strSipAccount);
  779. if (!strGwIp.empty())
  780. {
  781. size_t Index = strGwIp.find_first_of('@');
  782. if (Index != std::string::npos)
  783. {
  784. strGwIp = strGwIp.substr(Index);
  785. CallString = "sofia/internal/";
  786. CallString += DestNum;
  787. CallString += strGwIp.c_str();
  788. }
  789. }
  790. }
  791. }
  792. /*
  793. CString CallString;
  794. if (!m_CallStringMaker.makeCallString(CallerNum, DestNum, CallString))
  795. return false;
  796. */
  797. LOGGER(LOG_LEVEL_NORMAL, _T("{FsProxy}: 三方会议呼叫字符串: %s"), CallString);
  798. CString EslCmd;
  799. EslCmd.Format(_T("bgapi originate {origination_caller_id_number=%s,%s=%ld}%s %s XML %s\r\n%s: %ld"),
  800. CallerNum, ESL_VAR_OP_INSTANCE, JobId, CallString, MeetingId, CConfig::meetingContext(), ESL_HEADER_JOB_UUID, JobId);
  801. return m_Gateway.sendCmd(EslCmd);
  802. }
  803. /*****************************************************************
  804. **【函数名称】 muteOn
  805. **【函数功能】 静音
  806. **【参数】
  807. **【返回值】
  808. ****************************************************************/
  809. bool CFsProxy::muteOn(LONG JobId, CVirtualChan* pChan)
  810. {
  811. CString EslCmd;
  812. EslCmd.Format(_T("bgapi uuid_audio %s start write mute -4\r\n%s: %ld"), pChan->chanId(), ESL_HEADER_JOB_UUID, JobId);
  813. return m_Gateway.sendCmd(EslCmd);
  814. }
  815. /*****************************************************************
  816. **【函数名称】 muteOff
  817. **【函数功能】 取消静音
  818. **【参数】
  819. **【返回值】
  820. ****************************************************************/
  821. bool CFsProxy::muteOff(LONG JobId, CVirtualChan* pChan)
  822. {
  823. CString EslCmd;
  824. EslCmd.Format(_T("bgapi uuid_audio %s stop write mute -4\r\n%s: %ld"), pChan->chanId(), ESL_HEADER_JOB_UUID, JobId);
  825. return m_Gateway.sendCmd(EslCmd);
  826. }
  827. /*****************************************************************
  828. **【函数名称】 holdon
  829. **【函数功能】 保持
  830. **【参数】
  831. **【返回值】
  832. ****************************************************************/
  833. bool CFsProxy::holdon(LONG JobId, CVirtualChan* pChan)
  834. {
  835. CString EslCmd;
  836. EslCmd.Format(_T("bgapi uuid_hold %s\r\n%s: %ld"), pChan->chanId(), ESL_HEADER_JOB_UUID, JobId);
  837. return m_Gateway.sendCmd(EslCmd);
  838. }
  839. /*****************************************************************
  840. **【函数名称】 takeBack
  841. **【函数功能】 接回
  842. **【参数】
  843. **【返回值】
  844. ****************************************************************/
  845. bool CFsProxy::takeBack(LONG JobId, CVirtualChan* pChan)
  846. {
  847. CString EslCmd;
  848. EslCmd.Format(_T("bgapi uuid_hold off %s\r\n%s: %ld"), pChan->chanId(), ESL_HEADER_JOB_UUID, JobId);
  849. return m_Gateway.sendCmd(EslCmd);
  850. }
  851. /*****************************************************************
  852. **【函数名称】 record
  853. **【函数功能】 录音
  854. **【参数】
  855. **【返回值】
  856. ****************************************************************/
  857. bool CFsProxy::record(LONG JobId, CVirtualChan* pChan, LPCTSTR RcdFile)
  858. {
  859. CString EslCmd;
  860. EslCmd.Format(_T("bgapi uuid_record %s start %s\r\n%s: %ld"), pChan->chanId(), RcdFile, ESL_HEADER_JOB_UUID, JobId);
  861. return m_Gateway.sendCmd(EslCmd);
  862. }
  863. /*****************************************************************
  864. **【函数名称】 transfer
  865. **【函数功能】 呼叫转移
  866. **【参数】
  867. **【返回值】
  868. ****************************************************************/
  869. bool CFsProxy::transfer(LONG JobId, CVirtualChan* pChan, LPCTSTR DestNum)
  870. {
  871. CString Param;
  872. Param.Format(_T("ringback=${us-ring}"));
  873. //Param.Format(_T("ringback=$${sound_prefix}\\ringback.mp3"));
  874. m_Gateway.Execte2(ESL_APP_SET, Param, pChan->chanId());
  875. CString EslCmd;
  876. EslCmd.Format(_T("bgapi uuid_transfer %s %s xml %s\r\n%s: %ld"), pChan->chanId(), DestNum, CConfig::extContext(), ESL_HEADER_JOB_UUID, JobId);
  877. return m_Gateway.sendCmd(EslCmd);
  878. }
  879. /*****************************************************************
  880. **【函数名称】 transfer2Context
  881. **【函数功能】 转移通道至其它Context
  882. **【参数】
  883. **【返回值】
  884. ****************************************************************/
  885. bool CFsProxy::transfer2Context(LONG JobId, LPCTSTR DestChanId, LPCTSTR Exten, LPCTSTR Context, bool BothSide /*= false*/)
  886. {
  887. CString EslCmd;
  888. if (BothSide)
  889. EslCmd.Format(_T("bgapi uuid_transfer %s -both %s xml %s\r\n%s: %ld"), DestChanId, Exten, Context, ESL_HEADER_JOB_UUID, JobId);
  890. else
  891. EslCmd.Format(_T("bgapi uuid_transfer %s %s xml %s\r\n%s: %ld"), DestChanId, Exten, Context, ESL_HEADER_JOB_UUID, JobId);
  892. return m_Gateway.sendCmd(EslCmd);
  893. }
  894. /*****************************************************************
  895. **【函数名称】 cancel
  896. **【函数功能】 取消当前执行的应用
  897. **【参数】
  898. **【返回值】
  899. ****************************************************************/
  900. bool CFsProxy::cancel(CChanTrunk* pChan)
  901. {
  902. ASSERT(pChan != NULL);
  903. return m_Gateway.Execte2(ESL_APP_BREAK, NULL, pChan->chanId());
  904. }
  905. /*****************************************************************
  906. **【函数名称】 hangup
  907. **【函数功能】 挂机
  908. **【参数】
  909. **【返回值】
  910. ****************************************************************/
  911. bool CFsProxy::hangup(CChanTrunk* pChan)
  912. {
  913. ASSERT(pChan != NULL);
  914. return m_Gateway.execute(pChan->m_pEslHandle, ESL_APP_HANGUP, NULL);
  915. }
  916. /*****************************************************************
  917. **【函数名称】 leaveWord
  918. **【函数功能】 留言
  919. **【参数】
  920. **【返回值】
  921. ****************************************************************/
  922. bool CFsProxy::leaveWord(CChanTrunk* pChan, LPCTSTR FileName, UINT LimitTime, TCHAR FinishKey)
  923. {
  924. ASSERT(pChan != NULL);
  925. CString Param;
  926. Param.Format(_T("playback_terminators=%c"), FinishKey);
  927. m_Gateway.Execte2(ESL_APP_SET, Param, pChan->chanId());
  928. Param.Format(_T("%s %lu"), FileName, LimitTime);
  929. return m_Gateway.Execte2(ESL_APP_LEAVEWORD, Param, pChan->chanId());
  930. }
  931. /*****************************************************************
  932. **【函数名称】 playAndDtmf
  933. **【函数功能】 放音收按键
  934. **【参数】
  935. **【返回值】
  936. ****************************************************************/
  937. bool CFsProxy::playAndDtmf(CChanTrunk* pChan, PlayVoiceContent* pPlayContent)
  938. {
  939. ASSERT(pChan != NULL);
  940. ASSERT(pPlayContent != NULL);
  941. if (pChan == NULL || pPlayContent == NULL) return false;
  942. TCHAR AudioFile[MAX_PATH] = { 0 };
  943. // 如果使用了TTS,进行TTS转换
  944. switch (pPlayContent->nTts)
  945. {
  946. default:
  947. case PLAY_CONTENT_AUDIO: // 不使用TTS
  948. lstrcpy(AudioFile, pPlayContent->szFileName);
  949. break;
  950. case PLAY_CONTENT_TTS_STR: // TTS文本
  951. {
  952. ITtsInterface::getInstance().setTTSParam(pPlayContent->nTtsDigitMode, pPlayContent->nTtsSpeed, pPlayContent->nTtsVolume);
  953. if (!ITtsInterface::getInstance().string2Audio(pPlayContent->szFileName, AudioFile, MAX_PATH))
  954. {
  955. LOGGER(LOG_LEVEL_WARNING, _T("{FsProxy}: 中继通道[%lu]放音时TTS文本转换失败"), pChan->no());
  956. return false;
  957. }
  958. }
  959. break;
  960. case PLAY_CONTENT_TTS_FILE: // TTS文件
  961. {
  962. ITtsInterface::getInstance().setTTSParam(pPlayContent->nTtsDigitMode, pPlayContent->nTtsSpeed, pPlayContent->nTtsVolume);
  963. if (!ITtsInterface::getInstance().file2Audio(pPlayContent->szFileName, AudioFile, MAX_PATH))
  964. {
  965. LOGGER(LOG_LEVEL_WARNING, _T("{FsProxy}: 中继通道[%lu]放音时TTS文件转换失败, File = %s"), pChan->no(), pPlayContent->szFileName);
  966. return false;
  967. }
  968. }
  969. break;
  970. }
  971. CString Param;
  972. if (pPlayContent->nModel == PVM_PLAY_ONLY && pPlayContent->cDtmfEnd == 0)
  973. {
  974. Param.Format(_T("playback_terminators=none"));
  975. m_Gateway.Execte2(ESL_APP_SET, Param, pChan->chanId());
  976. //m_Gateway.execute(pChan->m_pEslHandle,ESL_APP_SET,Param);
  977. Param.Format(_T("%s"), AudioFile);
  978. return m_Gateway.Execte2(ESL_APP_PLAY, Param, pChan->chanId());
  979. //return m_Gateway.execute(pChan->m_pEslHandle,ESL_APP_PLAY,Param);
  980. }
  981. else
  982. {
  983. Param.Format(_T("%lu %lu 1 %lu %c %s silence_stream://250 %s"), pPlayContent->nDtmfCount, pPlayContent->nDtmfCount,
  984. pPlayContent->nDtmfCount * pPlayContent->nDtmfPeriod * 1000, pPlayContent->cDtmfEnd, AudioFile, ESL_VAR_DTMF_KEY);
  985. return m_Gateway.Execte2(ESL_APP_PLAY_DTMF, Param, pChan->chanId());
  986. //return m_Gateway.execute(pChan->m_pEslHandle,ESL_APP_PLAY_DTMF,Param);
  987. }
  988. }
  989. /*****************************************************************
  990. **【函数名称】 bridge
  991. **【函数功能】 桥接通道
  992. **【参数】
  993. **【返回值】
  994. ****************************************************************/
  995. bool CFsProxy::bridge(CChanTrunk* pChan, CString CallerNum, CString CalleeNum)
  996. {
  997. CString CallString;
  998. if (!m_CallStringMaker.makeCallString(CallerNum, CalleeNum, CallString))
  999. return false;
  1000. CString Param;
  1001. Param.Format(_T("ringback=${us-ring}"));
  1002. m_Gateway.Execte2(ESL_APP_SET, Param, pChan->chanId());
  1003. return m_Gateway.Execte2(ESL_APP_BRIDGE, CallString, pChan->chanId());
  1004. }
  1005. /*****************************************************************
  1006. **【函数名称】 bridge
  1007. **【函数功能】 桥接通道 动态网关方法
  1008. **【参数】
  1009. **【返回值】
  1010. ****************************************************************/
  1011. bool CFsProxy::bridge(CChanTrunk* pChan, CString CalleeNum)
  1012. {
  1013. CString EslCmd;
  1014. EslCmd.Format(_T("bgapi uuid_transfer %s %s xml %s"), pChan->chanId(), CalleeNum, "ForExten");
  1015. return m_Gateway.sendCmd(EslCmd);
  1016. }