How to use multiple IR signals for doing the same thing? - c++

I want to use different signals from different IR remotes to control a wheeled robot.
The robot is the Smart robot car kit v1.0 from Elegoo.
I used the infrared_remote_control_car.ino file from the disc with it.
I just added the #define JVC and the operators at the end.
The code looks like this:
#include <IRremote.h>
int receiverpin = 12;
int in1=9;
int in2=8;
int in3=7;
int in4=6;
int ENA=10;
int ENB=5;
int ABS=130;
unsigned long RED;
#define A 16736925
#define B 16754775
#define X 16712445
#define C 16720605
#define D 16761405
#define JVCfront 49816
#define JVCback 49688
#define JVCright 49704
#define JVCleft 49832
#define JVCmenu 49900
#define JVC3ok 49724
#define JVCstop 49856
IRrecv irrecv(receiverpin);
decode_results results;
void _mForward()
{
digitalWrite(ENA,HIGH);
digitalWrite(ENB,HIGH);
digitalWrite(in1,LOW);
digitalWrite(in2,HIGH);
digitalWrite(in3,LOW);
digitalWrite(in4,HIGH);
Serial.println("go forward!");
}
void _mBack()
{
digitalWrite(ENA,HIGH);
digitalWrite(ENB,HIGH);
digitalWrite(in1,HIGH);
digitalWrite(in2,LOW);
digitalWrite(in3,HIGH);
digitalWrite(in4,LOW);
Serial.println("go back!");
}
void _mleft()
{
analogWrite(ENA,ABS);
analogWrite(ENB,ABS);
digitalWrite(in1,LOW);
digitalWrite(in2,HIGH);
digitalWrite(in3,HIGH);
digitalWrite(in4,LOW);
Serial.println("go left!");
}
void _mright()
{
analogWrite(ENA,ABS);
analogWrite(ENB,ABS);
digitalWrite(in1,HIGH);
digitalWrite(in2,LOW);
digitalWrite(in3,LOW);
digitalWrite(in4,HIGH);
Serial.println("go right!");
}
void _mStop()
{
digitalWrite(ENA,LOW);
digitalWrite(ENB,LOW);
Serial.println("STOP!");
}
void setup() {
pinMode(in1,OUTPUT);
pinMode(in2,OUTPUT);
pinMode(in3,OUTPUT);
pinMode(in4,OUTPUT);
pinMode(ENA,OUTPUT);
pinMode(ENB,OUTPUT);
pinMode(receiverpin,INPUT);
Serial.begin(9600);
_mStop();
irrecv.enableIRIn();
}
void loop() {
if (irrecv.decode(&results))
{
RED=results.value;
Serial.println(RED);
irrecv.resume();
delay(150);
if(RED==(A || JVCfront))
{
_mForward();
}
else if(RED==(B or JVCback))
{
_mBack();
}
else if(RED==(C or JVCleft))
{
_mleft();
}
else if(RED==(D or JVCright))
{
_mright();
}
else if(RED==(X or JVCstop or JVCmenu or JVC3ok))
{
_mStop();
}
}
}
I tried different ways I saw on the internet for the OR operator as you can see.
Actually, the robot is always going forward.

