Chinaunix首页 | 论坛 | 博客
  • 博客访问: 145883
  • 博文数量: 124
  • 博客积分: 70
  • 博客等级: 民兵
  • 技术积分: 1745
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-24 13:49
文章分类

全部博文(124)

文章存档

2011年(55)

2010年(14)

2009年(30)

2008年(25)

我的朋友

分类: WINDOWS

2009-07-29 17:02:29

 
今天看到boost有个库叫做statechart. 不禁兴趣较大,学习了一下,受益匪浅.
 
大体来说,这个库处理了大部分状态机uml中涉及到的点.
 
1. 简单状态处理
 
  
 
如上图,我们暂时认为acive是一个简单状态而不是一个复合状态. 那么按照状态机来说,有几个元素呢?
 
1) 初始状态
2) 转换事件/action
3) 中间状态
4) 结束状态 (暂无)
 
 
那么boost::statechart 重现过后,有如下对应对象
1) state_machine
2) simple_state / state
3) event
4) transition / reaction
 
我们来看看大致的代码对应:
 
1. 定义一个状态机对象
 
 struct Machine : sc::state_machine
{
 Machine()
 {
  std::cout << __FUNCTION__ << std::endl;
 }
 ~Machine()
 {
  std::cout << __FUNCTION__ << std::endl;
 }
};
 
2. 定义初始状态对象
struct StatActive : sc::simple_state
{
 typedef sc::transition reactions;
 StatActive()
 {
  m_lElapsed = 0;
  std::cout << __FUNCTION__ << std::endl;
 }
 ~StatActive()
 {
  std::cout << __FUNCTION__ << std::endl;
 }
 };
 
3. 定义转化,注意第二步中已经贴出转换步骤代码,即typedef 部分
 
 
4. 启动状态机
 
 Machine machine;
 machine.initiate();
 machine.process_event(EvReset());
 machine.terminate();
 
 
就这样一个简单的状态机以及其相关的逻辑均已经体现.
 
 
 
下面我们来看一个复杂点的状态机,一个数码相机的例子;
 


  先来分析一下这个状态机有哪些元素:
 
1. 有3个状态
  1) notshooting (复合)
  2) shooting (复合)
  3) storing (基本)
 
 所谓基本和复合大致的区别是符合状态内部还有小的状态机.
 
2. 有如下几种动作转换
 
  1) HalfPress 
     a) 自动对焦模式
     b) 手动对焦模式
 
  2) FullPress
     a) 手动对焦模式
 
  3) Release
 
 
下面再来看复合状态
 
 
notshooting 内部分为:
1. idle状态
2. 配置状态
 
有一个转换事件:
1. config
 
 
 
shooting分为:
 
1. focusing
2. focused
3. storing
 
事件:
1. infocus
2. fullpressed  with condition
 
 
具体代码就不贴了,可以参见  d:\boost\test\state_machine
 
 
特别说明:
  看到了focusing这个状态了吧, 一般来说这个状态是需要持续一段时间的,  所以如果这段时间内用户按下了fullpress,如果按照上面的状态机处理,则此状态将会被丢弃; 这儿如果我们做一个改进,即将此状态记录下来,然后当用户再次对焦准确后自动触发,这就是boost::derreral的作用.
 
typedef mpl::list<
  sc::transition< EvInfocus, StatFocused >,
//  sc::transition< EvFullPress , StatFocusing>
  sc::deferral
 > reactions;
 
但是据我测试,如果多个fullpress的话,只会有一个生效。
 
 
一个小技法:
大部分情况下如果
typedef sc::transition reactions;
那么往往当状态发生转移时候,目标状态的对象会被构造;即通过构造函数来呼叫;
但是也可通过
typedef sc::transition< EvInFocus, Focused,
    Shooting, &Shooting::DisplayFocused > reactions;
这种方式,来指定对target的呼叫,甚至可以看到这个target的处理函数还不是focused对象。
 
从跟踪来看,此函数的call 遭遇focused 状态的构造。
 
 
事件通知机制
目前看到的transit 方式实际上优点类似于一种同步方式;
 
但是实际上还可以通过另外一种方式处理,即状态切换推迟到当前的reaction结束;
 
比如:
StatFocused::~StatFocused()
{
  post_event( EvPumpingFinished() );
}
那么post_event干了些啥呢?
它将一个事件放入主队列,状态机将在当前reaction处理结束后立刻处理此事件;
事件可以在reaction , enter entry , exit entry , transaction reaction 中被触发,但是在
内部的入口和出口点时候有一点复杂。
 
struct Pumping : sc::state< Pumping, Purifier >
{
  Pumping( my_context ctx ) : my_base( ctx )
  {
    post_event( EvPumpingStarted() );
  }
  // ...
};
即需要发送的状态,必需派生自state , 且实现入口点的构造,如上.
 
只要一个入口点的动作需要和外部世界解除,比如主队列,那么就需要如此,类似的函数有:
  • simple_state<>::post_event()
  • simple_state<>::clear_shallow_history<>()
  • simple_state<>::clear_deep_history<>()
  • simple_state<>::outermost_context()
  • simple_state<>::context<>()
  • simple_state<>::state_cast<>()
  • simple_state<>::state_downcast<>()
  • simple_state<>::state_begin()
  • simple_state<>::state_end()
 
