4

If I make a state machine and want to use an interface like this:

AddState ( state1, state2, Key_UP );
AddEvent ( Key_UP );
AddEventFunction ( Key_UP, &UP_Function);
AddStateFunction ( state1, &State1_In_Function, &State1_Out_Function);
AddStateFunction ( state2, &State2_In_Function, &State2_Out_Function);

State1_In_Function  ( void ) { printf ( "In #1 \n" ); }
State1_Out_Function ( void ) { printf ( "Out #1 \n" ); }
State2_In_Function  ( void ) { printf ( "In #2 \n" ); }
State2_Out_Function ( void ) { printf ( "Out #2 \n" ); }
UP_Function         ( void ) { printf ( "Goin UP \n" ); }

That way when I am in state1 and the FSM receives Key_UP the program prints:

Out #1
Goin UP
In #2

The question is how to store the state and transitional information inside the class without the programmer being required to change an array size. I was thinking I could use a 2D array and make it a state table like usual and to make it more portable I would just handle the addition of events and states by using the vector type to resize as needed. The problem with vectors is that not many embedded devices can use the memory allocation calls. My second option is to call a constructor with the state machine and pass it the size the table would require to be but then if I add any new states or events I need to change these values as well...

So how should I store my states, events and function pointers?!

MSalters
  • 173,980
  • 10
  • 155
  • 350
uMinded
  • 595
  • 1
  • 9
  • 21
  • Based off the example below by Matthieu I have completed a working system that does everything I wanted it to! If your interested take a look at the complete program [HERE](http://pastebin.com/RtPne3dC) simply do a "g++ file.cpp" and it compiles without a single error! – uMinded Jul 24 '11 at 01:20

3 Answers3

3

You could simply store them on the stack, though it's a bit more difficult :)

Still, it's an amusing solution, so here you go. The underlying principle is to play with decorators and mutability. Example of code:

State state1, state2; // creates a state
Event KEY_UP;
Event KEY_DOWN;

Transition t0(state1, KEY_UP, state2);
Transition t1(state2, KEY_DOWN, state1);

How does it work ?

Instead of state1 being a "simple" object, it will be slightly more convoluted. Something like:

struct State;

struct StateImpl {
  StateImpl(char const* n): name(n) {}
  char const* name;
};

struct StateNode {
  StateNode(Event e, State const& s, StateNode const* n):
    event(e), state(s), next(n) {}

  Event event;
  State const& destination;
  StateNode const* next;
};

struct State {
  State(char const* name):
    me(0), impl(name) {}

  StateNode const* me;
  StateImpl impl;
};

And then we define a Transition:

struct Transition {
  Transition(State& origin, Event e, State const& destination):
    node(e, destination, origin.me)
  {
    origin.me = node;
  }
  StateNode node;
};

Informally, we are building a singly-linked list, with the head sitting in State. We update the head each time we add a transition.

Upon an event occurrence, one needs to walk this list until either the event is encountered, and thus suitably dispatched, or the null pointer is reached, indicating that the event shall not have been received in this state.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Hmm.. This is very interesting indeed. By doing things this way its easy to add pointers to in/out functions to any of the structures! – uMinded Jul 22 '11 at 17:58
  • I added some debug into to follow the program logic, you can see the full program [HERE](http://pastebin.com/uKxLAReT) – uMinded Jul 22 '11 at 18:00
  • So I was walking through your code and adding some routines to walk through the table when I discovered I don't understand a few things with your example. I get recursive inclusion whenever I create a state because of StateNode. state1.me->destination.me->destination.me->.... etc The transition structure also does not make since to me as the origin.me passes the StateNode and not a State like I would imagine was whats wanted. On top of that the state in general when grated makes me NULL by default for every state. Do you mind giving a little walkthrough on your code please? – uMinded Jul 23 '11 at 05:24
  • Oh wow I was going to bed an just realized that a linked list is like a FIFO... Its not recursivly calling itself, the last structure member is the address of the next. Wow I dropped the ball there. – uMinded Jul 23 '11 at 07:19
  • @uMinded: well, I am glad you find it useful :) More seriously though, especially on the embedded world, I would recommend using a code generator to get the minimal footprint possible. Linked-List do have some overhead in comparison with, say, arrays. Code generation would allow more compact (if unreadable by a human) machinery... though it would only work if the machine can be specified entirely at compile-time. – Matthieu M. Jul 23 '11 at 09:39
  • I currently use an open source FSM generator that was abandoned 8 years ago as it creates minimal C code and is readable. It makes everything in if/else and switch statements combined with __inline and the C is long but the ASM is quite compact. I want my own C++ FSM as I move into the 100MHz+ chips as theirs room for the extra footprint and the volume of code requires easy readability. I am reading a linked list tutorial to see how to use your code, now that I realize I completely missed the point lol I will hopefully get it working and post it on pastebin if you want to check it out. Thx! – uMinded Jul 23 '11 at 18:13
  • @uMinded: I am going on vacation, so don't worry about keeping me posted, I'll be swamped up with emails/updates when I get back anyway ;) – Matthieu M. Jul 23 '11 at 19:28
  • Thanks for all the help, I learned quite a bit about linked lists. I had never used them before but their exceedingly handy. I updated my original question with my new code so hopefully someone else will benefit from it. Have a great vacation! – uMinded Jul 24 '11 at 01:22
0

I suggest you have a look at Boost.StateChart and, if needed, build a thin layer around it with the API you want.

That should solve your need for designing adequate data structures.

Benoît
  • 16,798
  • 8
  • 46
  • 66
  • 2
    I am not sure that Boost.StateChart is adapted for embedded devices. – Matthieu M. Jul 22 '11 at 06:31
  • 1
    the OP makes explicit reference to the lack of availability of heap-allocated memory on the platforms he care for. It means that he is using a subset of C++, not the full standard. I personally don't know enough about Boost.StateChart intrisics to know if it would work or not... thus the remark. Do you know about them ? – Matthieu M. Jul 22 '11 at 07:42
  • I must say i don't. But it might be worth at try anyway. – Benoît Jul 22 '11 at 08:19
  • Its not impossible to get the Boost libraries on an embedded device but they add a huge footprint. I have tried to add them on a 73MHz ARM and it pushed my code size over the flash space. I am trying to write my own Embedded.StateChart implementation – uMinded Jul 22 '11 at 17:01
0

You can get cute and do it this way, by the way.

spraff
  • 32,570
  • 22
  • 121
  • 229
  • Unfortunately that a bit too simplistic. Most of my state machines have around 30 states and 5 events. – uMinded Jul 22 '11 at 17:02