状态机的状态迁移表写法

状态机的状态迁移表写法概述 VOIP 服务器中 对于一通呼叫的管理一定会涉及到呼叫状态的变化 包括初始化 呼叫发起 振铃 接通 结束等各种状态 呼叫业务流程为了管理呼叫状态的变化 就要用到有限状态机这一概念 最简单的状态机实现 就是 if else 或者 switch 分支

大家好,欢迎来到IT知识分享网。

状态机的状态迁移表写法

概述

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 sk REST_TS_SEND_MSG = 17, // REST TS发送消息给REST SERVER REST_TS_CLOSE_LINK = 18, // REST TS主动关闭链接,通知transport的epoll delete sk并close sk REST_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_HANDUP ESL_AS_IVR_HANGUP = 27, //中原自定义事件ESL_AS_IVR_HANGUP ESL_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 transport if(NULL != m_pStateMachine) { m_pStateMachine->SendMsgDailAReq(pcallinfo); } } …

总结

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

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


空空如常

求真得真

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/178931.html

(0)
上一篇 2025-05-18 09:10
下一篇 2025-05-18 09:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信