最近在看《OMNET++ User Mannul》,了解到了有限状态机的工作机制。相比早之前看过的《OPNET xxx》教材中讲解FSM不明不白,《OMENT++ User Mannul》讲解的更为清晰简洁,且有偏底层的代码做实例,更易理解。
FSM是指完成模块功能的一种手法(例如OMNET++中的模块功能可选择是否由FSM完成),下文的FSM均指模块中的FSM。
构成FSM的主体为状态(State)和状态跳转(State Transition),其中状态分为两种:1)稳定状态;2)临时状态。每个状态拥有进入(Enter)和离开(Exit)操作,各对应一段代码,也就是OPNET中状态圆圈的上半圆和下半圆。介绍FSM之前,先给一个结论,在设计FSM时,稳定状态必须使用,临时状态可用亦可不用。
1)稳定状态:在进入稳定状态时,执行稳定状态的Enter代码。接着此FSM就中断运行(向调度器交出代码执行权),直到下次新的Event抵达这个模块,执行此稳定状态的Exit代码,执行完毕后跳转到其他状态(当然也可以跳回自个)。
2)临时状态:执行Enter代码,接着立即执行Exit代码,跳转到其他状态(也能跳回自个)。
可以看到,临时状态仅仅是代码的执行,并不受Event的影响;而稳定状态则受Event影响。考虑到事件驱动仿真器的工作流程大致是:调度器中取出Event --> 将消息传递给Event所属的模块 --> 执行模块的消息处理函数 --> 执行完毕后,将执行权重归调度器。
根据工作流程,每个模块实现的关键且必须之处在于:1)当调度到相关Event时,才会进入模块执行;2)模块必须有执行退出点,将执行权交回调度器。由于仅稳定状态可使FSM运作停止,交出执行权,因此FSM中必须包含稳定状态,且FSM停止(退出模块)一定发生在稳定状态的Enter之后;FSM唤醒(模块被调度)必然发生在稳定状态的Exit之前(Init状态除外)。
在事件驱动仿真器中,Event可是整个仿真运行被调度时的主体,仿真的执行(具体执行哪个模块的代码)和仿真的时间完全是由Event控制的。缺乏Event直接影响的临时状态,作用仅仅是作为一段功能代码而已,存在的意义在于简化稳定状态和编写FSM更灵活。由于临时状态的存在,可带来如下好处:
1)可将稳定状态中的部分功能移至临时状态中;
2)将重复功能交给临时状态实现,减少稳定状态中代码的重复度;
3)增加了状态数量,使得状态跳转的设计更为灵活,且跳转条件的设计更为简单。
状态跳转就容易理解了,是指状态执行完Exit代码后,必须做一次状态跳转,直到跳转到一个稳定状态(Enter代码执行完后,FSM就会停止运行,交出运行权)。
了解了上述原理后,如何规避FSM陷进就不会纯抓瞎。我所了解的陷进包括:1)无穷跳转循环;2)FSM唤醒后,当前稳定状态的Exit代码不能处理Event中的msg。如果踩到这两个雷,那就想象一下FSM的运作过程,进行排雷。
在设计FSM时,OPNET有界面,OMNET++为纯代码编写。界面使得OPNET中状态跳转显示非常直观,当状态或跳转较多时非常受用,而OMNET++中就得拿出笔在纸上画。此外,OPNET的开发流程决定了模块开发必须使用FSM;而OMNET++则并不是必须。难道是两者所谓的philosophy不一样?