Does the Serial.println(RED) always print out the expected value?
Your if-elseblock itself does not seem to be problematic at first glance. It doesn't matter if you use || or or. They are equivalent.
The problem is the way you are checking the value:
Therefore that you have parenthesis around your OR statement, you create a bool-value. if(RED==(A || JVCfront) translates to:
is A set or is JVCfront set; meaning, are they != 0 (yes they both are, so this expression is true
is RED == true (no it is not, because true represents 1 in integer)
so the code in that block is not being executed
If you want to solve it with an if-else, you need to:
if(RED == A or RED == JVCfront)
{
_mForward();
}
Anyway, I would suggest a switch-case statement:
switch(RED):
{
case A:
{
//intentional fallthrough
}
case JVCfront:
{
_mForward();
break;
}
case B:
{
//intentional fallthrough
}
case JVCback:
{
_mBack();
break;
}
case C:
{
//intentional fallthrough
}
case JVCleft:
{
_mleft();
break;
}
case D:
{
//intentional fallthrough
}
case JVCright:
{
_mright();
break;
}
case X:
{
//intentional fallthrough
}
case JVCstop:
{
//intentional fallthrough
}
case JVCmenu:
{
//intentional fallthrough
}
case JVC3ok:
{
_mStop();
break;
}
default:
{
_mStop();
}
}
I personally find this kind of code much easier to read, and more understandable.
Please note: If you dont put a break into a case, it automatically executes the next case as well. This is why I wrote //intentional fallthrough in this part of the code. P.e. when case A is executed, nothing happens. It just falls through into case JVCFront and executes everything there.

Related

Why does my switch/case default when using enums?

I have the following switch/case statement in Arduino 1.8.7 where the variable led is an integer:
switch (led) {
case ALL: {
/* do stuff */
break;
}
case LED1: {
/* do stuff */
break;
}
case LED2: {
/* do stuff */
break;
}
case LED3: {
/* do stuff */
break;
}
case LED4: {
/* do stuff */
break;
}
default: {
break;
}
}
I also have the following enum:
enum LED_References_e
{
ALL = 0,
LED1 = 1,
LED2 = 2,
LED3 = 3,
LED4 = 4
};
When using the enumerated values as cases to the statement, the statement always hits the default clause. If I substitute the enumerated values for the integers that they represent (i.e.case 0: ... case 1: ...) then the statement functions as expected.
I have tried, when using the enumerated values within the statement, to reference the enumerator as the value that the switch is performed on:
switch ((LED_References_e)led)
But this also defaults every time.
I am using another enumerator within my program and this functions correctly, however it is conditionally tested using if/else as opposed to switch/case.
My question is twofold:
Why does the switch/case statement seemingly not work with enumerated values?
What fundamental difference am I missing between if/else and switch/case?
Assuming Max Langhof is correct and there are other names ALL, LED1, etc... in scope at the switch so that the LED_References_e ones are shadowed, this should help:
I'm not 100% certain about the differences between standard C++ and Arduino C++, but you should be able to do the following:
enum LED_References_e
{
ALL = 0,
LED1 = 1,
LED2 = 2,
LED3 = 3,
LED4 = 4
};
switch (led) {
case LED_References_e::ALL: {
/* do stuff */
break;
}
case LED_References_e::LED1: {
/* do stuff */
break;
}
case LED_References_e::LED2: {
/* do stuff */
break;
}
case LED_References_e::LED3: {
/* do stuff */
break;
}
case LED_References_e::LED4: {
/* do stuff */
break;
}
default: {
break;
}
}
What this does is it tells the compiler you explicitly want LED1...LED4 from the LED_References_e enum.
If there are other LEDxes in the same scope, this should disambiguate.

Switch statement instead of multiple nested if - else?

I've come across a situation where I have a bunch of "systems" that need to be initialized in sequence, with the next system only being initialized if all of the proceeding systems initialized successfully.
This has led me to a whole slew of nested if - else statements. Here's some pseudo-code for visualization.
bool mainInit () {
if (!system1Init ()) {
reportError (); // some error reporting function
}
else {
if (!system2Init ()) {
reportError ();
}
else {
if (!system3Init ()) {
// ... and so on
I find that this starts to look like a mess when you get even a handful of levels to it.
Now I thought of using a switch statement instead, starting at the first case and falling through to the other cases on success, only breaking if there's an error.
bool mainInit () {
switch (1) {
case 1:
if (!system1Init ()) {
reportError ();
break;
}
case 2:
if (!system2Init ())
reportError ();
break;
}
// ....
}
Now, I like this a lot better. I find it much easier to read, especially with some decent comments, but I'm fairly new to programming.
So, my question is: Seeing how this is not how switch statements are traditionally used(at least from what I've seen), is something like this acceptable, or would this be considered bad form?
Being new to programming, I'm trying not to develop too many bad habits that might frustrate and make things more difficult for other programmers down the road.
I did a search, but most of what I found had to do with replacing chains of if - else if statements, not replacing nested ones.
Reference all of the systems in an array, for example an std::vector<mySystem*>, and loop over them sequentially, breaking off on the first fail. This way your entire code is reduced to less than 5 lines of code, even for 500+ systems.
The suggested switch hack is an evil example of XY problem solving: your real problem is that you don't have the array of systems, and are using named variables, thus eliminating all options to more flexibly use all systems, like in a loop.
Assuming that all your system#Init() calls are known at compile time, you can very easily put them in a table and then iterate over that table.
typedef (*system_init)(void);
system_init initialization_functions[] =
{
system1Init,
system2Init,
system3Init,
...
systemNInit
};
bool mainInit()
{
for(size_t idx(0); idx < sizeof(initialization_functions) / sizeof(initialization_functions[0]); ++idx)
{
if(!initialization_functions[idx]())
{
ReportError();
return false;
}
}
return true;
}
However, your existing code looks incorrect since the first mainInit() only calls system1Init() and then exits. Probably not what you wanted in the first place.
if(!system1Init())
{
ReportError();
return false;
}
// if you add an else, the system2Init() does not get called
// even if system1Init() succeeds
if(!system2Init())
{
ReportError();
return false;
}
[...]
return true;
Would the switch answer your problem? Not as it was written. That is, if you wanted to call the mainInit() function with a counter, it could be useful. Drupal uses that mechanism:
bool mainInit(int idx)
{
bool r(true);
switch(idx)
{
case 1:
r = system1Init();
break;
case 2:
r = system2Init();
break;
[...]
}
if(!r)
{
ReportError();
}
return r
}
Note that the table mechanism works the same way as the switch. As long as all the code is found in the systemNInit() functions (and it should be), the switch does not add anything, so you could do something like this too:
bool mainInit(int idx)
{
if(idx < 0 || idx >= sizeof(initialization_functions) / sizeof(initialization_functions[0]))
{
throw std::range_error("index out of bounds");
}
if(!initialization_functions[idx]())
{
ReportError();
return false;
}
return true;
}
Calling the mainInit() with an index can be helpful in case you want to "de-initialize" properly:
int main()
{
for(size_t idx(0); idx < ...; ++idx)
{
if(!mainInit(idx))
{
while(idx > 0)
{
--idx;
mainDeinit(idx);
}
exit(1);
}
}
...app do something here...
}
Use custom exceptions with clear error messages and add a try-catch-report-die around the code in main(). Exceptions are there to specifically make your case look good by making "bad path" implicit.
void initX() { ...; throw std::invalid_argument_exception("..."); }
int main() {
try {
init1(); init2(); ... run();
return 0;
} catch (std::exception const& e) {
log(e.what()); exit 42;
}
}
I'd do it this way:
bool mainInit () {
if (!system1Init ()) {
return(false);
}
if (!system2Init ()) {
return(false);
}
if (!system3Init ()) {
return(false);
}
//...
return(true);
}
//...
if(!mainInit()) {
reportError();
}

Incoming packets version handling - design issues

Consider packet coming from somewhere. It has field VERSION, there are N possible VERSIONS of incoming packet.
Every packet with VERSION X has to be processed by proper methods/set of instructions for every packet VERSION from X to 1. My only idea to accomplish this task is very ugly like:
PACKET p = GetPacketFromSomewhere();
// p.VERSION is 3
if (p.VERSION > 0) {
// things for p.VERSION == 1
}
if (p.VERSION > 1) {
// things for p.VERSION == 2
}
if (p.VERSION > 2) {
// things for p.VERSION == 3
}
// set of if statements up to version N
The real situation is that, I have packet VERSIONS above number 10 and things still are likely to change. New packet VERSIONS will be added while I need to keep backward compatibility. This code is bad, at least I don't like it. Do you guys have any better idea how to handle this case?
If the order of processing is not important, you can use switch without break:
PACKET p = GetPacketFromSomewhere();
// p.VERSION is 3
switch (p.VERSION) {
case 3: {
// things for p.VERSION == 3
}
case 2: {
// things for p.VERSION == 2
}
case 1: {
// things for p.VERSION == 1
}
}
EDIT:
You could also use recursive function template specialization like this:
template<int N>
void proc(PACKET& p){
proc<N-1>(p);
}
template<>
void proc<1>(PACKET& p){
//things for p.VERSION == 1
}
template<>
void proc<2>(PACKET& p){
proc<1>(p)
//things for p.VERSION == 2
}
template<>
void proc<3>(PACKET& p){
proc<2>(p)
//things for p.VERSION == 3
}
and then call the processing function like this:
switch (p.VERSION ) {
case 1: proc<1>(p) break;
case 3: proc<2>(p) break;
case 3: proc<3>(p) break;
default: {
std::cout << "Protocol version not impplemented - using highest known version" << std::endl;
proc<3>(p);
}
This should also be pretty efficient performance wise (in case this is a concern of yours) and you don't have to worry about gaps in the protocol version.
Use a map of type map<unsigned int, function> where function is a pointer to a function of the desired type:
typedef void (*function)();
std::map<int,function> handlers;
handlers[1] = &ver1handler;
...
handlers[N] = &verNhandler;
if(handlers.count(p.VERSION))
(*handlers.find(p.VERSION))();
where ver[N+1]handler would be defined as:
void ver[N+1]handler(){
ver[N]handler();
// additional handle commands
}
Or use the switch command, with the handlers defined in the same way.

branching based on two boolean variables

Suppose I have two boolean variables, and I want to do completely different things based on their values. What is the cleanest way to achieve this?
Variant 1:
if (a && b)
{
// ...
}
else if (a && !b)
{
// ...
}
else if (!a && b)
{
// ...
}
else
{
// ...
}
Variant 2:
if (a)
{
if (b)
{
// ...
}
else
{
// ...
}
}
else
{
if (b)
{
// ...
}
else
{
// ...
}
}
Variant 3:
switch (a << 1 | b)
{
case 0:
// ...
break;
case 1:
// ...
break;
case 2:
// ...
break;
case 3:
// ...
break;
}
Variant 4:
lut[a][b]();
void (*lut[2][2])() = {false_false, false_true, true_false, true_true};
void false_false()
{
// ...
}
void false_true()
{
// ...
}
void true_false()
{
// ...
}
void true_true()
{
// ...
}
Are variants 3 and 4 too tricky/complicated for the average programmer? Any other variants I have missed?
The first variant is the clearest and most readable, but it can be adjusted:
if (a && b) {
// ...
} else if (a) { // no need to test !b here - b==true would be the first case
// ...
} else if (b) { //no need to test !a here - that would be the first case
// ...
} else { // !a&&!b - the last remaining
// ...
}
You forgot about:
if (a) a_true(b);
else a_false(b);
which is probably the best choice when appliable, and when you truly need 4 different behaviours.
If you have more than 2 bools, I take this as a code smell if I have 2^n different behaviours which don't factorize well like the above. Then I may think about doing:
enum { case1, case2, ... }
int dispatch_cases(bool a, bool b, bool c, ..., bool z);
switch (dispatch_cases(a, b, ..., z))
{
case case1:
...
};
but without context, it is hard to tell whether such complexity is necessary.
IMHO, I will go for variant 3. Because personally, I don't like if/else when I am checking for equality. It clearly states that there are only 4 possibilities.
One minor edit would be:
inline int STATES(int X, int Y) { return (X<<1) | Y; }
// ...
switch (STATES(a,b))
To make it more fancy, you may replace 0,1,2,3 with an enum as well.
enum States {
NONE,
ONLY_B.
ONLY_A,
BOTH
};
For just two booleans, any of them is good and reasonable. One can choose based on his taste.
However, if there are more than two booleans, say four booleans, then I personally would go with lookup table, and I would do this as:
typedef void (*functype)();
//16 functions to handle 16 cases!
void f0() {}
void f1() {}
//...so on
void f15() {}
//setup lookup table
functype lut[] =
{
f0, //0000 - means all bool are false
f1, //0001
f2, //0010
f3, //0011
f4, //0100
f5, //0101
f6, //0110
f7, //0111
f8, //1000
f9, //1001
f10, //1010
f11, //1011
f12, //1100
f13, //1101
f14, //1110
f15 //1111 - means all bool are true
};
lut[MakeInt(b1,b2,b3,b4)](); //call
MakeInt() is easy to write:
int MakeInt(bool b1, bool b2, bool b3, bool b4)
{
return b1 | (b2<<1) | (b3 <<2) | (b4<<3);
}

C state-machine design [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I am crafting a small project in mixed C and C++. I am building one small-ish state-machine at the heart of one of my worker thread.
I was wondering if you gurus on SO would share your state-machine design techniques.
NOTE: I am primarily after tried & tested implementation techniques.
UPDATED: Based on all the great input gathered on SO, I've settled on this architecture:
State machines that I've designed before (C, not C++) have all come down to a struct array and a loop. The structure basically consists of a state and event (for look-up) and a function that returns the new state, something like:
typedef struct {
int st;
int ev;
int (*fn)(void);
} tTransition;
Then you define your states and events with simple defines (the ANY ones are special markers, see below):
#define ST_ANY -1
#define ST_INIT 0
#define ST_ERROR 1
#define ST_TERM 2
: :
#define EV_ANY -1
#define EV_KEYPRESS 5000
#define EV_MOUSEMOVE 5001
Then you define all the functions that are called by the transitions:
static int GotKey (void) { ... };
static int FsmError (void) { ... };
All these function are written to take no variables and return the new state for the state machine. In this example global variables are used for passing any information into the state functions where necessary.
Using globals isn't as bad as it sounds since the FSM is usually locked up inside a single compilation unit and all variables are static to that unit (which is why I used quotes around "global" above - they're more shared within the FSM, than truly global). As with all globals, it requires care.
The transitions array then defines all possible transitions and the functions that get called for those transitions (including the catch-all last one):
tTransition trans[] = {
{ ST_INIT, EV_KEYPRESS, &GotKey},
: :
{ ST_ANY, EV_ANY, &FsmError}
};
#define TRANS_COUNT (sizeof(trans)/sizeof(*trans))
What that means is: if you're in the ST_INIT state and you receive the EV_KEYPRESS event, make a call to GotKey.
The workings of the FSM then become a relatively simple loop:
state = ST_INIT;
while (state != ST_TERM) {
event = GetNextEvent();
for (i = 0; i < TRANS_COUNT; i++) {
if ((state == trans[i].st) || (ST_ANY == trans[i].st)) {
if ((event == trans[i].ev) || (EV_ANY == trans[i].ev)) {
state = (trans[i].fn)();
break;
}
}
}
}
As alluded to above, note the use of ST_ANY as wild-cards, allowing an event to call a function no matter the current state. EV_ANY also works similarly, allowing any event at a specific state to call a function.
It can also guarantee that, if you reach the end of the transitions array, you get an error stating your FSM hasn't been built correctly (by using the ST_ANY/EV_ANY combination.
I've used code similar for this on a great many communications projects, such as an early implementation of communications stacks and protocols for embedded systems. The big advantage was its simplicity and relative ease in changing the transitions array.
I've no doubt there will be higher-level abstractions which may be more suitable nowadays but I suspect they'll all boil down to this same sort of structure.
And, as ldog states in a comment, you can avoid the globals altogether by passing a structure pointer to all functions (and using that in the event loop). This will allow multiple state machines to run side-by-side without interference.
Just create a structure type which holds the machine-specific data (state at a bare minimum) and use that instead of the globals.
The reason I've rarely done that is simply because most of the state machines I've written have been singleton types (one-off, at-process-start, configuration file reading for example), not needing to run more than one instance. But it has value if you need to run more than one.
The other answers are good, but a very "lightweight" implementation I've used when the state machine is very simple looks like:
enum state { ST_NEW, ST_OPEN, ST_SHIFT, ST_END };
enum state current_state = ST_NEW;
while (current_state != ST_END)
{
input = get_input();
switch (current_state)
{
case ST_NEW:
/* Do something with input and set current_state */
break;
case ST_OPEN:
/* Do something different and set current_state */
break;
/* ... etc ... */
}
}
I would use this when the state machine is simple enough that the function pointer & state transition table approach is overkill. This is often useful for character-by-character or word-by-word parsing.
Pardon me for breaking every rule in computer science, but a state machine is one of the few (I can count only two off hand) places where a goto statement is not only more efficient, but also makes your code cleaner and easier to read. Because goto statements are based on labels, you can name your states instead of having to keep track of a mess of numbers or use an enum. It also makes for much cleaner code since you don't need all the extra cruft of function pointers or huge switch statements and while loops. Did I mention it's more efficient too?
Here's what a state machine might look like:
void state_machine() {
first_state:
// Do some stuff here
switch(some_var) {
case 0:
goto first_state;
case 1:
goto second_state;
default:
return;
}
second_state:
// Do some stuff here
switch(some_var) {
case 0:
goto first_state;
case 1:
goto second_state;
default:
return;
}
}
You get the general idea. The point is that you can implement the state machine in an efficient way and one that is relatively easy to read and screams at the reader that they are looking at a state machine. Note that if you are using goto statements, you must still be careful as it is very easy to shoot yourself in the foot while doing so.
You might consider the State Machine Compiler http://smc.sourceforge.net/
This splendid open source utility accepts a description of a state machine in a simple language and compiles it to any one of a dozen or so languages - including C and C++. The utility itself is written in Java, and can be included as part of a build.
The reason to do this, rather than hand coding using GoF State pattern or any other approach, is that once your state machine is expressed as code, the underlying structure tends to disappear under the weight of boilerplate that needs to be generated to support it. Using this approach gives you an excellent separation of concerns, and you keep the structure of your state machine 'visible'. The auto-generated code goes into modules that you don't need to touch, so that you can go back and fiddle with the state machine's structure without impacting the supporting code that you have written.
Sorry, I am being over-enthusiastic, and doubtless putting everyone off. But it is a top notch utility, and well-documented too.
Be sure to check the work of Miro Samek (blog State Space, website State Machines & Tools), whose articles at the C/C++ Users Journal were great.
The website contains a complete (C/C++) implementation in both open source and commercial license of a state machine framework (QP Framework), an event handler (QEP), a basic modeling tool (QM) and a tracing tool (QSpy) which allow to draw state machines, create code and debug them.
The book contains an extensive explanation on the what/why of the implementation and how to use it and is also great material to gain understanding of the fundamentals of hierachical and finite state machines.
The website also contains links to several board support packages for use of the software with embedded platforms.
I've done something similar to what paxdiablo describes, only instead of an array of state/event transitions, I set up a 2-dimensional array of function pointers, with the event value as the index of one axis and the current state value as the other. Then I just call state = state_table[event][state](params) and the right thing happens. Cells representing invalid state/event combinations get a pointer to a function that says so, of course.
Obviously, this only works if the state and event values are both contiguous ranges and start at 0 or close enough.
A very nice template-based C++ state machine "framework" is given by Stefan Heinzmann in his article.
Since there's no link to a complete code download in the article, I've taken the liberty to paste the code into a project and check it out. The stuff below is tested and includes the few minor but pretty much obvious missing pieces.
The major innovation here is that the compiler is generating very efficient code. Empty entry/exit actions have no cost. Non-empty entry/exit actions are inlined. The compiler is also verifying the completeness of the statechart. Missing actions generate linking errors. The only thing that is not caught is the missing Top::init.
This is a very nice alternative to Miro Samek's implementation, if you can live without what's missing -- this is far from a complete UML Statechart implementation, although it correctly implements the UML semantics, whereas Samek's code by design doesn't handle exit/transition/entry actions in correct order.
If this code works for what you need to do, and you have a decent C++ compiler for your system, it will probably perform better than Miro's C/C++ implementation. The compiler generates a flattened, O(1) transition state machine implementation for you. If the audit of assembly output confirms that the optimizations work as desired, you get close to theoretical performance. Best part: it's relatively tiny, easy to understand code.
#ifndef HSM_HPP
#define HSM_HPP
// This code is from:
// Yet Another Hierarchical State Machine
// by Stefan Heinzmann
// Overload issue 64 december 2004
// http://accu.org/index.php/journals/252
/* This is a basic implementation of UML Statecharts.
* The key observation is that the machine can only
* be in a leaf state at any given time. The composite
* states are only traversed, never final.
* Only the leaf states are ever instantiated. The composite
* states are only mechanisms used to generate code. They are
* never instantiated.
*/
// Helpers
// A gadget from Herb Sutter's GotW #71 -- depends on SFINAE
template<class D, class B>
class IsDerivedFrom {
class Yes { char a[1]; };
class No { char a[10]; };
static Yes Test(B*); // undefined
static No Test(...); // undefined
public:
enum { Res = sizeof(Test(static_cast<D*>(0))) == sizeof(Yes) ? 1 : 0 };
};
template<bool> class Bool {};
// Top State, Composite State and Leaf State
template <typename H>
struct TopState {
typedef H Host;
typedef void Base;
virtual void handler(Host&) const = 0;
virtual unsigned getId() const = 0;
};
template <typename H, unsigned id, typename B>
struct CompState;
template <typename H, unsigned id, typename B = CompState<H, 0, TopState<H> > >
struct CompState : B {
typedef B Base;
typedef CompState<H, id, Base> This;
template <typename X> void handle(H& h, const X& x) const { Base::handle(h, x); }
static void init(H&); // no implementation
static void entry(H&) {}
static void exit(H&) {}
};
template <typename H>
struct CompState<H, 0, TopState<H> > : TopState<H> {
typedef TopState<H> Base;
typedef CompState<H, 0, Base> This;
template <typename X> void handle(H&, const X&) const {}
static void init(H&); // no implementation
static void entry(H&) {}
static void exit(H&) {}
};
template <typename H, unsigned id, typename B = CompState<H, 0, TopState<H> > >
struct LeafState : B {
typedef H Host;
typedef B Base;
typedef LeafState<H, id, Base> This;
template <typename X> void handle(H& h, const X& x) const { Base::handle(h, x); }
virtual void handler(H& h) const { handle(h, *this); }
virtual unsigned getId() const { return id; }
static void init(H& h) { h.next(obj); } // don't specialize this
static void entry(H&) {}
static void exit(H&) {}
static const LeafState obj; // only the leaf states have instances
};
template <typename H, unsigned id, typename B>
const LeafState<H, id, B> LeafState<H, id, B>::obj;
// Transition Object
template <typename C, typename S, typename T>
// Current, Source, Target
struct Tran {
typedef typename C::Host Host;
typedef typename C::Base CurrentBase;
typedef typename S::Base SourceBase;
typedef typename T::Base TargetBase;
enum { // work out when to terminate template recursion
eTB_CB = IsDerivedFrom<TargetBase, CurrentBase>::Res,
eS_CB = IsDerivedFrom<S, CurrentBase>::Res,
eS_C = IsDerivedFrom<S, C>::Res,
eC_S = IsDerivedFrom<C, S>::Res,
exitStop = eTB_CB && eS_C,
entryStop = eS_C || eS_CB && !eC_S
};
// We use overloading to stop recursion.
// The more natural template specialization
// method would require to specialize the inner
// template without specializing the outer one,
// which is forbidden.
static void exitActions(Host&, Bool<true>) {}
static void exitActions(Host&h, Bool<false>) {
C::exit(h);
Tran<CurrentBase, S, T>::exitActions(h, Bool<exitStop>());
}
static void entryActions(Host&, Bool<true>) {}
static void entryActions(Host& h, Bool<false>) {
Tran<CurrentBase, S, T>::entryActions(h, Bool<entryStop>());
C::entry(h);
}
Tran(Host & h) : host_(h) {
exitActions(host_, Bool<false>());
}
~Tran() {
Tran<T, S, T>::entryActions(host_, Bool<false>());
T::init(host_);
}
Host& host_;
};
// Initializer for Compound States
template <typename T>
struct Init {
typedef typename T::Host Host;
Init(Host& h) : host_(h) {}
~Init() {
T::entry(host_);
T::init(host_);
}
Host& host_;
};
#endif // HSM_HPP
Test code follows.
#include <cstdio>
#include "hsm.hpp"
#include "hsmtest.hpp"
/* Implements the following state machine from Miro Samek's
* Practical Statecharts in C/C++
*
* |-init-----------------------------------------------------|
* | s0 |
* |----------------------------------------------------------|
* | |
* | |-init-----------| |-------------------------| |
* | | s1 |---c--->| s2 | |
* | |----------------|<--c----|-------------------------| |
* | | | | | |
* |<-d-| |-init-------| | | |-init----------------| | |
* | | | s11 |<----f----| | s21 | | |
* | /--| |------------| | | |---------------------| | |
* | a | | | | | | | | |
* | \->| | |------g--------->|-init------| | | |
* | | |____________| | | |-b->| s211 |---g--->|
* | |----b---^ |------f------->| | | | |
* | |________________| | |<-d-|___________|<--e----|
* | | |_____________________| | |
* | |_________________________| |
* |__________________________________________________________|
*/
class TestHSM;
typedef CompState<TestHSM,0> Top;
typedef CompState<TestHSM,1,Top> S0;
typedef CompState<TestHSM,2,S0> S1;
typedef LeafState<TestHSM,3,S1> S11;
typedef CompState<TestHSM,4,S0> S2;
typedef CompState<TestHSM,5,S2> S21;
typedef LeafState<TestHSM,6,S21> S211;
enum Signal { A_SIG, B_SIG, C_SIG, D_SIG, E_SIG, F_SIG, G_SIG, H_SIG };
class TestHSM {
public:
TestHSM() { Top::init(*this); }
~TestHSM() {}
void next(const TopState<TestHSM>& state) {
state_ = &state;
}
Signal getSig() const { return sig_; }
void dispatch(Signal sig) {
sig_ = sig;
state_->handler(*this);
}
void foo(int i) {
foo_ = i;
}
int foo() const {
return foo_;
}
private:
const TopState<TestHSM>* state_;
Signal sig_;
int foo_;
};
bool testDispatch(char c) {
static TestHSM test;
if (c<'a' || 'h'<c) {
return false;
}
printf("Signal<-%c", c);
test.dispatch((Signal)(c-'a'));
printf("\n");
return true;
}
int main(int, char**) {
testDispatch('a');
testDispatch('e');
testDispatch('e');
testDispatch('a');
testDispatch('h');
testDispatch('h');
return 0;
}
#define HSMHANDLER(State) \
template<> template<typename X> inline void State::handle(TestHSM& h, const X& x) const
HSMHANDLER(S0) {
switch (h.getSig()) {
case E_SIG: { Tran<X, This, S211> t(h);
printf("s0-E;");
return; }
default:
break;
}
return Base::handle(h, x);
}
HSMHANDLER(S1) {
switch (h.getSig()) {
case A_SIG: { Tran<X, This, S1> t(h);
printf("s1-A;"); return; }
case B_SIG: { Tran<X, This, S11> t(h);
printf("s1-B;"); return; }
case C_SIG: { Tran<X, This, S2> t(h);
printf("s1-C;"); return; }
case D_SIG: { Tran<X, This, S0> t(h);
printf("s1-D;"); return; }
case F_SIG: { Tran<X, This, S211> t(h);
printf("s1-F;"); return; }
default: break;
}
return Base::handle(h, x);
}
HSMHANDLER(S11) {
switch (h.getSig()) {
case G_SIG: { Tran<X, This, S211> t(h);
printf("s11-G;"); return; }
case H_SIG: if (h.foo()) {
printf("s11-H");
h.foo(0); return;
} break;
default: break;
}
return Base::handle(h, x);
}
HSMHANDLER(S2) {
switch (h.getSig()) {
case C_SIG: { Tran<X, This, S1> t(h);
printf("s2-C"); return; }
case F_SIG: { Tran<X, This, S11> t(h);
printf("s2-F"); return; }
default: break;
}
return Base::handle(h, x);
}
HSMHANDLER(S21) {
switch (h.getSig()) {
case B_SIG: { Tran<X, This, S211> t(h);
printf("s21-B;"); return; }
case H_SIG: if (!h.foo()) {
Tran<X, This, S21> t(h);
printf("s21-H;"); h.foo(1);
return;
} break;
default: break;
}
return Base::handle(h, x);
}
HSMHANDLER(S211) {
switch (h.getSig()) {
case D_SIG: { Tran<X, This, S21> t(h);
printf("s211-D;"); return; }
case G_SIG: { Tran<X, This, S0> t(h);
printf("s211-G;"); return; }
}
return Base::handle(h, x);
}
#define HSMENTRY(State) \
template<> inline void State::entry(TestHSM&) { \
printf(#State "-ENTRY;"); \
}
HSMENTRY(S0)
HSMENTRY(S1)
HSMENTRY(S11)
HSMENTRY(S2)
HSMENTRY(S21)
HSMENTRY(S211)
#define HSMEXIT(State) \
template<> inline void State::exit(TestHSM&) { \
printf(#State "-EXIT;"); \
}
HSMEXIT(S0)
HSMEXIT(S1)
HSMEXIT(S11)
HSMEXIT(S2)
HSMEXIT(S21)
HSMEXIT(S211)
#define HSMINIT(State, InitState) \
template<> inline void State::init(TestHSM& h) { \
Init<InitState> i(h); \
printf(#State "-INIT;"); \
}
HSMINIT(Top, S0)
HSMINIT(S0, S1)
HSMINIT(S1, S11)
HSMINIT(S2, S21)
HSMINIT(S21, S211)
The technique I like for state machines (at least ones for program control) is to use function pointers. Each state is represented by a different function. The function takes an input symbol and returns the function pointer for the next state. The central dispatch loop monitors takes the next input, feeds it to the current state, and processes the result.
The typing on it gets a little odd, since C doesn't have a way to indicate types of function pointers returning themselves, so the state functions return void*. But you can do something like this:
typedef void* (*state_handler)(input_symbol_t);
void dispatch_fsm()
{
state_handler current = initial_handler;
/* Let's assume returning null indicates end-of-machine */
while (current) {
current = current(get_input);
}
}
Then your individual state functions can switch on their input to process and return the appropriate value.
Simplest case
enum event_type { ET_THIS, ET_THAT };
union event_parm { uint8_t this; uint16_t that; }
static void handle_event(enum event_type event, union event_parm parm)
{
static enum { THIS, THAT } state;
switch (state)
{
case THIS:
switch (event)
{
case ET_THIS:
// Handle event.
break;
default:
// Unhandled events in this state.
break;
}
break;
case THAT:
// Handle state.
break;
}
}
Points:
State is private, not only to the compilation unit but also to the event_handler.
Special cases may be handled separately from the main switch using whatever construct deemed necessary.
More complex case
When the switch gets bigger than a couple of screens full, split it into functions that handle each state, using a state table to look up the function directly. The state is still private to the event handler. The state handler functions return the next state. If needed some events can still receive special treatment in the main event handler. I like to throw in pseudo-events for state entry and exit and perhaps state machine start:
enum state_type { THIS, THAT, FOO, NA };
enum event_type { ET_START, ET_ENTER, ET_EXIT, ET_THIS, ET_THAT, ET_WHATEVER, ET_TIMEOUT };
union event_parm { uint8_t this; uint16_t that; };
static void handle_event(enum event_type event, union event_parm parm)
{
static enum state_type state;
static void (* const state_handler[])(enum event_type event, union event_parm parm) = { handle_this, handle_that };
enum state_type next_state = state_handler[state](event, parm);
if (NA != next_state && state != next_state)
{
(void)state_handler[state](ET_EXIT, 0);
state = next_state;
(void)state_handler[state](ET_ENTER, 0);
}
}
I am not sure if I nailed the syntax, especially regarding the array of function pointers. I have not run any of this through a compiler. Upon review, I noticed that I forgot to explicitly discard the next state when handling the pseudo events (the (void) parenthesis before the call to state_handler()). This is something that I like to do even if compilers accept the omission silently. It tells readers of the code that "yes, I did indeed mean to call the function without using the return value", and it may stop static analysis tools from warning about it. It may be idiosyncratic because I do not recall having seen anybody else doing this.
Points: adding a tiny bit of complexity (checking if the next state is different from the current), can avoid duplicated code elsewhere, because the state handler functions can enjoy the pseudo events that occur when a state is entered and left. Remember that state cannot change when handling the pseudo events, because the result of the state handler is discarded after these events. You may of course choose to modify the behaviour.
A state handler would look like so:
static enum state_type handle_this(enum event_type event, union event_parm parm)
{
enum state_type next_state = NA;
switch (event)
{
case ET_ENTER:
// Start a timer to do whatever.
// Do other stuff necessary when entering this state.
break;
case ET_WHATEVER:
// Switch state.
next_state = THAT;
break;
case ET_TIMEOUT:
// Switch state.
next_state = FOO;
break;
case ET_EXIT:
// Stop the timer.
// Generally clean up this state.
break;
}
return next_state;
}
More complexity
When the compilation unit becomes too large (whatever you feel that is, I should say around 1000 lines), put each state handler in a separate file. When each state handler becomes longer than a couple of screens, split each event out in a separate function, similar to the way that the state switch was split. You may do this in a number of ways, separately from the state or by using a common table, or combining various schemes. Some of them have been covered here by others. Sort your tables and use binary search if speed is a requirement.
Generic programming
I should like the preprocessor to deal with issues such as sorting tables or even generating state machines from descriptions, allowing you to "write programs about programs". I believe this is what the Boost people are exploiting C++ templates for, but I find the syntax cryptic.
Two-dimensional tables
I have used state/event tables in the past but I have to say that for the simplest cases I do not find them necessary and I prefer the clarity and readability of the switch statement even if it does extend past one screen full. For more complex cases the tables quickly get out of hand as others have noted. The idioms I present here allow you to add a slew of events and states when you feel like it, without having to maintain a memory consuming table (even if it may be program memory).
Disclaimer
Special needs may render these idioms less useful, but I have found them to be very clear and maintainable.
Saw this somewhere
#define FSM
#define STATE(x) s_##x :
#define NEXTSTATE(x) goto s_##x
FSM {
STATE(x) {
...
NEXTSTATE(y);
}
STATE(y) {
...
if (x == 0)
NEXTSTATE(y);
else
NEXTSTATE(x);
}
}
Extremely untested, but fun to code, now in a more refined version than my original answer; up-to-date versions can be found at mercurial.intuxication.org:
sm.h
#ifndef SM_ARGS
#error "SM_ARGS undefined: " \
"use '#define SM_ARGS (void)' to get an empty argument list"
#endif
#ifndef SM_STATES
#error "SM_STATES undefined: " \
"you must provide a list of comma-separated states"
#endif
typedef void (*sm_state) SM_ARGS;
static const sm_state SM_STATES;
#define sm_transit(STATE) ((sm_state (*) SM_ARGS)STATE)
#define sm_def(NAME) \
static sm_state NAME ## _fn SM_ARGS; \
static const sm_state NAME = (sm_state)NAME ## _fn; \
static sm_state NAME ## _fn SM_ARGS
example.c
#include <stdio.h>
#define SM_ARGS (int i)
#define SM_STATES EVEN, ODD
#include "sm.h"
sm_def(EVEN)
{
printf("even %i\n", i);
return ODD;
}
sm_def(ODD)
{
printf("odd %i\n", i);
return EVEN;
}
int main(void)
{
int i = 0;
sm_state state = EVEN;
for(; i < 10; ++i)
state = sm_transit(state)(i);
return 0;
}
I really liked paxdiable's answer and decided to implement all the missing features for my application like guard variables and state machine specific data.
I uploaded my implementation to this site to share with the community. It has been tested using IAR Embedded Workbench for ARM.
https://sourceforge.net/projects/compactfsm/
Another interesting open source tool is Yakindu Statechart Tools on statecharts.org. It makes use of Harel statecharts and thus provides hierarchical and parallel states and generates C and C++ (as well as Java) code. It does not make use of libraries but follows a 'plain code' approach. The code basically applies switch-case structures. The code generators can also be customized. Additionally the tool provides many other features.
Here is an example of a Finite State Machine for Linux that uses message queues as the events. The events are put on the queue and handled in order. The state changes depending on what happens for each event.
This is an example for a data connection with states like:
Uninitialized
Initialized
Connected
MTU Negotiated
Authenticated
One little extra feature I added was a timestamp for each message/event. The event handler will ignore events that are too old (they have expired). This can happen a lot in the real world where you might get stuck in a state unexpectedly.
This example runs on Linux, use the Makefile below to compile it and play around with it.
state_machine.c
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <unistd.h> // sysconf()
#include <errno.h> // errno
#include <string.h> // strerror()
#include <sys/time.h> // gettimeofday()
#include <fcntl.h> // For O_* constants
#include <sys/stat.h> // For mode constants
#include <mqueue.h>
#include <poll.h>
//------------------------------------------------
// States
//------------------------------------------------
typedef enum
{
ST_UNKNOWN = 0,
ST_UNINIT,
ST_INIT,
ST_CONNECTED,
ST_MTU_NEGOTIATED,
ST_AUTHENTICATED,
ST_ERROR,
ST_DONT_CHANGE,
ST_TERM,
} fsmState_t;
//------------------------------------------------
// Events
//------------------------------------------------
typedef enum
{
EV_UNKNOWN = 0,
EV_INIT_SUCCESS,
EV_INIT_FAIL,
EV_MASTER_CMD_MSG,
EV_CONNECT_SUCCESS,
EV_CONNECT_FAIL,
EV_MTU_SUCCESS,
EV_MTU_FAIL,
EV_AUTH_SUCCESS,
EV_AUTH_FAIL,
EV_TX_SUCCESS,
EV_TX_FAIL,
EV_DISCONNECTED,
EV_DISCON_FAILED,
EV_LAST_ENTRY,
} fsmEvName_t;
typedef struct fsmEvent_type
{
fsmEvName_t name;
struct timeval genTime; // Time the event was generated.
// This allows us to see how old the event is.
} fsmEvent_t;
// Finite State Machine Data Members
typedef struct fsmData_type
{
int connectTries;
int MTUtries;
int authTries;
int txTries;
} fsmData_t;
// Each row of the state table
typedef struct stateTable_type {
fsmState_t st; // Current state
fsmEvName_t evName; // Got this event
int (*conditionfn)(void *); // If this condition func returns TRUE
fsmState_t nextState; // Change to this state and
void (*fn)(void *); // Run this function
} stateTable_t;
// Finite State Machine state structure
typedef struct fsm_type
{
const stateTable_t *pStateTable; // Pointer to state table
int numStates; // Number of entries in the table
fsmState_t currentState; // Current state
fsmEvent_t currentEvent; // Current event
fsmData_t *fsmData; // Pointer to the data attributes
mqd_t mqdes; // Message Queue descriptor
mqd_t master_cmd_mqdes; // Master command message queue
} fsm_t;
// Wildcard events and wildcard state
#define EV_ANY -1
#define ST_ANY -1
#define TRUE (1)
#define FALSE (0)
// Maximum priority for message queues (see "man mq_overview")
#define FSM_PRIO (sysconf(_SC_MQ_PRIO_MAX) - 1)
static void addev (fsm_t *fsm, fsmEvName_t ev);
static void doNothing (void *fsm) {addev(fsm, EV_MASTER_CMD_MSG);}
static void doInit (void *fsm) {addev(fsm, EV_INIT_SUCCESS);}
static void doConnect (void *fsm) {addev(fsm, EV_CONNECT_SUCCESS);}
static void doMTU (void *fsm) {addev(fsm, EV_MTU_SUCCESS);}
static void reportFailConnect (void *fsm) {addev(fsm, EV_ANY);}
static void doAuth (void *fsm) {addev(fsm, EV_AUTH_SUCCESS);}
static void reportDisConnect (void *fsm) {addev(fsm, EV_ANY);}
static void doDisconnect (void *fsm) {addev(fsm, EV_ANY);}
static void doTransaction (void *fsm) {addev(fsm, EV_TX_FAIL);}
static void fsmError (void *fsm) {addev(fsm, EV_ANY);}
static int currentlyLessThanMaxConnectTries (void *fsm) {
fsm_t *l = (fsm_t *)fsm;
return (l->fsmData->connectTries < 5 ? TRUE : FALSE);
}
static int isMoreThanMaxConnectTries (void *fsm) {return TRUE;}
static int currentlyLessThanMaxMTUtries (void *fsm) {return TRUE;}
static int isMoreThanMaxMTUtries (void *fsm) {return TRUE;}
static int currentyLessThanMaxAuthTries (void *fsm) {return TRUE;}
static int isMoreThanMaxAuthTries (void *fsm) {return TRUE;}
static int currentlyLessThanMaxTXtries (void *fsm) {return FALSE;}
static int isMoreThanMaxTXtries (void *fsm) {return TRUE;}
static int didNotSelfDisconnect (void *fsm) {return TRUE;}
static int waitForEvent (fsm_t *fsm);
static void runEvent (fsm_t *fsm);
static void runStateMachine(fsm_t *fsm);
static int newEventIsValid(fsmEvent_t *event);
static void getTime(struct timeval *time);
void printState(fsmState_t st);
void printEvent(fsmEvName_t ev);
// Global State Table
const stateTable_t GST[] = {
// Current state Got this event If this condition func returns TRUE Change to this state and Run this function
{ ST_UNINIT, EV_INIT_SUCCESS, NULL, ST_INIT, &doNothing },
{ ST_UNINIT, EV_INIT_FAIL, NULL, ST_UNINIT, &doInit },
{ ST_INIT, EV_MASTER_CMD_MSG, NULL, ST_INIT, &doConnect },
{ ST_INIT, EV_CONNECT_SUCCESS, NULL, ST_CONNECTED, &doMTU },
{ ST_INIT, EV_CONNECT_FAIL, &currentlyLessThanMaxConnectTries, ST_INIT, &doConnect },
{ ST_INIT, EV_CONNECT_FAIL, &isMoreThanMaxConnectTries, ST_INIT, &reportFailConnect },
{ ST_CONNECTED, EV_MTU_SUCCESS, NULL, ST_MTU_NEGOTIATED, &doAuth },
{ ST_CONNECTED, EV_MTU_FAIL, &currentlyLessThanMaxMTUtries, ST_CONNECTED, &doMTU },
{ ST_CONNECTED, EV_MTU_FAIL, &isMoreThanMaxMTUtries, ST_CONNECTED, &doDisconnect },
{ ST_CONNECTED, EV_DISCONNECTED, &didNotSelfDisconnect, ST_INIT, &reportDisConnect },
{ ST_MTU_NEGOTIATED, EV_AUTH_SUCCESS, NULL, ST_AUTHENTICATED, &doTransaction },
{ ST_MTU_NEGOTIATED, EV_AUTH_FAIL, &currentyLessThanMaxAuthTries, ST_MTU_NEGOTIATED, &doAuth },
{ ST_MTU_NEGOTIATED, EV_AUTH_FAIL, &isMoreThanMaxAuthTries, ST_MTU_NEGOTIATED, &doDisconnect },
{ ST_MTU_NEGOTIATED, EV_DISCONNECTED, &didNotSelfDisconnect, ST_INIT, &reportDisConnect },
{ ST_AUTHENTICATED, EV_TX_SUCCESS, NULL, ST_AUTHENTICATED, &doDisconnect },
{ ST_AUTHENTICATED, EV_TX_FAIL, &currentlyLessThanMaxTXtries, ST_AUTHENTICATED, &doTransaction },
{ ST_AUTHENTICATED, EV_TX_FAIL, &isMoreThanMaxTXtries, ST_AUTHENTICATED, &doDisconnect },
{ ST_AUTHENTICATED, EV_DISCONNECTED, &didNotSelfDisconnect, ST_INIT, &reportDisConnect },
{ ST_ANY, EV_DISCON_FAILED, NULL, ST_DONT_CHANGE, &doDisconnect },
{ ST_ANY, EV_ANY, NULL, ST_UNINIT, &fsmError } // Wildcard state for errors
};
#define GST_COUNT (sizeof(GST)/sizeof(stateTable_t))
int main()
{
int ret = 0;
fsmData_t dataAttr;
dataAttr.connectTries = 0;
dataAttr.MTUtries = 0;
dataAttr.authTries = 0;
dataAttr.txTries = 0;
fsm_t lfsm;
memset(&lfsm, 0, sizeof(fsm_t));
lfsm.pStateTable = GST;
lfsm.numStates = GST_COUNT;
lfsm.currentState = ST_UNINIT;
lfsm.currentEvent.name = EV_ANY;
lfsm.fsmData = &dataAttr;
struct mq_attr attr;
attr.mq_maxmsg = 30;
attr.mq_msgsize = sizeof(fsmEvent_t);
// Dev info
//printf("Size of fsmEvent_t [%ld]\n", sizeof(fsmEvent_t));
ret = mq_unlink("/abcmq");
if (ret == -1) {
fprintf(stderr, "Error on mq_unlink(), errno[%d] strerror[%s]\n",
errno, strerror(errno));
}
lfsm.mqdes = mq_open("/abcmq", O_CREAT | O_RDWR, S_IWUSR | S_IRUSR, &attr);
if (lfsm.mqdes == (mqd_t)-1) {
fprintf(stderr, "Error on mq_open(), errno[%d] strerror[%s]\n",
errno, strerror(errno));
return -1;
}
doInit(&lfsm); // This will generate the first event
runStateMachine(&lfsm);
return 0;
}
static void runStateMachine(fsm_t *fsm)
{
int ret = 0;
if (fsm == NULL) {
fprintf(stderr, "[%s] NULL argument\n", __func__);
return;
}
// Cycle through the state machine
while (fsm->currentState != ST_TERM) {
printf("current state [");
printState(fsm->currentState);
printf("]\n");
ret = waitForEvent(fsm);
if (ret == 0) {
printf("got event [");
printEvent(fsm->currentEvent.name);
printf("]\n");
runEvent(fsm);
}
sleep(2);
}
}
static int waitForEvent(fsm_t *fsm)
{
//const int numFds = 2;
const int numFds = 1;
struct pollfd fds[numFds];
int timeout_msecs = -1; // -1 is forever
int ret = 0;
int i = 0;
ssize_t num = 0;
fsmEvent_t newEv;
if (fsm == NULL) {
fprintf(stderr, "[%s] NULL argument\n", __func__);
return -1;
}
fsm->currentEvent.name = EV_ANY;
fds[0].fd = fsm->mqdes;
fds[0].events = POLLIN;
//fds[1].fd = fsm->master_cmd_mqdes;
//fds[1].events = POLLIN;
ret = poll(fds, numFds, timeout_msecs);
if (ret > 0) {
// An event on one of the fds has occurred
for (i = 0; i < numFds; i++) {
if (fds[i].revents & POLLIN) {
// Data may be read on device number i
num = mq_receive(fds[i].fd, (void *)(&newEv),
sizeof(fsmEvent_t), NULL);
if (num == -1) {
fprintf(stderr, "Error on mq_receive(), errno[%d] "
"strerror[%s]\n", errno, strerror(errno));
return -1;
}
if (newEventIsValid(&newEv)) {
fsm->currentEvent = newEv;
} else {
return -1;
}
}
}
} else {
fprintf(stderr, "Error on poll(), ret[%d] errno[%d] strerror[%s]\n",
ret, errno, strerror(errno));
return -1;
}
return 0;
}
static int newEventIsValid(fsmEvent_t *event)
{
if (event == NULL) {
fprintf(stderr, "[%s] NULL argument\n", __func__);
return FALSE;
}
printf("[%s]\n", __func__);
struct timeval now;
getTime(&now);
if ( (event->name < EV_LAST_ENTRY) &&
((now.tv_sec - event->genTime.tv_sec) < (60*5))
)
{
return TRUE;
} else {
return FALSE;
}
}
//------------------------------------------------
// Performs event handling on the FSM (finite state machine).
// Make sure there is a wildcard state at the end of
// your table, otherwise; the event will be ignored.
//------------------------------------------------
static void runEvent(fsm_t *fsm)
{
int i;
int condRet = 0;
if (fsm == NULL) {
fprintf(stderr, "[%s] NULL argument\n", __func__);
return;
}
printf("[%s]\n", __func__);
// Find a relevant entry for this state and event
for (i = 0; i < fsm->numStates; i++) {
// Look in the table for our current state or ST_ANY
if ( (fsm->pStateTable[i].st == fsm->currentState) ||
(fsm->pStateTable[i].st == ST_ANY)
)
{
// Is this the event we are looking for?
if ( (fsm->pStateTable[i].evName == fsm->currentEvent.name) ||
(fsm->pStateTable[i].evName == EV_ANY)
)
{
if (fsm->pStateTable[i].conditionfn != NULL) {
condRet = fsm->pStateTable[i].conditionfn(fsm->fsmData);
}
// See if there is a condition associated
// or we are not looking for any condition
//
if ( (condRet != 0) || (fsm->pStateTable[i].conditionfn == NULL))
{
// Set the next state (if applicable)
if (fsm->pStateTable[i].nextState != ST_DONT_CHANGE) {
fsm->currentState = fsm->pStateTable[i].nextState;
printf("new state [");
printState(fsm->currentState);
printf("]\n");
}
// Call the state callback function
fsm->pStateTable[i].fn(fsm);
break;
}
}
}
}
}
//------------------------------------------------
// EVENT HANDLERS
//------------------------------------------------
static void getTime(struct timeval *time)
{
if (time == NULL) {
fprintf(stderr, "[%s] NULL argument\n", __func__);
return;
}
printf("[%s]\n", __func__);
int ret = gettimeofday(time, NULL);
if (ret != 0) {
fprintf(stderr, "gettimeofday() failed: errno [%d], strerror [%s]\n",
errno, strerror(errno));
memset(time, 0, sizeof(struct timeval));
}
}
static void addev (fsm_t *fsm, fsmEvName_t ev)
{
int ret = 0;
if (fsm == NULL) {
fprintf(stderr, "[%s] NULL argument\n", __func__);
return;
}
printf("[%s] ev[%d]\n", __func__, ev);
if (ev == EV_ANY) {
// Don't generate a new event, just return...
return;
}
fsmEvent_t newev;
getTime(&(newev.genTime));
newev.name = ev;
ret = mq_send(fsm->mqdes, (void *)(&newev), sizeof(fsmEvent_t), FSM_PRIO);
if (ret == -1) {
fprintf(stderr, "[%s] mq_send() failed: errno [%d], strerror [%s]\n",
__func__, errno, strerror(errno));
}
}
//------------------------------------------------
// end EVENT HANDLERS
//------------------------------------------------
void printState(fsmState_t st)
{
switch(st) {
case ST_UNKNOWN:
printf("ST_UNKNOWN");
break;
case ST_UNINIT:
printf("ST_UNINIT");
break;
case ST_INIT:
printf("ST_INIT");
break;
case ST_CONNECTED:
printf("ST_CONNECTED");
break;
case ST_MTU_NEGOTIATED:
printf("ST_MTU_NEGOTIATED");
break;
case ST_AUTHENTICATED:
printf("ST_AUTHENTICATED");
break;
case ST_ERROR:
printf("ST_ERROR");
break;
case ST_TERM:
printf("ST_TERM");
break;
default:
printf("unknown state");
break;
}
}
void printEvent(fsmEvName_t ev)
{
switch (ev) {
case EV_UNKNOWN:
printf("EV_UNKNOWN");
break;
case EV_INIT_SUCCESS:
printf("EV_INIT_SUCCESS");
break;
case EV_INIT_FAIL:
printf("EV_INIT_FAIL");
break;
case EV_MASTER_CMD_MSG:
printf("EV_MASTER_CMD_MSG");
break;
case EV_CONNECT_SUCCESS:
printf("EV_CONNECT_SUCCESS");
break;
case EV_CONNECT_FAIL:
printf("EV_CONNECT_FAIL");
break;
case EV_MTU_SUCCESS:
printf("EV_MTU_SUCCESS");
break;
case EV_MTU_FAIL:
printf("EV_MTU_FAIL");
break;
case EV_AUTH_SUCCESS:
printf("EV_AUTH_SUCCESS");
break;
case EV_AUTH_FAIL:
printf("EV_AUTH_FAIL");
break;
case EV_TX_SUCCESS:
printf("EV_TX_SUCCESS");
break;
case EV_TX_FAIL:
printf("EV_TX_FAIL");
break;
case EV_DISCONNECTED:
printf("EV_DISCONNECTED");
break;
case EV_LAST_ENTRY:
printf("EV_LAST_ENTRY");
break;
default:
printf("unknown event");
break;
}
}
Makefile
CXX = gcc
COMPFLAGS = -c -Wall -g
state_machine: state_machine.o
$(CXX) -lrt state_machine.o -o state_machine
state_machine.o: state_machine.c
$(CXX) $(COMPFLAGS) state_machine.c
clean:
rm state_machine state_machine.o
Coming to this late (as usual) but scanning the answers to date I thinks something important is missing;
I have found in my own projects that it can be very helpful to not have a function for every valid state/event combination. I do like the idea of effectively having a 2D table of states/events. But I like the table elements to be more than a simple function pointer. Instead I try to organize my design so at it's heart it comprises a bunch of simple atomic elements or actions. That way I can list those simple atomic elements at each intersection of my state/event table. The idea is that you don't have to define a mass of N squared (typically very simple) functions. Why have something so error-prone, time consuming, hard to write, hard to read, you name it ?
I also include an optional new state, and an optional function pointer for each cell in the table. The function pointer is there for those exceptional cases where you don't want to just fire off a list of atomic actions.
You know you are doing it right when you can express a lot of different functionality, just by editing your table, with no new code to write.
Alrght, I think mine's just a little different from everybody else's. A little more separation of code and data than I see in the other answers. I really read up on the theory to write this, which implements a full Regular-language (without regular expressions, sadly). Ullman, Minsky, Chomsky. Can't say I understood it all, but I've drawn from the old masters as directly as possible: through their words.
I use a function pointer to a predicate that determines the transition to a 'yes' state or a 'no' state. This facilitates the creation of a finite state acceptor for a regular language that you program in a more assembly-language-like manner.
Please don't be put-off by my silly name choices. 'czek' == 'check'. 'grok' == [go look it up in the Hacker Dictionary].
So for each iteration, czek calls a predicate function with the current character as argument. If the predicate returns true, the character is consumed (the pointer advanced) and we follow the 'y' transition to select the next state. If the predicate returns false, the character is NOT consumed and we follow the 'n' transition. So every instruction is a two-way branch! I must have been reading The Story of Mel at the time.
This code comes straight from my postscript interpreter, and evolved into its current form with much guidance from the fellows on comp.lang.c. Since postscript basically has no syntax (only requiring balanced brackets), a Regular Language Accepter like this functions as the parser as well.
/* currentstr is set to the start of string by czek
and used by setrad (called by israd) to set currentrad
which is used by israddig to determine if the character
in question is valid for the specified radix
--
a little semantic checking in the syntax!
*/
char *currentstr;
int currentrad;
void setrad(void) {
char *end;
currentrad = strtol(currentstr, &end, 10);
if (*end != '#' /* just a sanity check,
the automaton should already have determined this */
|| currentrad > 36
|| currentrad < 2)
fatal("bad radix"); /* should probably be a simple syntaxerror */
}
/*
character classes
used as tests by automatons under control of czek
*/
char *alpha = "0123456789" "ABCDE" "FGHIJ" "KLMNO" "PQRST" "UVWXYZ";
#define EQ(a,b) a==b
#define WITHIN(a,b) strchr(a,b)!=NULL
int israd (int c) {
if (EQ('#',c)) { setrad(); return true; }
return false;
}
int israddig(int c) {
return strchrnul(alpha,toupper(c))-alpha <= currentrad;
}
int isdot (int c) {return EQ('.',c);}
int ise (int c) {return WITHIN("eE",c);}
int issign (int c) {return WITHIN("+-",c);}
int isdel (int c) {return WITHIN("()<>[]{}/%",c);}
int isreg (int c) {return c!=EOF && !isspace(c) && !isdel(c);}
#undef WITHIN
#undef EQ
/*
the automaton type
*/
typedef struct { int (*pred)(int); int y, n; } test;
/*
automaton to match a simple decimal number
*/
/* /^[+-]?[0-9]+$/ */
test fsm_dec[] = {
/* 0*/ { issign, 1, 1 },
/* 1*/ { isdigit, 2, -1 },
/* 2*/ { isdigit, 2, -1 },
};
int acc_dec(int i) { return i==2; }
/*
automaton to match a radix number
*/
/* /^[0-9]+[#][a-Z0-9]+$/ */
test fsm_rad[] = {
/* 0*/ { isdigit, 1, -1 },
/* 1*/ { isdigit, 1, 2 },
/* 2*/ { israd, 3, -1 },
/* 3*/ { israddig, 4, -1 },
/* 4*/ { israddig, 4, -1 },
};
int acc_rad(int i) { return i==4; }
/*
automaton to match a real number
*/
/* /^[+-]?(d+(.d*)?)|(d*.d+)([eE][+-]?d+)?$/ */
/* represents the merge of these (simpler) expressions
[+-]?[0-9]+\.[0-9]*([eE][+-]?[0-9]+)?
[+-]?[0-9]*\.[0-9]+([eE][+-]?[0-9]+)?
The complexity comes from ensuring at least one
digit in the integer or the fraction with optional
sign and optional optionally-signed exponent.
So passing isdot in state 3 means at least one integer digit has been found
but passing isdot in state 4 means we must find at least one fraction digit
via state 5 or the whole thing is a bust.
*/
test fsm_real[] = {
/* 0*/ { issign, 1, 1 },
/* 1*/ { isdigit, 2, 4 },
/* 2*/ { isdigit, 2, 3 },
/* 3*/ { isdot, 6, 7 },
/* 4*/ { isdot, 5, -1 },
/* 5*/ { isdigit, 6, -1 },
/* 6*/ { isdigit, 6, 7 },
/* 7*/ { ise, 8, -1 },
/* 8*/ { issign, 9, 9 },
/* 9*/ { isdigit, 10, -1 },
/*10*/ { isdigit, 10, -1 },
};
int acc_real(int i) {
switch(i) {
case 2: /* integer */
case 6: /* real */
case 10: /* real with exponent */
return true;
}
return false;
}
/*
Helper function for grok.
Execute automaton against the buffer,
applying test to each character:
on success, consume character and follow 'y' transition.
on failure, do not consume but follow 'n' transition.
Call yes function to determine if the ending state
is considered an acceptable final state.
A transition to -1 represents rejection by the automaton
*/
int czek (char *s, test *fsm, int (*yes)(int)) {
int sta = 0;
currentstr = s;
while (sta!=-1 && *s) {
if (fsm[sta].pred((int)*s)) {
sta=fsm[sta].y;
s++;
} else {
sta=fsm[sta].n;
}
}
return yes(sta);
}
/*
Helper function for toke.
Interpret the contents of the buffer,
trying automatons to match number formats;
and falling through to a switch for special characters.
Any token consisting of all regular characters
that cannot be interpreted as a number is an executable name
*/
object grok (state *st, char *s, int ns,
object *src,
int (*next)(state *,object *),
void (*back)(state *,int, object *)) {
if (czek(s, fsm_dec, acc_dec)) {
long num;
num = strtol(s,NULL,10);
if ((num==LONG_MAX || num==LONG_MIN) && errno==ERANGE) {
error(st,limitcheck);
/* } else if (num > INT_MAX || num < INT_MIN) { */
/* error(limitcheck, OP_token); */
} else {
return consint(num);
}
}
else if (czek(s, fsm_rad, acc_rad)) {
long ra,num;
ra = (int)strtol(s,NULL,10);
if (ra > 36 || ra < 2) {
error(st,limitcheck);
}
num = strtol(strchr(s,'#')+1, NULL, (int)ra);
if ((num==LONG_MAX || num==LONG_MIN) && errno==ERANGE) {
error(st,limitcheck);
/* } else if (num > INT_MAX || num < INT_MAX) { */
/* error(limitcheck, OP_token); */
} else {
return consint(num);
}
}
else if (czek(s, fsm_real, acc_real)) {
double num;
num = strtod(s,NULL);
if ((num==HUGE_VAL || num==-HUGE_VAL) && errno==ERANGE) {
error(st,limitcheck);
} else {
return consreal(num);
}
}
else switch(*s) {
case '(': {
int c, defer=1;
char *sp = s;
while (defer && (c=next(st,src)) != EOF ) {
switch(c) {
case '(': defer++; break;
case ')': defer--;
if (!defer) goto endstring;
break;
case '\\': c=next(st,src);
switch(c) {
case '\n': continue;
case 'a': c = '\a'; break;
case 'b': c = '\b'; break;
case 'f': c = '\f'; break;
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'v': c = '\v'; break;
case '\'': case '\"':
case '(': case ')':
default: break;
}
}
if (sp-s>ns) error(st,limitcheck);
else *sp++ = c;
}
endstring: *sp=0;
return cvlit(consstring(st,s,sp-s));
}
case '<': {
int c;
char d, *x = "0123456789abcdef", *sp = s;
while (c=next(st,src), c!='>' && c!=EOF) {
if (isspace(c)) continue;
if (isxdigit(c)) c = strchr(x,tolower(c)) - x;
else error(st,syntaxerror);
d = (char)c << 4;
while (isspace(c=next(st,src))) /*loop*/;
if (isxdigit(c)) c = strchr(x,tolower(c)) - x;
else error(st,syntaxerror);
d |= (char)c;
if (sp-s>ns) error(st,limitcheck);
*sp++ = d;
}
*sp = 0;
return cvlit(consstring(st,s,sp-s));
}
case '{': {
object *a;
size_t na = 100;
size_t i;
object proc;
object fin;
fin = consname(st,"}");
(a = malloc(na * sizeof(object))) || (fatal("failure to malloc"),0);
for (i=0 ; objcmp(st,a[i]=toke(st,src,next,back),fin) != 0; i++) {
if (i == na-1)
(a = realloc(a, (na+=100) * sizeof(object))) || (fatal("failure to malloc"),0);
}
proc = consarray(st,i);
{ size_t j;
for (j=0; j<i; j++) {
a_put(st, proc, j, a[j]);
}
}
free(a);
return proc;
}
case '/': {
s[1] = (char)next(st,src);
puff(st, s+2, ns-2, src, next, back);
if (s[1] == '/') {
push(consname(st,s+2));
opexec(st, op_cuts.load);
return pop();
}
return cvlit(consname(st,s+1));
}
default: return consname(st,s);
}
return null; /* should be unreachable */
}
/*
Helper function for toke.
Read into buffer any regular characters.
If we read one too many characters, put it back
unless it's whitespace.
*/
int puff (state *st, char *buf, int nbuf,
object *src,
int (*next)(state *,object *),
void (*back)(state *,int, object *)) {
int c;
char *s = buf;
while (isreg(c=next(st,src))) {
if (s-buf >= nbuf-1) return false;
*s++ = c;
}
*s = 0;
if (!isspace(c) && c != EOF) back(st,c,src); /* eat interstice */
return true;
}
/*
Helper function for Stoken Ftoken.
Read a token from src using next and back.
Loop until having read a bona-fide non-whitespace non-comment character.
Call puff to read into buffer up to next delimiter or space.
Call grok to figure out what it is.
*/
#define NBUF MAXLINE
object toke (state *st, object *src,
int (*next)(state *, object *),
void (*back)(state *, int, object *)) {
char buf[NBUF] = "", *s=buf;
int c,sta = 1;
object o;
do {
c=next(st,src);
//if (c==EOF) return null;
if (c=='%') {
if (DUMPCOMMENTS) fputc(c, stdout);
do {
c=next(st,src);
if (DUMPCOMMENTS) fputc(c, stdout);
} while (c!='\n' && c!='\f' && c!=EOF);
}
} while (c!=EOF && isspace(c));
if (c==EOF) return null;
*s++ = c;
*s = 0;
if (!isdel(c)) sta=puff(st, s,NBUF-1,src,next,back);
if (sta) {
o=grok(st,buf,NBUF-1,src,next,back);
return o;
} else {
return null;
}
}
boost.org comes with 2 different state chart implementations:
Meta State Machine
Statechart
As always, boost will beam you into template hell.
The first library is for more performance-critical state machines. The second library gives you a direct transition path from a UML Statechart to code.
Here's the SO question asking for a comparison between the two where both of the authors respond.
You can consider UML-state-machine-in-c, a "lightweight" state machine framework in C. I have written this framework to support both Finite state machine and Hierarchical state machine. Compare to state tables or simple switch cases, a framework approach is more scalable. It can be used for simple finite state machines to complex hierarchical state machines.
State machine is represented by state_machine_t structure. It contains only two members "Event" and a pointer to the "state_t".
struct state_machine_t
{
uint32_t Event; //!< Pending Event for state machine
const state_t* State; //!< State of state machine.
};
state_machine_t must be the first member of your state machine structure. e.g.
struct user_state_machine
{
state_machine_t Machine; // Base state machine. Must be the first member of user derived state machine.
// User specific state machine members
uint32_t param1;
uint32_t param2;
...
};
state_t contains a handler for the state and also optional handlers for entry and exit action.
//! finite state structure
struct finite_state{
state_handler Handler; //!< State handler to handle event of the state
state_handler Entry; //!< Entry action for state
state_handler Exit; //!< Exit action for state.
};
If the framework is configured for a hierarchical state machine then the state_t contains a pointer to parent and child state.
Framework provides an API dispatch_event to dispatch the event to the state machine and switch_state to trigger state transition.
For further details on how to implement a hierarchical state machine refer to the GitHub repository.
code examples,
https://github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/simple_state_machine/readme.md
https://github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/simple_state_machine_enhanced/readme.md
This series of Ars OpenForum posts about a somewhat complicated bit of control logic includes a very easy-to-follow implementation as a state machine in C.
Your question is quite generic,
Here are two reference articles that might be useful,
Embedded State Machine Implementation
This article describes a simple approach to implementing a state machine for an embedded system. For purposes of this article, a state machine is defined as an algorithm that can be in one of a small number of states. A state is a condition that causes a prescribed relationship of inputs to outputs, and of inputs to next states.
A savvy reader will quickly note that the state machines described in this article are Mealy machines. A Mealy machine is a state machine where the outputs are a function of both present state and input, as opposed to a Moore machine, in which the outputs are a function only of state.
Coding State Machines in C and C++
My preoccupation in this article is with state-machine fundamentals and some straightforward programming guidelines for coding state machines in C or C++. I hope that these simple techniques can become more common, so that you (and others) can readily see the state-machine structure right from the source code.
I have used State Machine Compiler in Java and Python projects to with success.
Given that you imply you can use C++ and hence OO code, I would suggest evaluating the 'GoF'state pattern (GoF = Gang of Four, the guys who wrote the design patterns book which brought design patterns into the limelight).
It is not particularly complex and it is widely used and discussed so it is easy to see examples and explanations on line.
It will also quite likely be recognizable by anyone else maintaining your code at a later date.
If efficiency is the worry, it would be worth actually benchmarking to make sure that a non OO approach is more efficient as lots of factors affect performance and it is not always simply OO bad, functional code good. Similarly, if memory usage is a constraint for you it is again worth doing some tests or calculations to see if this will actually be an issue for your particular application if you use the state pattern.
The following are some links to the 'Gof' state pattern, as Craig suggests:
http://en.wikipedia.org/wiki/State_pattern
http://www.vincehuston.org/dp/state.html
This is an old post with lots of answers, but I thought I'd add my own approach to the finite state machine in C. I made a Python script to produce the skeleton C code for any number of states. That script is documented on GituHub at FsmTemplateC
This example is based on other approaches I've read about. It doesn't use goto or switch statements but instead has transition functions in a pointer matrix (look-up table). The code relies on a big multi-line initializer macro and C99 features (designated initializers and compound literals) so if you don't like these things, you might not like this approach.
Here is a Python script of a turnstile example which generates skeleton C-code using FsmTemplateC:
# dict parameter for generating FSM
fsm_param = {
# main FSM struct type string
'type': 'FsmTurnstile',
# struct type and name for passing data to state machine functions
# by pointer (these custom names are optional)
'fopts': {
'type': 'FsmTurnstileFopts',
'name': 'fopts'
},
# list of states
'states': ['locked', 'unlocked'],
# list of inputs (can be any length > 0)
'inputs': ['coin', 'push'],
# map inputs to commands (next desired state) using a transition table
# index of array corresponds to 'inputs' array
# for this example, index 0 is 'coin', index 1 is 'push'
'transitiontable': {
# current state | 'coin' | 'push' |
'locked': ['unlocked', ''],
'unlocked': [ '', 'locked']
}
}
# folder to contain generated code
folder = 'turnstile_example'
# function prefix
prefix = 'fsm_turnstile'
# generate FSM code
code = fsm.Fsm(fsm_param).genccode(folder, prefix)
The generated output header contains the typedefs:
/* function options (EDIT) */
typedef struct FsmTurnstileFopts {
/* define your options struct here */
} FsmTurnstileFopts;
/* transition check */
typedef enum eFsmTurnstileCheck {
EFSM_TURNSTILE_TR_RETREAT,
EFSM_TURNSTILE_TR_ADVANCE,
EFSM_TURNSTILE_TR_CONTINUE,
EFSM_TURNSTILE_TR_BADINPUT
} eFsmTurnstileCheck;
/* states (enum) */
typedef enum eFsmTurnstileState {
EFSM_TURNSTILE_ST_LOCKED,
EFSM_TURNSTILE_ST_UNLOCKED,
EFSM_TURNSTILE_NUM_STATES
} eFsmTurnstileState;
/* inputs (enum) */
typedef enum eFsmTurnstileInput {
EFSM_TURNSTILE_IN_COIN,
EFSM_TURNSTILE_IN_PUSH,
EFSM_TURNSTILE_NUM_INPUTS,
EFSM_TURNSTILE_NOINPUT
} eFsmTurnstileInput;
/* finite state machine struct */
typedef struct FsmTurnstile {
eFsmTurnstileInput input;
eFsmTurnstileCheck check;
eFsmTurnstileState cur;
eFsmTurnstileState cmd;
eFsmTurnstileState **transition_table;
void (***state_transitions)(struct FsmTurnstile *, FsmTurnstileFopts *);
void (*run)(struct FsmTurnstile *, FsmTurnstileFopts *, const eFsmTurnstileInput);
} FsmTurnstile;
/* transition functions */
typedef void (*pFsmTurnstileStateTransitions)(struct FsmTurnstile *, FsmTurnstileFopts *);
enum eFsmTurnstileCheck is used to determine whether a transition was blocked with EFSM_TURNSTILE_TR_RETREAT, allowed to progress with EFSM_TURNSTILE_TR_ADVANCE, or the function call was not preceded by a transition with EFSM_TURNSTILE_TR_CONTINUE.
enum eFsmTurnstileState is simply the list of states.
enum eFsmTurnstileInput is simply the list of inputs.
The FsmTurnstile struct is the heart of the state machine with the transition check, function lookup table, current state, commanded state, and an alias to the primary function that runs the machine.
Every function pointer (alias) in FsmTurnstile should only be called from the struct and has to have its first input as a pointer to itself so as to maintain a persistent state, object-oriented style.
Now for the function declarations in the header:
/* fsm declarations */
void fsm_turnstile_locked_locked (FsmTurnstile *fsm, FsmTurnstileFopts *fopts);
void fsm_turnstile_locked_unlocked (FsmTurnstile *fsm, FsmTurnstileFopts *fopts);
void fsm_turnstile_unlocked_locked (FsmTurnstile *fsm, FsmTurnstileFopts *fopts);
void fsm_turnstile_unlocked_unlocked (FsmTurnstile *fsm, FsmTurnstileFopts *fopts);
void fsm_turnstile_run (FsmTurnstile *fsm, FsmTurnstileFopts *fopts, const eFsmTurnstileInput input);
Function names are in the format {prefix}_{from}_{to}, where {from} is the previous (current) state and {to} is the next state. Note that if the transition table does not allow for certain transitions, a NULL pointer instead of a function pointer will be set. Finally, the magic happens with a macro. Here we build the transition table (matrix of state enums) and the state transition functions look up table (a matrix of function pointers):
/* creation macro */
#define FSM_TURNSTILE_CREATE() \
{ \
.input = EFSM_TURNSTILE_NOINPUT, \
.check = EFSM_TURNSTILE_TR_CONTINUE, \
.cur = EFSM_TURNSTILE_ST_LOCKED, \
.cmd = EFSM_TURNSTILE_ST_LOCKED, \
.transition_table = (eFsmTurnstileState * [EFSM_TURNSTILE_NUM_STATES]) { \
(eFsmTurnstileState [EFSM_TURNSTILE_NUM_INPUTS]) { \
EFSM_TURNSTILE_ST_UNLOCKED, \
EFSM_TURNSTILE_ST_LOCKED \
}, \
(eFsmTurnstileState [EFSM_TURNSTILE_NUM_INPUTS]) { \
EFSM_TURNSTILE_ST_UNLOCKED, \
EFSM_TURNSTILE_ST_LOCKED \
} \
}, \
.state_transitions = (pFsmTurnstileStateTransitions * [EFSM_TURNSTILE_NUM_STATES]) { \
(pFsmTurnstileStateTransitions [EFSM_TURNSTILE_NUM_STATES]) { \
fsm_turnstile_locked_locked, \
fsm_turnstile_locked_unlocked \
}, \
(pFsmTurnstileStateTransitions [EFSM_TURNSTILE_NUM_STATES]) { \
fsm_turnstile_unlocked_locked, \
fsm_turnstile_unlocked_unlocked \
} \
}, \
.run = fsm_turnstile_run \
}
When creating the FSM, the macro FSM_EXAMPLE_CREATE() has to be used.
Now, in the source code every state transition function declared above should be populated. The FsmTurnstileFopts struct can be used to pass data to/from the state machine. Every transition must set fsm->check to be equal to either EFSM_EXAMPLE_TR_RETREAT to block it from transitioning or EFSM_EXAMPLE_TR_ADVANCE to allow it to transition to the commanded state.
A working example can be found at (FsmTemplateC)[https://github.com/ChisholmKyle/FsmTemplateC].
Here is the very simple actual usage in your code:
/* create fsm */
FsmTurnstile fsm = FSM_TURNSTILE_CREATE();
/* create fopts */
FsmTurnstileFopts fopts = {
.msg = ""
};
/* initialize input */
eFsmTurnstileInput input = EFSM_TURNSTILE_NOINPUT;
/* main loop */
for (;;) {
/* wait for timer signal, inputs, interrupts, whatever */
/* optionally set the input (my_input = EFSM_TURNSTILE_IN_PUSH for example) */
/* run state machine */
my_fsm.run(&my_fsm, &my_fopts, my_input);
}
All that header business and all those functions just to have a simple and fast interface is worth it in my mind.
You could use the open source library OpenFST.
OpenFst is a library for constructing, combining, optimizing, and searching weighted finite-state transducers (FSTs). Weighted finite-state transducers are automata where each transition has an input label, an output label, and a weight. The more familiar finite-state acceptor is represented as a transducer with each transition's input and output label equal. Finite-state acceptors are used to represent sets of strings (specifically, regular or rational sets); finite-state transducers are used to represent binary relations between pairs of strings (specifically, rational transductions). The weights can be used to represent the cost of taking a particular transition.
void (* StateController)(void);
void state1(void);
void state2(void);
void main()
{
StateController=&state1;
while(1)
{
(* StateController)();
}
}
void state1(void)
{
//do something in state1
StateController=&state2;
}
void state2(void)
{
//do something in state2
//Keep changing function direction based on state transition
StateController=&state1;
}
I personally use self referencing structs in combination with pointer arrays.
I uploaded a tutorial on github a while back, link:
https://github.com/mmelchger/polling_state_machine_c
Note: I do realise that this thread is quite old, but I hope to get input and thoughts on the design of the state-machine as well as being able to provide an example for a possible state-machine design in C.
Here is a method for a state machine that uses macros such that each function can have its own set of states: https://www.codeproject.com/Articles/37037/Macros-to-simulate-multi-tasking-blocking-code-at
It is titled "simulate multi tasking" but that is not the only use.
This method uses callbacks to pickup in each function where it left off. Each function contains a list of states unique to each function. A central "idle loop" is used to run the state machines. The "idle loop" has no idea how the state machines work, it is the individual functions that "know what to do". In order to write code for the functions, one just creates a list of states and uses the macros to "suspend" and "resume". I used these macros at Cisco when I wrote the Transceiver Library for the Nexus 7000 switch.