状态机的状态迁移表写法

阅读: 评论:0

状态机的状态迁移表写法

状态机的状态迁移表写法

 

概述

VOIP服务器中,对于一通呼叫的管理一定会涉及到呼叫状态的变化,包括初始化、呼叫发起、振铃、接通、结束等各种状态。

呼叫业务流程为了管理呼叫状态的变化,就要用到有限状态机这一概念。

最简单的状态机实现,就是if-else或者switch分支方法。

当状态机的状态变化比较简单明了时,条件分支的写法就很好用,但是在条件和状态越来越复杂的情况下,就需要用到状态迁移表的写法,对于整体的状态变迁更容易扩展、维护和理解。

本文根据一个实际的呼叫业务需求,使用状态迁移表实现状态机的业务逻辑。

环境

centos:CentOS  release 7.0 (Final)或以上版本

GCC:4.8.5

需求

原始需求:使用freeswitch的ESL接口实现双呼功能。

双呼功能,就是先对A号码发起呼叫,当A号码answer应答之后,再对B号码发起呼叫,并将A和B俩路呼叫bridge桥接起来,实现A和B的通话功能。

功能列表:

对外提供双呼接口,供第三方调用

内部使用FS的ESL接口,实现双呼业务逻辑

业务逻辑

状态机的基本业务逻辑如图

状态迁移表的逻辑如图

 

代码实现

源代码主要部分。

