mod_counterconnector.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  1. /////////////////////////////////
  2. ///// 连线服务
  3. /////////////////////////////////
  4. #include "stdafx.h"
  5. #include "ListEntry.h"
  6. #include "Event.h"
  7. #include "mod_counterconnector.h"
  8. #include "CounterConnector_msg_g.h"
  9. #include "../include/EventCode.h"
  10. #include "y2k_time.h"
  11. using namespace CounterConnector;
  12. #define EVT_CONVERTER "EventConverter"
  13. void CCounterConnectorEntity ::OnPreStart(CAutoArray<CSimpleStringA> strArgs,CSmartPointer<ITransactionContext> pTransactionContext)
  14. {
  15. ErrorCodeEnum Error = __OnStart(Error_Succeed);
  16. CSmartPointer<IEntityFunction> spFunction = GetFunction();
  17. Error = spFunction->SubscribeBroadcast("LivenessDetection", NULL, this, m_uidlivenessListener);
  18. if (Error != Error_Succeed)
  19. {
  20. LOG_TRACE("subscribe LivenessDetection evt failed!");
  21. pTransactionContext->SendAnswer(Error);
  22. return;
  23. }
  24. pTransactionContext->SendAnswer(Error);
  25. }
  26. void CCounterConnectorEntity ::OnPreClose(EntityCloseCauseEnum eCloseCause,CSmartPointer<ITransactionContext> pTransactionContext)
  27. {
  28. ErrorCodeEnum Error = __OnClose(Error_Succeed);
  29. pTransactionContext->SendAnswer(Error);
  30. }
  31. void CCounterConnectorEntity ::OnSelfTest(EntityTestEnum eTestType,CSmartPointer<ITransactionContext> pTransactionContext)
  32. {
  33. if (Test_ShakeHand == eTestType)
  34. {
  35. pTransactionContext->SendAnswer(Error_Succeed);
  36. }
  37. }
  38. ErrorCodeEnum CCounterConnectorEntity ::__OnStart(ErrorCodeEnum preOperationError)
  39. {
  40. if (preOperationError != Error_Succeed)
  41. return preOperationError;
  42. //MessageBoxA(0, 0, 0, 0);
  43. //is Pad Version
  44. m_pCurrentSession = NULL;
  45. m_bIsSalesRecord = FALSE;
  46. m_bIsRemoteRecord = FALSE;
  47. m_bIsRemoteRecordBroadCast = FALSE;
  48. CSmartPointer<IEntityFunction> spFunction = GetFunction();
  49. CSystemStaticInfo stStaticinfo;
  50. spFunction->GetSystemStaticInfo(stStaticinfo);
  51. if (stricmp(stStaticinfo.strMachineType,"RVC.PAD")==0)
  52. {
  53. m_bIsPadType = TRUE;
  54. }
  55. else
  56. {
  57. m_bIsPadType = FALSE;
  58. }
  59. m_IsStand2SType = TRUE;
  60. if (stricmp(stStaticinfo.strMachineType,"RVC.Stand2S")!=0){
  61. m_IsStand2SType = FALSE;
  62. }
  63. ErrorCodeEnum Error;
  64. m_pCounterConnectorChannel = new ChannelCounterConnectorClient(this);
  65. Error = m_pCounterConnectorChannel->Connect();
  66. if (Error != Error_Succeed) {
  67. LogError(Severity_Middle, Error_Hardware, EVENT_MOD_CONNECT_ASSIST_ERROR, "connect assistant channel error");
  68. m_pCounterConnectorChannel->SafeDelete();
  69. return Error;
  70. }
  71. {
  72. ChannelService_BeginState_Sub Sub;
  73. Error = m_pCounterConnectorChannel->BeginState(Sub);
  74. if (Error != Error_Succeed)
  75. {
  76. LOG_TRACE("BeginState biz channel failed!");
  77. m_pCounterConnectorChannel->GetFunction()->CloseSession();
  78. m_pCounterConnectorChannel = NULL;
  79. return Error;
  80. }
  81. }
  82. {
  83. ChannelService_BeginRecv_Sub Sub;
  84. Sub.type = ACM_TYPE_DEVICE;
  85. Error = m_pCounterConnectorChannel->BeginRecv(Sub);
  86. if (Error != Error_Succeed)
  87. {
  88. Dbg("Begin BeginRecv ACM_TYPE_DEVICE failed!");
  89. m_pCounterConnectorChannel->GetFunction()->CloseSession();
  90. m_pCounterConnectorChannel = NULL;
  91. return Error;
  92. }
  93. }
  94. {
  95. ChannelService_BeginRecv_Sub Sub;
  96. Sub.type = ACM_TYPE_AGENTVIDEOTYPE;
  97. Error = m_pCounterConnectorChannel->BeginRecv(Sub);
  98. if (Error != Error_Succeed)
  99. {
  100. Dbg("Begin BeginRecv ACM_TYPE_AGENTVIDEOTYPE failed!");
  101. m_pCounterConnectorChannel->GetFunction()->CloseSession();
  102. m_pCounterConnectorChannel = NULL;
  103. return Error;
  104. }
  105. }
  106. {
  107. ChannelService_BeginRecv_Sub Sub;
  108. Sub.type = ACM_TYPE_CALLTRANS;
  109. Error = m_pCounterConnectorChannel->BeginRecv(Sub);
  110. if (Error != Error_Succeed)
  111. {
  112. Dbg("Begin BeginRecv ACM_TYPE_CALLTRANS failed!");
  113. m_pCounterConnectorChannel->GetFunction()->CloseSession();
  114. m_pCounterConnectorChannel = NULL;
  115. return Error;
  116. }
  117. }
  118. m_fsm.Init(this);
  119. int i = 0;
  120. m_arrListener.Init(24);
  121. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, EVENT_MOD_CONNECT_PICKUP_CALL,NULL,false);
  122. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, EVENT_MOD_CONNECT_HANDFREE_CALL,NULL,false);
  123. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, EVENT_MOD_CONNECT_HANDFREE_TO_PICKUP);
  124. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, EVENT_MOD_CONNECT_PICKUP_TO_HANDFREE);
  125. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, EVENT_MOD_CONNECT_HANNUP);
  126. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, EVENT_MOD_CONNECT_HANNUP_BY_CONNECTING);
  127. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, EVENT_MOD_CONNECT_HANNUP_BY_AGENT);
  128. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, EVENT_MOD_CONNECT_GPIO_PICKUP);
  129. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, EVENT_MOD_CONNECT_GPIO_HANDFREE);
  130. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, EVENT_MOD_CONNECT_AGENT_HANDFREE_PICKUP);
  131. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, EVENT_MOD_CONNECT_AGENT_PICKUP_HANDFREE);
  132. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, LOG_EVT_SELFCHECK_ASSISTANTCHANNEL_IDLE);
  133. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, LOG_EVT_SELFCHECK_SIPPHONE_IDLE);
  134. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, LOG_EVT_UI_STARTRECORD,NULL,false);
  135. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, LOG_EVT_UI_STOPRECORD,NULL,false);
  136. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, LOG_EVT_UI_RETURNMENU,NULL,false);
  137. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, LOG_EVT_UI_STARTPHOTOGRAPH,NULL,false);
  138. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, LOG_EVT_DISTRIBUTE_ASSISTANTCHANNEL_IDLE);
  139. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, LOG_EVT_DISTRIBUTE_SIPPHONE_IDLE);
  140. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, EVENT_MOD_CONNECT_STOP_RECORD_BROADCAST,NULL,false);
  141. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, EVENT_MOD_CONNECT_BEGAIN_RECORD_CALL,NULL,false);
  142. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, LOG_EVT_UI_STARTREMOTERECORD,NULL,false);
  143. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, LOG_EVT_UI_STOPREMOTERECORD,NULL,false);
  144. spFunction->SubscribeLog(m_arrListener[i++], this, Log_Event, Severity_None, Error_IgnoreAll, LOG_EVT_UI_STARTRECORDPREVIEW,NULL,false);
  145. return Error_Succeed;
  146. }
  147. ErrorCodeEnum CCounterConnectorEntity ::__OnClose(ErrorCodeEnum preOperationError)
  148. {
  149. if (preOperationError != Error_Succeed)
  150. return preOperationError;
  151. int i;
  152. CSmartPointer<IEntityFunction> spFunction = GetFunction();
  153. for (i = 0; i < m_arrListener.GetCount(); ++i) {
  154. spFunction->UnsubscribeLog(m_arrListener[i]);
  155. }
  156. m_arrListener.Clear();
  157. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_EXIT));
  158. return Error_Succeed;
  159. }
  160. void CCounterConnectorEntity ::OnLog(const CAutoArray<CUUID> &SubIDs, const CUUID nLogID,const LogTypeEnum eLogType, const SeverityLevelEnum eLevel,
  161. const DWORD dwSysError,const DWORD dwUserCode,const DWORD dwEntityInstanceID, const WORD wEntityDevelID,
  162. const CAutoArray<DWORD> &Param, const char *pszEntityName, const char *pszModuleName,const char *pszMessage)
  163. {
  164. LOG_TRACE("user_code = 0x%08x", dwUserCode);
  165. switch (dwUserCode)
  166. {
  167. case EVENT_MOD_CONNECT_GPIO_PICKUP:
  168. {
  169. m_fsm.m_bHandFree = FALSE;
  170. if (!m_fsm.m_bIsAgentControl)
  171. {
  172. m_fsm.m_bAgentHandFree = FALSE;
  173. }
  174. CSimpleStringA strValue;
  175. GetFunction()->GetSysVar("CallState", strValue);
  176. Dbg("摘机,and CallState is %s.", strValue.GetData());
  177. }
  178. break;
  179. case EVENT_MOD_CONNECT_GPIO_HANDFREE:
  180. {
  181. m_fsm.m_bHandFree = TRUE;
  182. if (!m_fsm.m_bIsAgentControl)
  183. {
  184. m_fsm.m_bAgentHandFree = TRUE;
  185. }
  186. CSimpleStringA strValue;
  187. GetFunction()->GetSysVar("CallState", strValue);
  188. Dbg("挂机,and CallState is %s.", strValue.GetData());
  189. }
  190. break;
  191. case EVENT_MOD_CONNECT_PICKUP_CALL: // 提机呼叫
  192. if (!m_bIsRemoteRecordBroadCast){
  193. Dbg("提机呼叫");
  194. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_PICKUP_CALL));
  195. }
  196. else{
  197. Dbg("remote recording broadcast time, ignore mod connect pickup call event, and hand free flag is %s.",m_fsm.m_bHandFree?"true": "false");
  198. }
  199. break;
  200. case EVENT_MOD_CONNECT_HANDFREE_CALL: // 免提呼叫
  201. if (m_fsm.m_bHandFree)
  202. {
  203. LogEvent(Severity_Middle,EVENT_MOD_CONNECT_SLV_HANDFREECALL,"handfree call");
  204. Dbg("界面拨号,免提呼叫");
  205. if (pszMessage && strlen(pszMessage) > 0) {
  206. LogWarn(Severity_Middle, Error_Unexpect, EVENT_MOD_CONNECT_SLV_PICKUPCALL_HAS_CMD, pszMessage);
  207. }
  208. if (pszMessage && strlen(pszMessage) > 0 && isdigit(pszMessage[0]))
  209. {
  210. char tmp[32];
  211. int i = 0;
  212. const char *p = pszMessage;
  213. while (isdigit(*p) && i < 30) {
  214. tmp[i++] = *p++;
  215. }
  216. tmp[i] = 0;
  217. m_fsm.m_strHintCallNum = tmp;
  218. }
  219. else
  220. {
  221. m_fsm.m_strHintCallNum = "";
  222. }
  223. Dbg("recv UI call num = %s",m_fsm.m_strHintCallNum.GetData());
  224. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_HANDFREE_CALL));
  225. }
  226. else
  227. {
  228. LogEvent(Severity_Middle,EVENT_MOD_CONNECT_SLV_PICKUPCALL,"from handfree to pickup call because pickup");
  229. Dbg("界面拨号,话筒呼叫");
  230. if (pszMessage && strlen(pszMessage) > 0 && isdigit(pszMessage[0]))
  231. {
  232. char tmp[32];
  233. int i = 0;
  234. const char *p = pszMessage;
  235. while (isdigit(*p) && i < 30) {
  236. tmp[i++] = *p++;
  237. }
  238. tmp[i] = 0;
  239. m_fsm.m_strHintCallNum = tmp;
  240. }
  241. else
  242. {
  243. m_fsm.m_strHintCallNum = "";
  244. }
  245. Dbg("recv call num = %s",m_fsm.m_strHintCallNum);
  246. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_PICKUP_CALL));
  247. }
  248. break;
  249. case EVENT_MOD_CONNECT_HANDFREE_TO_PICKUP: // 免提->提机
  250. if (!m_bIsRemoteRecordBroadCast){
  251. Dbg("免提->提机");
  252. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_TO_PICKUP));
  253. if (strcmp(m_fsm.GetCurrStateName(),"HandFree")==0){
  254. m_fsm.m_bAgentHandFree = FALSE;
  255. }
  256. SendCurAudioDevice();
  257. }
  258. else{
  259. Dbg("remote recording broadcast time, ignore mod connect hand free to pickup event,and hand free flag is %s.",m_fsm.m_bHandFree?"true": "false");
  260. }
  261. break;
  262. case EVENT_MOD_CONNECT_PICKUP_TO_HANDFREE: // 提机->免提
  263. if (!m_bIsRemoteRecordBroadCast){
  264. Dbg("提机->免提");
  265. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_TO_HANDFREE));
  266. if (strcmp(m_fsm.GetCurrStateName(),"Pickup")==0){
  267. m_fsm.m_bAgentHandFree = TRUE;
  268. }
  269. SendCurAudioDevice();
  270. }
  271. else{
  272. Dbg("remote recording broadcast time, ignore mod connect pickup to hand free event,and hand free flag is %s.",m_fsm.m_bHandFree?"true": "false");
  273. }
  274. break;
  275. case EVENT_MOD_CONNECT_AGENT_HANDFREE_PICKUP: // 坐席控制免提->提机
  276. Dbg("免提->提机");
  277. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_TO_PICKUP));
  278. SendCurAudioDevice();
  279. break;
  280. case EVENT_MOD_CONNECT_AGENT_PICKUP_HANDFREE: // 坐席控制提机->免提
  281. Dbg("提机->免提");
  282. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_TO_HANDFREE));
  283. SendCurAudioDevice();
  284. break;
  285. case EVENT_MOD_CONNECT_BEGAIN_RECORD_CALL:
  286. {
  287. Dbg("开始远程双录呼叫.");
  288. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_DOUBLE_RECORD_CALL));
  289. if (TRUE == m_fsm.m_bHandFree){
  290. LogEvent(Severity_Middle,LOG_EVT_HANDFREE_MODE_REMOTE_CALL,"remote double call start with hand free mode.");
  291. }
  292. else{
  293. LogEvent(Severity_Middle,LOG_EVT_PICKUP_MODE_REMOTE_CALL,"remote double call start with pick up mode.");
  294. }
  295. }
  296. break;
  297. case EVENT_MOD_CONNECT_STOP_RECORD_BROADCAST:
  298. Dbg("双录播报完结束,重置电话状态");
  299. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_STOP_RECORD_BROADCAST));
  300. m_bIsRemoteRecordBroadCast = FALSE;
  301. break;
  302. case EVENT_MOD_CONNECT_HANNUP: // 挂机
  303. {
  304. Dbg("断开呼叫");
  305. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_HANGUP));
  306. m_fsm.m_bHangup=TRUE;
  307. }
  308. break;
  309. case EVENT_MOD_CONNECT_HANNUP_BY_CONNECTING: // 话筒未接通时挂机
  310. Dbg("话筒未接通时断开呼叫");
  311. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_HANGUP));
  312. //m_fsm.m_bHandFree = TRUE;
  313. m_fsm.m_bHangup=TRUE;
  314. break;
  315. case EVENT_MOD_CONNECT_HANNUP_BY_AGENT: // 授权操作
  316. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_AGENT_WRITABLE));
  317. case LOG_EVT_DISTRIBUTE_ASSISTANTCHANNEL_IDLE:
  318. case LOG_EVT_SELFCHECK_ASSISTANTCHANNEL_IDLE: //若assistchannel重启
  319. Dbg("user_code = %08x", dwUserCode);
  320. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_ASSISTCHAN_IDEL));
  321. if(m_pCounterConnectorChannel != NULL)
  322. {
  323. m_pCounterConnectorChannel->GetFunction()->CloseSession();
  324. m_pCounterConnectorChannel = NULL;
  325. Dbg("Close AssistChannel Session ");
  326. }
  327. if(m_pCounterConnectorChannel == NULL)
  328. {
  329. Dbg("ReConnection AssistChannel Session");
  330. ErrorCodeEnum Error;
  331. m_pCounterConnectorChannel = new ChannelCounterConnectorClient(this);
  332. Error = m_pCounterConnectorChannel->Connect();
  333. if (Error != Error_Succeed)
  334. {
  335. m_pCounterConnectorChannel->SafeDelete();
  336. m_pCounterConnectorChannel = NULL;
  337. Dbg("ChannelCounterConnectorClient connect fail!");
  338. }
  339. if (Error == Error_Succeed)
  340. {
  341. ChannelService_BeginState_Sub Sub;
  342. Error = m_pCounterConnectorChannel->BeginState(Sub);
  343. if (Error != Error_Succeed)
  344. {
  345. Dbg("BeginState biz channel failed!");
  346. m_pCounterConnectorChannel->GetFunction()->CloseSession();
  347. m_pCounterConnectorChannel = NULL;
  348. }
  349. }
  350. if (Error == Error_Succeed)
  351. {
  352. ChannelService_BeginRecv_Sub Sub;
  353. Sub.type = ACM_TYPE_DEVICE;
  354. Error = m_pCounterConnectorChannel->BeginRecv(Sub);
  355. if (Error != Error_Succeed)
  356. {
  357. Dbg("Begin BeginRecv ACM_TYPE_DEVICE failed!");
  358. m_pCounterConnectorChannel->GetFunction()->CloseSession();
  359. m_pCounterConnectorChannel = NULL;
  360. }
  361. }
  362. if (Error == Error_Succeed)
  363. {
  364. ChannelService_BeginRecv_Sub Sub;
  365. Sub.type = ACM_TYPE_CALLTRANS;
  366. Error = m_pCounterConnectorChannel->BeginRecv(Sub);
  367. if (Error != Error_Succeed)
  368. {
  369. Dbg("Begin BeginRecv ACM_TYPE_CALLTRANS failed!");
  370. m_pCounterConnectorChannel->GetFunction()->CloseSession();
  371. m_pCounterConnectorChannel = NULL;
  372. }
  373. }
  374. }
  375. break;
  376. case LOG_EVT_SELFCHECK_SIPPHONE_IDLE: //sip话机重启
  377. Dbg("recv LOG_EVT_SELFCHECK_SIPPHONE_IDLE");
  378. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_SIPPHONE_IDEL));
  379. break;
  380. case LOG_EVT_UI_STARTRECORD:
  381. case LOG_EVT_UI_STARTRECORDPREVIEW:
  382. Dbg("start sales record video,get pos = %s",pszMessage);
  383. Handle_StartRecord(pszMessage);
  384. break;
  385. case LOG_EVT_UI_STARTREMOTERECORD:
  386. {
  387. Dbg("start sales remote record video,get pos = %s",pszMessage);
  388. Handle_StartRemoteRecord(pszMessage);
  389. }
  390. break;
  391. case LOG_EVT_UI_STOPRECORD:
  392. Dbg("begin stop sales record video.");
  393. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_STOPLOCALVIDEO));
  394. m_bIsSalesRecord = FALSE;
  395. break;
  396. case LOG_EVT_UI_STOPREMOTERECORD:
  397. Dbg("begin stop double record video,and cur state name is %s.",m_fsm.GetCurrStateName());
  398. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_STOPLOACALREMOTEVIDEO));
  399. m_bIsRemoteRecord = FALSE;
  400. m_bIsRemoteRecordBroadCast = FALSE;
  401. break;
  402. case LOG_EVT_UI_RETURNMENU:
  403. if (m_bIsSalesRecord)
  404. {
  405. Dbg("stop sales record video by return menu");
  406. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_STOPLOCALVIDEO));
  407. m_bIsSalesRecord = FALSE;
  408. }
  409. if (m_bIsRemoteRecord)
  410. {
  411. Dbg("stop remote record video by return menu.");
  412. if (m_fsm.m_CallingParam.nCallType != DOUBLERECORD_CALLTYPE){
  413. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_STOPLOACALREMOTEVIDEO));
  414. }
  415. else{
  416. Dbg("hangup call by return menu, CurrStateName is %s.", m_fsm.GetCurrStateName());
  417. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_HANGUP));
  418. m_fsm.m_bHangup=TRUE;
  419. }
  420. m_bIsRemoteRecord = FALSE;
  421. }
  422. break;
  423. case LOG_EVT_UI_STARTPHOTOGRAPH:
  424. if (stricmp(m_fsm.GetCurrStateName(),"Offline")!=0)
  425. {
  426. LogEvent(Severity_Middle,EVENT_MOD_CONNECT_HANNUP,"UI start photograrh, hangup!");
  427. }
  428. else
  429. {
  430. Dbg("offline state,ignore UI_STARTPHOTOGRAPH hangup event");
  431. }
  432. break;
  433. case LOG_EVT_DISTRIBUTE_SIPPHONE_IDLE: //sip话机状态重置
  434. Dbg("recv LOG_EVT_DISTRIBUTE_SIPPHONE_IDLE");
  435. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_SIPPHONE_IDEL));
  436. break;
  437. default:
  438. break;
  439. }
  440. }
  441. CServerSessionBase*CCounterConnectorEntity::OnNewSession(const char* /*pszRemoteEntityName*/, const char* /*pszClass*/)
  442. {
  443. LOG_FUNCTION();
  444. m_pCurrentSession = new CCounterConnectorSession(this);
  445. return m_pCurrentSession;
  446. }
  447. bool CCounterConnectorEntity ::IsService() const
  448. {
  449. return true;
  450. }
  451. //change audio device message
  452. void CCounterConnectorEntity ::OnReceivePkt(int type, int sub_type, const char *buffer, int size)
  453. {
  454. Dbg("recv pkt type=%d,subtype=%d",type,sub_type);
  455. if ((type == ACM_TYPE_DEVICE)&&!m_bIsPadType)
  456. {
  457. switch (sub_type)
  458. {
  459. //设备切换
  460. //change to handfree
  461. case ACM_CHANGE_HANDFREE:
  462. Dbg("recv change audiodevice to handfree");
  463. if (!m_fsm.m_bAgentHandFree)
  464. {
  465. LogEvent(Severity_Middle,EVENT_MOD_CONNECT_AGENT_PICKUP_HANDFREE,"agent change audio device to handfree");
  466. m_fsm.m_bAgentHandFree = TRUE;
  467. }
  468. else
  469. {
  470. Dbg("agent want change audiodevice to handfree,but the device is handfree now");
  471. }
  472. break;
  473. //change to pickup
  474. case ACM_CHANGE_PICKUP:
  475. Dbg("recv change audiodevice to pickup");
  476. if (m_fsm.m_bAgentHandFree)
  477. {
  478. LogEvent(Severity_Middle,EVENT_MOD_CONNECT_AGENT_HANDFREE_PICKUP,"agent change audio device to pickup");
  479. m_fsm.m_bAgentHandFree = FALSE;
  480. }
  481. else
  482. {
  483. Dbg("agent want change audiodevice to pickup,but the device is pickup now");
  484. }
  485. break;
  486. default:
  487. LOG_TRACE("unknown sub_type %d from agent!", sub_type);
  488. break;
  489. }
  490. }
  491. else if (type == ACM_TYPE_CALLTRANS)
  492. {
  493. Dbg("recv ACM_TYPE_CALLTRANS");
  494. if (sub_type == ACM_CALLTRANS_NUM)
  495. {
  496. //CALL transfer
  497. CallTransferInfo Callnum;
  498. SpBuffer buf;
  499. buf.OpenRead(buffer,size);
  500. Callnum.Serialize(buf);
  501. Dbg("recv ACM_CALLTRANS_NUM = %s",Callnum.CallNum);
  502. //广播,业务中台接收
  503. SpSendBroadcast(GetFunction(), SP_MSG_OF(CallTransferInfo), SP_MSG_SIG_OF(CallTransferInfo), Callnum);
  504. }
  505. }
  506. else if (type == ACM_TYPE_AGENTVIDEOTYPE)
  507. {
  508. if (sub_type == ACM_AGENTVIDEOTYPE_ONEWAY)
  509. {
  510. //广播单向视频
  511. AgentVideoType agentvideotype;
  512. agentvideotype.VideoType = 0;
  513. Dbg("Broadcast Oneway video");
  514. SpSendBroadcast(GetFunction(), SP_MSG_OF(AgentVideoType), SP_MSG_SIG_OF(AgentVideoType), agentvideotype);
  515. }
  516. else if (sub_type == ACM_AGENTVIDEOTYPE_TWOWAY)
  517. {
  518. //广播双向视频
  519. AgentVideoType agentvideotype;
  520. agentvideotype.VideoType = 1;
  521. Dbg("Broadcast Twoway Video");
  522. SpSendBroadcast(GetFunction(), SP_MSG_OF(AgentVideoType), SP_MSG_SIG_OF(AgentVideoType), agentvideotype);
  523. }
  524. }
  525. }
  526. CSimpleStringA CCounterConnectorEntity ::BuildVideoDesc(int local_view_x, int local_view_y, int local_view_cx, int local_view_cy)
  527. {
  528. char param[512];
  529. sprintf(param,
  530. "remote_ip:0\r\n"
  531. "remote_video_rtp:0\r\n"
  532. "remote_video_width:0\r\n"
  533. "remote_video_height:0\r\n"
  534. "remote_video_fps:0\r\n"
  535. "local_view_x:%d\r\n"
  536. "local_view_y:%d\r\n"
  537. "local_view_cx:%d\r\n"
  538. "local_view_cy:%d\r\n"
  539. "remote_view_x:0\r\n"
  540. "remote_view_y:0\r\n"
  541. "remote_view_cx:0\r\n"
  542. "remote_view_cy:0\r\n\r\n",
  543. local_view_x, local_view_y, local_view_cx, local_view_cy);
  544. return CSimpleStringA(param);
  545. }
  546. CSimpleStringA CCounterConnectorEntity ::BuildDoubleVideoDesc(int local_view_x, int local_view_y, int local_view_cx, int local_view_cy, int remote_view_x, int remote_view_y, int remote_view_cx, int remote_view_cy)
  547. {
  548. char param[512];
  549. sprintf(param,
  550. "remote_ip:0\r\n"
  551. "remote_video_rtp:0\r\n"
  552. "remote_video_width:0\r\n"
  553. "remote_video_height:0\r\n"
  554. "remote_video_fps:0\r\n"
  555. "local_view_x:%d\r\n"
  556. "local_view_y:%d\r\n"
  557. "local_view_cx:%d\r\n"
  558. "local_view_cy:%d\r\n"
  559. "remote_view_x:%d\r\n"
  560. "remote_view_y:%d\r\n"
  561. "remote_view_cx:%d\r\n"
  562. "remote_view_cy:%d\r\n\r\n",
  563. local_view_x, local_view_y, local_view_cx, local_view_cy,
  564. remote_view_x, remote_view_y, remote_view_cx, remote_view_cy);
  565. return CSimpleStringA(param);
  566. }
  567. CSimpleStringA CCounterConnectorEntity ::ConstructVideoParam(CSimpleStringA strMsg, bool bDoubleVideo)
  568. {
  569. int lxPos,lyPos,lwidth,lheight,rxPos,ryPos,rwidth,rheight;
  570. CSimpleStringA strVideoParam;
  571. CSimpleStringA str;
  572. if (false == bDoubleVideo){
  573. sscanf(strMsg.GetData(), "%d@%d@%d@%d@%d@%s", &lxPos, &lyPos, &lwidth, &lheight, &str);
  574. Dbg("local video param : (x=%d,y=%d,width=%d,height=%d).",lxPos,lyPos,lwidth,lheight);
  575. strVideoParam = BuildVideoDesc(lxPos,lyPos,lwidth,lheight);
  576. }
  577. else{
  578. int iPostionArr[4][2] = {0};
  579. if (strMsg.GetLength() >0)
  580. {
  581. CAutoArray<CSimpleStringA> arrstr = strMsg.Split('@');
  582. if (arrstr.GetCount() >= 4)
  583. {
  584. for(int i=0; i<4; i++)
  585. {
  586. sscanf(arrstr[i].GetData(), "%d|%d", &iPostionArr[i][0], &iPostionArr[i][1]);
  587. }
  588. }
  589. }
  590. Dbg("remote record local video param : (x=%d,y=%d,width=%d,height=%d), remote video param : (x=%d,y=%d,width=%d,height=%d)",
  591. iPostionArr[0][0], iPostionArr[1][0],iPostionArr[2][0], iPostionArr[3][0],iPostionArr[0][1], iPostionArr[1][1],iPostionArr[2][1], iPostionArr[3][1]);
  592. strVideoParam = BuildDoubleVideoDesc(iPostionArr[0][0], iPostionArr[1][0],iPostionArr[2][0], iPostionArr[3][0],iPostionArr[0][1], iPostionArr[1][1],iPostionArr[2][1], iPostionArr[3][1]);
  593. }
  594. return strVideoParam;
  595. }
  596. void CCounterConnectorEntity::Handle_StartRecord(const char* pszMessage)
  597. {
  598. // edit by ly@2019/04/18
  599. CSimpleStringA strMsg = pszMessage;
  600. if (strMsg.IsStartWith("ews|",true)){
  601. strMsg = strMsg.SubString(4,strMsg.GetLength()-4);
  602. }
  603. CSimpleStringA strVideo;
  604. strVideo = ConstructVideoParam(strMsg, false);
  605. m_fsm.PostEventFIFO(new ShowLocalVideoEvent(strVideo)); // 非连坐席双录
  606. m_bIsSalesRecord = TRUE;
  607. }
  608. void CCounterConnectorEntity::Handle_StartRemoteRecord(const char* pszMessage)
  609. {
  610. // edit by ly@2019/04/18
  611. CSimpleStringA strMsg = pszMessage;
  612. CSimpleStringA strVideo = ConstructVideoParam(strMsg, true);
  613. m_fsm.PostEventFIFO(new ShowLocalAndRemoteVideoEvent(strVideo)); // 连坐席双录
  614. m_bIsRemoteRecord = TRUE;
  615. m_bIsRemoteRecordBroadCast = TRUE;
  616. m_bIsRemoteRecordStopSpeakerCapture = FALSE;
  617. }
  618. void CCounterConnectorEntity::StopRemoteRecordSpeakerAudioCapture()
  619. {
  620. if(DOUBLERECORD_CALLTYPE == m_fsm.m_CallingParam.nCallType){
  621. if (TRUE == m_IsStand2SType){
  622. if (FALSE == m_bIsRemoteRecordStopSpeakerCapture){
  623. if (Error_Succeed == m_fsm.StopSpeakerAudioCapture()){
  624. m_bIsRemoteRecordStopSpeakerCapture = TRUE;
  625. }
  626. }
  627. else{
  628. Dbg("remote record has stop speaker capture.");
  629. }
  630. }
  631. }
  632. }
  633. void CCounterConnectorEntity::Handle_StartRecordPreview(const char* pszMessage)
  634. {
  635. CSimpleStringA strMsg = pszMessage;
  636. if (strMsg.IsStartWith("ews|",true)){
  637. strMsg = strMsg.SubString(4,strMsg.GetLength()-4);
  638. }
  639. CSimpleStringA strVideo;
  640. strVideo = ConstructVideoParam(strMsg, false);
  641. m_fsm.PostEventFIFO(new ShowLocalVideoEvent(strVideo)); // 非连坐席双录
  642. m_bIsSalesRecord = TRUE;
  643. }
  644. //send cur audio device to agent
  645. void CCounterConnectorEntity ::SendCurAudioDevice()
  646. {
  647. if (m_fsm.m_nSysCallType == 0)
  648. {
  649. ChannelService_Send_Info Info;
  650. Info.compress = false;
  651. Info.encrypt = false;
  652. Info.type = ACM_TYPE_DEVICE;
  653. Info.id = 0;
  654. Info.sub_type = ACM_AUDIO_DEVICE;
  655. Info.data.Alloc(sizeof(int));
  656. SpBuffer buf;
  657. buf.OpenWrite();
  658. int nDevice = 0;
  659. if (m_fsm.m_bIsAgentControl)
  660. {
  661. if (m_fsm.m_bAgentHandFree)
  662. {
  663. nDevice = 0;
  664. }
  665. else
  666. {
  667. nDevice = 1;
  668. }
  669. }
  670. else
  671. {
  672. if (m_fsm.m_bHandFree)
  673. {
  674. nDevice = 0;
  675. }
  676. else
  677. {
  678. nDevice = 1;
  679. }
  680. }
  681. buf & nDevice;
  682. Info.data = buf.ToBlob();
  683. m_pCounterConnectorChannel->Send(Info);
  684. Dbg("send cur Audio device = %d",nDevice);
  685. }
  686. else
  687. {
  688. Dbg("cur call type cannot send pkt");
  689. }
  690. }
  691. void CCounterConnectorEntity::SetCallType(CallingTypeEnum eType)
  692. {
  693. m_fsm.SetCallingType(eType);
  694. }
  695. void CCounterConnectorEntity::OnLivenessDetectionStarted(const char *pszEntityName, DWORD dwMessageId, DWORD dwMessageSignature, LivenessDetection::ActiveDetectionStarted &evt)
  696. {
  697. Dbg("LiveDetectionStarted %s", (LPCTSTR)evt.Param);
  698. LogEvent(Severity_Middle,LOG_EVT_STARTLIVEDETECTDISPLAY,"start live detection");
  699. Sleep(400);
  700. int rxPos,ryPos,rwidth,rheight,xPos,yPos,width,height;
  701. CSimpleStringA str;
  702. sscanf((LPCTSTR)evt.Param, "%d@%d@%d@%d@%d@%d@%d@%d", &rxPos, &ryPos, &rwidth, &rheight,&xPos, &yPos, &width, &height);
  703. Dbg("live detection display window x=%d,y=%d,width=%d,height=%d",xPos,yPos,width,height);
  704. CSimpleStringA strVideo = BuildVideoDesc(xPos,yPos,width,height);
  705. m_fsm.PostEventFIFO(new StartVideoDisplayEvent(strVideo));
  706. }
  707. void CCounterConnectorEntity::OnLivenessDetectionStopped(const char *pszEntityName, DWORD dwMessageId, DWORD dwMessageSignature, LivenessDetection::ActiveDetectionStopped &evt)
  708. {
  709. Dbg("LiveDetectionStopped %s", (LPCTSTR)evt.Param);
  710. LogEvent(Severity_Middle,LOG_EVT_STOPLIVEDETECTDISPLAY,"stop live detection");
  711. m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_STOPVIDEODISPLAY));
  712. }
  713. void CCounterConnectorEntity::OnActiveDetectionDone(const char *pszEntityName, DWORD dwMessageId, DWORD dwMessageSignature, LivenessDetection::ActiveDetectionDone &evt)
  714. {
  715. }
  716. void CCounterConnectorEntity::OnAutoSnapshotRemind(const char *pszEntityName, DWORD dwMessageId, DWORD dwMessageSignature, LivenessDetection::AutoSnapshotRemind &evt)
  717. {
  718. }
  719. void CCounterConnectorEntity::OnLivenessDetectionHeartBeat(const char *pszEntityName, DWORD dwMessageId, DWORD dwMessageSignature, LivenessDetection::LivenessDetectionHeartBeat &evt)
  720. {
  721. }
  722. void CCounterConnectorEntity::OnDetectionStopUnExpected(const char *pszEntityName, DWORD dwMessageId, DWORD dwMessageSignature, LivenessDetection::DetectionStopUnExpected &evt)
  723. {
  724. }
  725. ChannelCounterConnectorClient::ChannelCounterConnectorClient( CCounterConnectorEntity *pEntity ) : ChannelService_ClientBase(pEntity)
  726. {
  727. m_eLastState = eChannelState_Idle;
  728. m_uConnectTime = 0;
  729. }
  730. void ChannelCounterConnectorClient::OnMessage(ErrorCodeEnum Error, ChannelService_State_Info &Msg, CSmartPointer<IReleasable> pData)
  731. {
  732. if (Error == Error_Succeed)
  733. {
  734. CCounterConnectorEntity *pEntity = static_cast<CCounterConnectorEntity*>(m_pEntityBase);
  735. if (Msg.state == eChannelState_Idle)
  736. {
  737. pEntity->m_fsm.m_bIsAgentControl = FALSE;
  738. Dbg("ChannelState is eChannelState_Idle");
  739. if (eChannelState_Connected == m_eLastState){
  740. char strmsg[MAX_PATH] = {0};
  741. _snprintf(strmsg, MAX_PATH, "call connected time is %us.", y2k_time_now() - m_uConnectTime);
  742. LogWarn(Severity_Middle, Error_Debug, LOG_WARN_COUNTERCONNECT_CALL_CONNECT_TIME, strmsg);
  743. }
  744. if (eChannelState_Connecting == m_eLastState){
  745. LogWarn(Severity_Middle, Error_Debug, LOG_WARN_COUNTERCONNECT_ASSIST_CONNECT_FAILED,"make call failed for assistant channel connect failed!");
  746. }
  747. }
  748. else if (Msg.state == eChannelState_Connected)
  749. {
  750. pEntity->m_fsm.m_bIsAgentControl = TRUE;
  751. pEntity->SendCurAudioDevice();
  752. Dbg("ChannelState is eChannelState_Connected");
  753. m_uConnectTime = y2k_time_now();
  754. }
  755. else if (eChannelState_Connecting == Msg.state){
  756. Dbg("ChannelState is eChannelState_Connecting");
  757. }
  758. else if(eChannelState_Closing == Msg.state){
  759. Dbg("ChannelState is eChannelState_Closing");
  760. if (eChannelState_Connecting == m_eLastState){
  761. LogWarn(Severity_Middle, Error_Debug, LOG_WARN_COUNTERCONNECT_ASSIST_CONNECT_FAILED,"make call failed for assistant channel closed.");
  762. }
  763. }
  764. m_eLastState = Msg.state;
  765. }
  766. }
  767. void ChannelCounterConnectorClient::OnMessage( ErrorCodeEnum Error, ChannelService_Packet_Info &Msg, CSmartPointer<IReleasable> pData )
  768. {
  769. LOG_FUNCTION();
  770. if (Error == Error_Succeed)
  771. {
  772. CCounterConnectorEntity *pEntity = static_cast<CCounterConnectorEntity*>(m_pEntityBase);
  773. pEntity->OnReceivePkt(Msg.type, Msg.sub_type, (const char*)Msg.data.m_pData, Msg.data.m_iLength);
  774. }
  775. }
  776. void CCounterConnectorSession::Handle_StartCall(SpReqAnsContext<ConnectService_StartCall_Req, ConnectService_StartCall_Ans>::Pointer ctx)
  777. {
  778. LOG_FUNCTION();
  779. ErrorCodeEnum rc = Error_Succeed;
  780. m_pEntity->m_fsm.m_CallingParam.connect_ip = ctx->Req.connect_ip;
  781. m_pEntity->m_fsm.m_CallingParam.connect_port = ctx->Req.connect_port;
  782. m_pEntity->m_fsm.m_CallingParam.nCallType = (CallingTypeEnum)ctx->Req.callingtype;
  783. m_pEntity->m_fsm.m_CallingParam.connect_session = ctx->Req.connect_session;
  784. m_pEntity->m_fsm.m_CallingParam.assistant_port = ctx->Req.assistant_port;
  785. m_pEntity->m_fsm.m_CallingParam.subid = ctx->Req.subid;
  786. m_pEntity->m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_COMMAND_CALL));
  787. ctx->Answer((ErrorCodeEnum)rc);
  788. }
  789. void CCounterConnectorSession::Handle_StartCallExternal(SpReqAnsContext<ConnectService_StartCallExternal_Req, ConnectService_StartCallExternal_Ans>::Pointer ctx)
  790. {
  791. LOG_FUNCTION();
  792. ErrorCodeEnum rc = Error_Succeed;
  793. RVCCallingParam* callparam = (RVCCallingParam*)ctx->Req.CommandParam.m_pData;
  794. if (callparam)
  795. {
  796. m_pEntity->m_fsm.m_CallingParam.connect_ip = callparam->strConnectIp ;
  797. m_pEntity->m_fsm.m_CallingParam.connect_port = callparam->nSipPort;
  798. m_pEntity->m_fsm.m_CallingParam.nCallType = (CallingTypeEnum)callparam->nCallType;
  799. m_pEntity->m_fsm.m_CallingParam.connect_session = callparam->strConnectSession;
  800. m_pEntity->m_fsm.m_CallingParam.subid = callparam->strSubid;
  801. m_pEntity->m_fsm.m_CallingParam.assistant_port = callparam->nAsisstPort;
  802. Dbg("Handle_StartCallExternal strConnectIp=%s, nSipPort=%d, nCallType=%d, strConnectSession=%s, strSubid=%s, nAsisstPort=%d", callparam->strConnectIp,
  803. callparam->nSipPort, callparam->nCallType, callparam->strConnectSession, callparam->strSubid, callparam->nAsisstPort);
  804. }
  805. else
  806. {
  807. Dbg("Get RVCCallingParam Failed!\n");
  808. }
  809. m_pEntity->m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_COMMAND_CALL));
  810. ctx->Answer((ErrorCodeEnum)rc);
  811. }
  812. void CCounterConnectorSession::Handle_StopCall(SpReqAnsContext<ConnectService_StopCall_Req, ConnectService_StopCall_Ans>::Pointer ctx)
  813. {
  814. LOG_FUNCTION();
  815. ErrorCodeEnum rc = Error_Succeed;
  816. RVCCallingParam* callparam = (RVCCallingParam*)ctx->Req.SessionParam.m_pData;
  817. if (callparam){
  818. m_pEntity->m_fsm.m_SessionParam.connect_ip = callparam->strConnectIp ;
  819. m_pEntity->m_fsm.m_SessionParam.connect_session = callparam->strConnectSession;
  820. m_pEntity->m_fsm.m_SessionParam.event_port = callparam->nInstructPort;
  821. Dbg("Handle_StopCall strConnectIp=%s, strConnectSession=%s,event_port=%d", m_pEntity->m_fsm.m_SessionParam.connect_ip.GetData(),
  822. m_pEntity->m_fsm.m_SessionParam.connect_session.GetData(), m_pEntity->m_fsm.m_SessionParam.event_port);
  823. }
  824. else
  825. {
  826. Dbg("Get SessionParam Failed!\n");
  827. }
  828. Dbg("holder hangup call!");
  829. m_pEntity->m_fsm.PostEventFIFO(new FSMEvent(USER_EVT_HANGUP));
  830. ctx->Answer((ErrorCodeEnum)rc);
  831. }
  832. void CCounterConnectorSession::OnClose( ErrorCodeEnum eErrorCode )
  833. {
  834. LOG_FUNCTION();
  835. }
  836. SP_BEGIN_ENTITY_MAP()
  837. SP_ENTITY(CCounterConnectorEntity)
  838. SP_END_ENTITY_MAP()