用C语言实现分层状态机

时间:2010-08-17 23:07:59

标签: c state-machine hsm

我对如何实现我的状态机感到有点困惑 我已经知道它是等级的,因为一些州分享相同的行动 我通过这些参数确定了我需要做什么:

  • (值为: Base Derived Specific
  • 操作码
  • 参数1 - 可选
  • 参数2 - 可选

我的层次结构由Class确定, OpCode 表示操作 派生可以使用 Base 操作码特定可以使用 OpCodes em> Base 和 Derived 天真的实现如下:

void (*const state_table [MAX_CLASSES][MAX_OPCODES]) (state *) {
  {base_state1, base_state2, NULL, NULL},
  {base_state1, base_state2, derived_state1, NULL},
  {base_state1,base_state2, derived_state1, specific_state3},
};

void dispatch(state *s)
{
  if (state_table[s->Class][s->OpCode] != NULL)
    state_table[s->Class][s->OpCode](s);
}

这将很快变得无法维持 是否有另一种方法将状态映射到超类?

编辑:
进一步的计算让我觉得我可能会使用最多(如果不是全部) OpCodes ,但我不会使用所有可用的 Classes
另一个澄清:
某些操作码可能会通过多个派生和基本共享。
例如:

  • 我有一个名为 Any Class 这是 Base 类。它有 操作码 STATE_ON STATE_OFF STATE_SET
  • 我调用了另一个 Class MyGroup ,这是派生类。它有 OpCodes STATE_FLIP STATE_FLOP

  • 第三个特定 类名为 ThingInMyGroup OpCode : 的 STATE_FLIP_FLOP_AND_FLOOP

因此,从服务器发送一个类任何的消息,在所有客户端中收到并处理。

从服务器发送包含 MyGroup 类的邮件,在所有客户端中收到并仅在属于 MyGroup 的客户端上处理,任何 OpCodes 任何类有效的 MyGroup 类有效。

从服务器发送包含 ThingInMyGroup 类的邮件,在所有客户端中收到并仅在属于 MyGroup 的客户端上处理,并且是 ThingInMyGroup *,对任何类和 MyGroup 类有效的任何** OpCodes ThingInMyGroup 类有效。

收到消息后,客户端将相应地发出ACK / NACK。

我不喜欢使用switch case或const数组,因为当它们变大时它们将变得不可维护 我需要一个灵活的设计,允许我:

  1. 指定哪些操作码可用 对于每个
  2. 为每个 Class 指定一个超类,并通过该规范允许我调用由当前 OpCode 表示的函数指针。

2 个答案:

答案 0 :(得分:4)

有几种方法可以解决这个问题。这是一个:

编辑 - 添加了通用层次结构

typedef unsigned op_code_type;
typedef void (*dispatch_type)(op_code_type);
typedef struct hierarchy_stack hierarchy_stack;
struct hierarchy_stack {
       dispatch_type func;
       hierarchy_stack *tail;
};

void dispatch(state *s, hierarchy_stack *stk) {
    if (!stk) {
          printf("this shouldn't have happened");
    } else {
          stk->func(s, stk->tail);
    }
}

void Base(state *s, hierarchy_stack *stk ) {
    switch (s->OpCode) {
          case bstate1:
               base_state1(s);
               break;
          case bstate2:
               base_state(2);
               break;
          default:
               dispatch(s, stk);
    }
}
void Derived(state *s, hierarchy_stack *stk ) {
    switch(s->opcode) {
           case dstate1:
                deriveds_state1(s);
                break;
           default:
                dispatch(s, stk);
    }
}
... 

注意:所有函数调用都是尾调用。

这样可以很好地本地化你的“类”,这样如果你确定Derived需要100多个方法/操作码,那么你只需要编辑用于定义操作码的方法和枚举(或其他)。

处理这个问题的另一种更动态的方法是在每个“类”中都有一个指向“类”的父指针,它会处理它无法处理的任何东西。

2D表方法快速而灵活(对于操作码0,Derived可能具有与Base不同的处理程序),但它会快速增长。

答案 1 :(得分:1)

我编写了一个小工具,可以生成类似于基于迷你语言的幼稚实现的代码。语言刚刚指定了状态操作码 - 操作关系,所有操作都只是符合typedef的C函数。

它没有处理HSM方面,但这可以相对容易地添加到语言中。

我建议采用这种方法 - 创建一种语言,为您提供一种描述状态机的简洁方法,然后根据该机器描述生成代码。这样,当你需要从现在开始一个月后插入一个新状态时,整个事情并不是一个混乱的编辑。

如果您需要代码,请告诉我,我会确保它仍然可以在某个地方使用。