//呼叫状态typedef enum ESL_CHANNEL_STATE{STATE_INIT = 0,STATE_DIAL_A,STATE_A_INVITING,STATE_A_ANSWER,STATE_WAIT_B_CALL_REQ,STATE_DIAL_B,STATE_B_INVITING,STATE_B_ANSWER,STATE_B_EXECUTE,STATE_BRIDGE,STATE_TALK,STATE_HANGUP,STATE_ERROR,} CHANNEL_STATE;//呼叫事件typedef enum EN_IPC_HEADER_TYPE{ESL_CALL_REQ = 0,           //发起呼叫请求ESL_DIALACALL_REQ = 1,               //发起呼叫ACall请求ESL_DIALBCALL_REQ = 2,               //发起呼叫BCall请求ESL_RECORDCALL_REQ = 3,                  //发起录音请求ESL_BRIDGECALL_REQ = 4,             //发起bridge请求             ESL_CREAT_RESP = 5,         //发起呼叫的响应ESL_ANSWER_RESP = 6,            //应答的事件ESL_EXECUTE_RESP = 7,                    //命令执行响应ESL_BRIDGECALL_RESP = 8,              //发起bridge的响应ESL_HANGUP_RESP = 9,            //挂机的事件ESL_CALL_NOTIFY_INVITE = 10,         //INVITE通知ESL_CALL_NOTIFY_ESTABLISH = 11,   //ESTABLISH通知ESL_HANGUP_REQ = 12,                   //发起挂机请求ESL_HANGUP_NOTIFY = 13,              //HANGUP通知REST_TP_TS_SK_MSG= 14,         // REST TP--->REST TS,传输层发给事务层的socket消息REST_TP_CONN_MSG = 15,        // REST TP新链接REST_TP_DISCONN_MSG = 16,   // REST TP链接中断,发送者transport的epoll delete sk并close skREST_TS_SEND_MSG = 17,          // REST TS发送消息给REST SERVERREST_TS_CLOSE_LINK = 18,      // REST TS主动关闭链接,通知transport的epoll delete sk并close skREST_TS_BUFFER_MSG = 19,     // 通知ts缓冲消息REST_TS_RESEND_MSG = 20,    // 通知tp,该数据为重发的消息SYS_MSG_CHANGE_LOG_LEVEL = 21,      // 外部应用通知CB更改日志级别PROCESS_End = 22,                           // CB进程退出ESL_RECODESTOP_RESP = 23,                  //录音结束响应ESL_CANCEL_CB_REQ = 24,                // 取消回拨ESL_SCHED_HANDUP = 25,                 // 定时结束通话ESL_RAS_HANDUP = 26,                      //隐私自定事件ESL_RAS_HANDUPESL_AS_IVR_HANGUP = 27,          //中原自定义事件ESL_AS_IVR_HANGUPESL_VOICE_NOTICE_REQ = 28,        ESL_VOICE_NOTICE_HANGUP = 29,ESL_RAS_DC_HANGUP = 30,ESL_RAS_RC_HANGUP = 31,ESL_VOICE_VERTIFY_REQ = 32,ESL_VOICE_VERTIFY_HANGUP = 33,FS_HEARTBEAT = 34, //FS 心跳FS_DISCONNECT =35, //FS 断连FS_RECONNECT = 36, //FS 重连ESL_PLAY_VOICE_REQ = 39,  //放音请求ESL_VOICE_PLAYBACK_STOP = 40   //放音结束   } IPC_HEADER_TYPE;//状态转移表定义typedef struct ST_STATE_TRANSFER{CHANNEL_STATE curstate;IPC_HEADER_TYPE  eventtype;CHANNEL_STATE  nextstate;} STATE_TRANSFER;//全局状态转移表STATE_TRANSFER g_stateTransferTable[]={{STATE_INIT,                ESL_CALL_REQ,           STATE_DIAL_A},{STATE_DIAL_A,           ESL_CREAT_RESP,        STATE_A_INVITING},{STATE_A_INVITING,    ESL_ANSWER_RESP,     STATE_A_ANSWER},{STATE_DIAL_B,           ESL_CREAT_RESP,        STATE_B_INVITING},{STATE_B_INVITING,     ESL_BRIDGECALL_RESP,STATE_BRIDGE},{STATE_B_INVITING,     ESL_ANSWER_RESP,     STATE_B_ANSWER},  {STATE_BRIDGE,          ESL_ANSWER_RESP,    STATE_B_ANSWER},{STATE_B_ANSWER,      ESL_BRIDGECALL_RESP,STATE_BRIDGE},{STATE_WAIT_B_CALL_REQ, ESL_DIALBCALL_REQ,  STATE_DIAL_B},{STATE_WAIT_B_CALL_REQ, ESL_HANGUP_RESP,  STATE_HANGUP},   //{STATE_B_INVITING,   ESL_EXECUTE_RESP,     STATE_B_EXECUTE},//{STATE_B_ANSWER,   ESL_EXECUTE_RESP,    STATE_B_EXECUTE},//{STATE_B_EXECUTE,   ESL_ANSWER_RESP,    STATE_B_ANSWER},//{STATE_BRIDGE,        ESL_BRIDGECALL_RESP,STATE_TALK},{STATE_DIAL_A,           ESL_HANGUP_RESP,    STATE_HANGUP},{STATE_A_INVITING,    ESL_HANGUP_RESP,    STATE_HANGUP},{STATE_A_ANSWER,     ESL_HANGUP_RESP,    STATE_HANGUP},{STATE_DIAL_B,           ESL_HANGUP_RESP,    STATE_HANGUP},{STATE_B_INVITING,     ESL_HANGUP_RESP,    STATE_HANGUP},{STATE_B_ANSWER,      ESL_HANGUP_RESP,    STATE_HANGUP},{STATE_B_EXECUTE,      ESL_HANGUP_RESP,    STATE_HANGUP},{STATE_BRIDGE,           ESL_HANGUP_RESP,    STATE_HANGUP},{STATE_TALK,              ESL_HANGUP_RESP,    STATE_HANGUP}};//状态转移回调函数指针定义typedef void (*PFUNToState)(CALL_INFO *pcallinfo);//状态转移回调函数映射表,表中函数指针顺序与CHANNEL_STATE的顺序要保持一致PFUNToState g_pfunToState[]={CEslStateMachine::PFUNToInit,CEslStateMachine::PFUNToDIAL_A,CEslStateMachine::PFUNToA_INVITING,CEslStateMachine::PFUNToA_ANSWER,CEslStateMachine::PFUNToWait_B_CaLL_REQ,CEslStateMachine::PFUNToDIAL_B,CEslStateMachine::PFUNToB_INVITING,CEslStateMachine::PFUNToB_ANSWER,CEslStateMachine::PFUNToB_EXECUTE,CEslStateMachine::PFUNToBRIDGE,CEslStateMachine::PFUNToTALK,CEslStateMachine::PFUNToHANGUP,CEslStateMachine::PFUNToERROR};//状态机事件分发逻辑实现void CEslStateMachine::dispatch(CALL_INFO *pcallinfo, int event){PrintLog(DEBUG, "%s, pcallinfo=%8p", __FUNCTION__, pcallinfo);if(NULL == pcallinfo){return;}int curstate = pcallinfo->curstate;for(unsigned int i = 0; i < sizeof(g_stateTransferTable)/sizeof(STATE_TRANSFER); i++){if(curstate == g_stateTransferTable[i].curstate && event == g_stateTransferTable[i].eventtype){g_pfunToState[g_stateTransferTable[i].nextstate](pcallinfo);break;}}}//INIT状态回调函数实现void CEslStateMachine::PFUNToInit(CALL_INFO *pcallinfo){}//DIAL_A状态回调函数实现void CEslStateMachine::PFUNToDIAL_A(CALL_INFO *pcallinfo){pcallinfo->curstate = STATE_DIAL_A;//send msg to transportif(NULL != m_pStateMachine){m_pStateMachine->SendMsgDailAReq(pcallinfo);}}…

总结

本文针对状态机的迁移表写法进行了介绍,读者可以根据自己的业务需求来选择实现方案。

学习最好的办法还是亲自动手试试,just do it。


空空如常

求真得真

本文发布于:2024-02-02 05:57:52,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170682467341817.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:写法   状态   状态机
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23