5

我对如何实现我的状态机有点困惑。
我已经知道它是分层的,因为某些州共享相同的操作。
我通过这些参数确定我需要做什么:

  • (值是:BaseDerivedSpecific
  • 操作码
  • 参数 1 - 可选
  • 参数 2 - 可选

我的层次结构由 Class 确定,OpCode代表操作。
Derived可以使用BaseOpCodesSpecific可以使用BaseDerivedOpCodes。 天真的实现如下:

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可能通过多个派生类和基共享。
例如:

  • 我有一个名为Any的 ,它是一个类。它有 OpCodesSTATE_ONSTATE_OFFSTATE_SET
  • 我有另一个名为 MyGroup的,它是一个派生类。它有OpCodesSTATE_FLIPSTATE_FLOP

  • 第三个是一个 名为ThingInMyGroup的特定类,它具有OpCodeSTATE_FLIP_FLOP_AND_FLOOP

因此,从服务器发送 一条Any类的消息,在所有客户端中接收并处理。

从服务器发送一条MyGroup类的消息,在所有客户端中接收并仅在属于MyGroup的客户端上处理,任何对Any类有效的操作码对MyGroup有效。

从服务器发送具有ThingInMyGroup类的消息,在所有客户端中接收并仅在属于MyGroup并且是ThingInMyGroup* 的客户端上处理,任何Any类和MyGroup类有效的 **OpCodes 对ThingInMyGroup类有效.

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

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

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

2 回答 2

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 表方法既快速又灵活(Derived 可能具有与操作码 0 的 Base 不同的处理程序),但它的增长速度很快。

于 2010-08-18T00:40:07.043 回答
1

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

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

我建议采用这种方法——创建一种小语言,为您提供一种清晰的方式来描述状态机,然后根据该机器描述生成代码。这样,当您需要在一个月后插入一个新状态时,整个事情就不会是一团乱麻。

如果您想要代码,请告诉我,我会确保它在某处仍然可用。

于 2010-08-17T23:36:37.800 回答