但是幸运的是一般我们总是在react或者transaction中处理,因此一般来说不需要考虑。
 
这儿来举个例子,比如focusing 状态到 focused 状态,实际上后一个状态由前一个状态触发,而且从实际操作来看,是很有可能在前者的entry action中完成了对焦,然后触发此事件, 因此这个可能就是一个内部动作。
 
我之前的例子是,类似如下方式测试的:
std::cout << "testing focusing fullpressed" << std::endl;
 machine.MemAvaiable() = true;
 machine.AutoFocus() = true;
 machine.process_event(EvHalfPress());
 machine.process_event(EvInfocus());
 machine.process_event(EvFullPress());
 machine.process_event(EvRelease()); 
 
即是主动发送EvInfocus ,这个就是说外部出发了focusing 后又触发infocus,实在点说,这个可能性不如内部处理来得大。
 
看看输出:
 
 
再来看修改过后的代码:
 
      std::cout << "testing focusing fullpressed" << std::endl;
 machine.MemAvaiable() = true;
 machine.AutoFocus() = true;
 machine.process_event(EvHalfPress());
 //machine.process_event(EvInfocus());
 machine.process_event(EvFullPress());
 machine.process_event(EvRelease()); 
 
struct StatFocusing : sc::state
{
 typedef sc::transition< EvInfocus, StatFocused,
    StatShooting, &StatShooting::DisplayFocuesed > reactions;
/*
 typedef mpl::list<
  sc::transition< EvInfocus, StatFocused >,
//  sc::transition< EvFullPress , StatFocusing>
  sc::deferral
 > reactions;
*/
 StatFocusing( my_context ctx ) : my_base( ctx )
 {
  std::cout << "enter focusing" << std::endl;
  post_event(EvInfocus());
 }
 ~StatFocusing()
 {
  std::cout << "leave focusing" << std::endl;
 }
};
 
从输出来看是一致的。
 
 
不过过程是不一致的,后者是在machine.process_event(EvHalfPress())之后
 machine.process_event(EvFullPress())之前得到处理的.
 
 
历史记录的处理:
由于idle状态是个复合状态,即其有两个状态,idle 和 config;那么也就意味着用户是可以在这两个状态的任何一个,按动 half-press , full-press 进行操作后,将返回到idle状态;但是有些情况下,用户也许希望回到前一个状态,比如曾经是在配置界面进入拍摄的。
 
 
 
请观察图中shooting 到notshooting 直接的H*,这这个标示状态切换前需要回到历史状态,*标示状态为0或者多个。
 
std::cout << "testing history" << std::endl;
 machine.process_event(EvConfig());
 machine.process_event(EvHalfPress());
 machine.process_event(EvFullPress());
 machine.process_event(EvRelease()); 
 
 
从上图可以看到,从拍照结束后进入到了config状态,即事发前的状态.
 
到了这儿,其实还有一个问题,即deep_history , 但是boost还提供了一个shallow_history,
从字面来看;另外从boost的表述,deep_history实际上是保存了一个内部状态的hierachy,然后返回时候返回到最内层状态,我们继续一下测试;修改config的状态图如下:
 
 
 
上图我们将config的状态从简单状态修改为一个复合状态,即其内部含有一个历史图片配置,以及单张图片配置两个内部状态。
 
 
 
从上图看到,在返回历史记录时候层次关系式ok的。
 
 
接下来看看shallow 和deep的差别;

 std::cout << "testing history" << std::endl;
 machine.process_event(EvConfig());
 machine.process_event(EvSingleConfig());
 machine.process_event(EvHalfPress());
 machine.process_event(EvFullPress());
 machine.process_event(EvRelease()); 
 
如果设定为has_shallow_hisotry shallow_hisotory 那么输出很上面相似;
如果启用has_deep_hisotory deep_history 那么输出如下:
 
 
 
你会发现,deep的效果能够追述到内部状态的切换;
 
另外还存在一个has_full_history deep_history,boost的文档说其相当于 shallow & deep.
更加的具体的我从测试中未发现差异。或许唯一的差别就是,当外界有时候会以 deep_history 有时候会以shallow_history 定义历史callback时候,has_full_history 就会有随之发生变化;
 
 
为了更加清晰的展示一个实用的例子, 再来看看修改后的uml图:
 
 
 
 
即开机后进入notshooting状态,但是notshooting状态也带有上次关机前的历史记录.
特别说明,由于visio的uml图无法画出inside inner state的图,因此H+只能挑个合适的地方画,比如shooting到idle的转换间的H+,被移动到了这儿,严格来说这个H+确实应该在状态之内,即notshooting之内.
 
std::cout << "testing idle hisotry" << std::endl;
 machine.process_event(EvPowerOn());
 machine.process_event(EvConfig());
 machine.process_event(EvSingleConfig());
 machine.process_event(EvPowerOff());
 machine.process_event(EvPowerOn());
 
 
 
 
还有一些比较正交的状态图:
 
 
或者异常处理时候的的处理,这儿不再列举,有兴趣参考
 
 
阅读(2261) | 评论(0) | 转发(0) |
0

上一篇:.net 迭代

下一篇:pcre stack overflow

给主人留下些什么吧!~~