├── READ.ME ├── README.md ├── c ├── hsm.c ├── hsm.h ├── hsmtst.c └── watch.c ├── cpp ├── hsm.cpp ├── hsm.h ├── hsmtst.cpp └── watch.cpp └── samek0008.pdf /READ.ME: -------------------------------------------------------------------------------- 1 | 2 | State Oriented Programming 3 | Hierarchical State Machines in C/C++ 4 | Miro Samek and Paul Y. Montgomery 5 | May 13, 2000 6 | 7 | The code accompanying the ESP article is organized into two 8 | subdirectories: C and Cpp. Each subdirectory contains Microsoft 9 | Visual C++ v 5.0 project to build and run the digital watch example 10 | discussed in the article. To compile and run the examples you would 11 | load the workspace (watch.dsw) into the Visual C++ IDE. The Debug 12 | versions of both C and C++ projects define preprocess macro HSM_INSTR 13 | and demonstrate simple instrumentation of the state machines. For 14 | your convenience we have included the executable files (watch.exe), 15 | which you can try directly. You inject events into the watch state 16 | machine by typing numbers on your keyboard: (0=DATE_EVT, 1=SET_EVT, 17 | 2=TICK_EVT). 18 | 19 | In order to use the code in your embedded projects you would need to 20 | extract files hsm.h and hsm.c from the C subdirectory, or files hsm.h 21 | and hsm.cpp form the Cpp subdirectory. We have compiled the code with 22 | a variety of C and C++ compilers, including: VC++ and Borland 23 | compilers for Windows, g++ compiler for Linux, ARM Software 24 | Development Toolkit v. 2.0 and 2.5 C compiler, and Green Hills MULTI 25 | 2000 C and EC++ ARM/THUMB compilers. We have noticed one potential 26 | problem with one aspect of the C++ implementation. Depending on the 27 | compiler you would use you may encounter compilation errors in 28 | casting (upcasting) event handlers to a Hsm member function pointer 29 | (EvtHndlr). This upcasting is necessary to configure the state 30 | machine in the constructor. In our code we use the most commonly 31 | accepted by different compilers cast: 32 | 33 | (EvtHndlr)&:: 34 | 35 | newer C++ compilers (but not EC++ compilers) may accept construct: 36 | 37 | reinterpret_cast(&::) 38 | 39 | Your compiler may allow you to use a simpler form: 40 | 41 | (EvtHndlr) 42 | 43 | since specifying class with the scope operator :: should not be 44 | necessary inside the class method (the constructor). 45 | 46 | Should you have any questions or comments please feel free to contact 47 | us directly: 48 | 49 | miro@IntegriNautics.com, or 50 | paulm@IntegriNautics.com 51 | 52 | ===================================================================== 53 | 54 | Corrections 55 | January 23, 2003 56 | 57 | Kevin Flemming at Philips Electronics North America has recently 58 | alerted us of rather serious bug in the original code. As reported by 59 | Kevin, our original HSM implementation handles incorrectly some 60 | inherited transitions, that is transitions originating at the higher 61 | levels of state hierarchy than the currently active state. More 62 | specifically, the issue is created by the optimization involving 63 | one-time only calculation of the least common ancestor (LCA) state, 64 | which is stored in a static variable shared by all instances of a given 65 | state machine. The bug shows up when the transition is inherited by 66 | more than one substate. The original code makes no distinction between 67 | the source state for a transition, and the currently active state. In 68 | case of inherited state transition, the two states are different. 69 | 70 | 71 | The bug fix designed by Paul Montgomery upholds all the discussion and 72 | claims we made in the article. It is not expensive in terms of either 73 | memory or CPU cycles, and retains the static LCA optimization. The bug 74 | fix involves augmenting the abstract Hsm base class with one 75 | additional attribute (State *source). This is a pointer to the 76 | "source state"--the state from which the transition actually 77 | originates. (Please note that for the inherited transitions the source 78 | of a transition is *different* from the current state.) Calculation of 79 | the LCA can be still performed statically (done just once) because the 80 | LCA is a function of the state topology only. This is fixed at 81 | compile time and is identical for all instances of any Hsm subclass. 82 | Paul's code correctly augments the event processor (Hsm::onEvent() 83 | method in C++ and HsmOnEvent() in C) to exit the currently active 84 | state up to the level of the "source state", for which the LCA is 85 | statically calculated. 86 | 87 | We provide a new, fixed state machine code (hsm.h/hsm.cpp for C++ and 88 | hsm.h/hsm.c for C). Alongside the previous Watch example described in 89 | the article, we also provide a new test harness for the bug fix (hsmtst 90 | workspace and project in both directories C and Cpp). Please note that 91 | in the meantime we switched from Microsoft Visual C++ v5.0 to v6.0. 92 | 93 | It is also probably worth noting that the bug and the fix pertains only 94 | to the HSM implementation published in the "State Oriented Programming" 95 | ESP article from August 2000, and specifically does *not* pertain to the 96 | implementation of HSM published in the book "Practical Statecharts in 97 | C/C++" by Miro Samek (CMP Books, 2002). The "Practical Statecharts" 98 | implementation handles all inherited transitions correctly. 99 | 100 | 101 | We would like to apologize for the bug that escaped our attention and 102 | testing. At the same time, we'd like to thank Kevin Flemming for finding 103 | and alerting us about the issue. 104 | 105 | Sincerely, 106 | 107 | Paul 108 | paulm@IntegriNautics.com 109 | 110 | Miro 111 | miro@quantum-leaps.com 112 | 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HierarchicalStateMachine 2 | A hierarchical state machine for C or C++ 3 | 4 | 5 | 6 | State Oriented Programming 7 | Hierarchical State Machines in C/C++ 8 | Miro Samek and Paul Y. Montgomery 9 | May 13, 2000 10 | 11 | The code accompanying the ESP article is organized into two 12 | subdirectories: C and Cpp. Each subdirectory contains Microsoft 13 | Visual C++ v 5.0 project to build and run the digital watch example 14 | discussed in the article. To compile and run the examples you would 15 | load the workspace (watch.dsw) into the Visual C++ IDE. The Debug 16 | versions of both C and C++ projects define preprocess macro HSM_INSTR 17 | and demonstrate simple instrumentation of the state machines. For 18 | your convenience we have included the executable files (watch.exe), 19 | which you can try directly. You inject events into the watch state 20 | machine by typing numbers on your keyboard: (0=DATE_EVT, 1=SET_EVT, 21 | 2=TICK_EVT). 22 | 23 | In order to use the code in your embedded projects you would need to 24 | extract files hsm.h and hsm.c from the C subdirectory, or files hsm.h 25 | and hsm.cpp form the Cpp subdirectory. We have compiled the code with 26 | a variety of C and C++ compilers, including: VC++ and Borland 27 | compilers for Windows, g++ compiler for Linux, ARM Software 28 | Development Toolkit v. 2.0 and 2.5 C compiler, and Green Hills MULTI 29 | 2000 C and EC++ ARM/THUMB compilers. We have noticed one potential 30 | problem with one aspect of the C++ implementation. Depending on the 31 | compiler you would use you may encounter compilation errors in 32 | casting (upcasting) event handlers to a Hsm member function pointer 33 | (EvtHndlr). This upcasting is necessary to configure the state 34 | machine in the constructor. In our code we use the most commonly 35 | accepted by different compilers cast: 36 | 37 | (EvtHndlr)&:: 38 | 39 | newer C++ compilers (but not EC++ compilers) may accept construct: 40 | 41 | reinterpret_cast(&::) 42 | 43 | Your compiler may allow you to use a simpler form: 44 | 45 | (EvtHndlr) 46 | 47 | since specifying class with the scope operator :: should not be 48 | necessary inside the class method (the constructor). 49 | 50 | Should you have any questions or comments please feel free to contact 51 | us directly: 52 | 53 | miro@IntegriNautics.com, or 54 | paulm@IntegriNautics.com 55 | 56 | ===================================================================== 57 | 58 | Corrections 59 | January 23, 2003 60 | 61 | Kevin Flemming at Philips Electronics North America has recently 62 | alerted us of rather serious bug in the original code. As reported by 63 | Kevin, our original HSM implementation handles incorrectly some 64 | inherited transitions, that is transitions originating at the higher 65 | levels of state hierarchy than the currently active state. More 66 | specifically, the issue is created by the optimization involving 67 | one-time only calculation of the least common ancestor (LCA) state, 68 | which is stored in a static variable shared by all instances of a given 69 | state machine. The bug shows up when the transition is inherited by 70 | more than one substate. The original code makes no distinction between 71 | the source state for a transition, and the currently active state. In 72 | case of inherited state transition, the two states are different. 73 | 74 | 75 | The bug fix designed by Paul Montgomery upholds all the discussion and 76 | claims we made in the article. It is not expensive in terms of either 77 | memory or CPU cycles, and retains the static LCA optimization. The bug 78 | fix involves augmenting the abstract Hsm base class with one 79 | additional attribute (State *source). This is a pointer to the 80 | "source state"--the state from which the transition actually 81 | originates. (Please note that for the inherited transitions the source 82 | of a transition is *different* from the current state.) Calculation of 83 | the LCA can be still performed statically (done just once) because the 84 | LCA is a function of the state topology only. This is fixed at 85 | compile time and is identical for all instances of any Hsm subclass. 86 | Paul's code correctly augments the event processor (Hsm::onEvent() 87 | method in C++ and HsmOnEvent() in C) to exit the currently active 88 | state up to the level of the "source state", for which the LCA is 89 | statically calculated. 90 | 91 | We provide a new, fixed state machine code (hsm.h/hsm.cpp for C++ and 92 | hsm.h/hsm.c for C). Alongside the previous Watch example described in 93 | the article, we also provide a new test harness for the bug fix (hsmtst 94 | workspace and project in both directories C and Cpp). Please note that 95 | in the meantime we switched from Microsoft Visual C++ v5.0 to v6.0. 96 | 97 | It is also probably worth noting that the bug and the fix pertains only 98 | to the HSM implementation published in the "State Oriented Programming" 99 | ESP article from August 2000, and specifically does *not* pertain to the 100 | implementation of HSM published in the book "Practical Statecharts in 101 | C/C++" by Miro Samek (CMP Books, 2002). The "Practical Statecharts" 102 | implementation handles all inherited transitions correctly. 103 | 104 | 105 | We would like to apologize for the bug that escaped our attention and 106 | testing. At the same time, we'd like to thank Kevin Flemming for finding 107 | and alerting us about the issue. 108 | 109 | Sincerely, 110 | 111 | Paul 112 | paulm@IntegriNautics.com 113 | 114 | Miro 115 | miro@quantum-leaps.com 116 | 117 | -------------------------------------------------------------------------------- /c/hsm.c: -------------------------------------------------------------------------------- 1 | /** hsm.c -- Hierarchical State Machine implementation 2 | */ 3 | #include "hsm.h" 4 | 5 | static Msg const startMsg = { START_EVT }; 6 | static Msg const entryMsg = { ENTRY_EVT }; 7 | static Msg const exitMsg = { EXIT_EVT }; 8 | #define MAX_STATE_NESTING 8 9 | 10 | /* State Ctor...............................................................*/ 11 | void StateCtor(State *me, char const *name, State *super, EvtHndlr hndlr) { 12 | me->name = name; 13 | me->super = super; 14 | me->hndlr = hndlr; 15 | } 16 | 17 | /* Hsm Ctor.................................................................*/ 18 | void HsmCtor(Hsm *me, char const *name, EvtHndlr topHndlr) { 19 | StateCtor(&me->top, "top", 0, topHndlr); 20 | me->name = name; 21 | } 22 | 23 | /* enter and start the top state............................................*/ 24 | void HsmOnStart(Hsm *me) { 25 | me->curr = &me->top; 26 | me->next = 0; 27 | StateOnEvent(me->curr, me, &entryMsg); 28 | while (StateOnEvent(me->curr, me, &startMsg), me->next) { 29 | State *entryPath[MAX_STATE_NESTING]; 30 | register State **trace = entryPath; 31 | register State *s; 32 | *trace = 0; 33 | for (s = me->next; s != me->curr; s = s->super) { 34 | *(++trace) = s; /* trace path to target */ 35 | } 36 | while ( (s = *trace--) ) { /* retrace entry from source */ 37 | StateOnEvent(s, me, &entryMsg); 38 | } 39 | me->curr = me->next; 40 | me->next = 0; 41 | } 42 | } 43 | 44 | /* state machine "engine"...................................................*/ 45 | void HsmOnEvent(Hsm *me, Msg const *msg) { 46 | State *entryPath[MAX_STATE_NESTING]; 47 | register State **trace; 48 | register State *s; 49 | for (s = me->curr; s; s = s->super) { 50 | me->source = s; /* level of outermost event handler */ 51 | msg = StateOnEvent(s, me, msg); 52 | if (msg == 0) { 53 | if (me->next) { /* state transition taken? */ 54 | trace = entryPath; 55 | *trace = 0; 56 | for (s = me->next; s != me->curr; s = s->super) { 57 | *(++trace) = s; /* trace path to target */ 58 | } 59 | while ( (s = *trace--) ) { /* retrace entry from LCA */ 60 | StateOnEvent(s, me, &entryMsg); 61 | } 62 | me->curr = me->next; 63 | me->next = 0; 64 | while (StateOnEvent(me->curr, me, &startMsg), me->next) { 65 | trace = entryPath; 66 | *trace = 0; 67 | for (s = me->next; s != me->curr; s = s->super) { 68 | *(++trace) = s; /* record path to target */ 69 | } 70 | while ( (s = *trace--) ) { /* retrace the entry */ 71 | StateOnEvent(s, me, &entryMsg); 72 | } 73 | me->curr = me->next; 74 | me->next = 0; 75 | } 76 | } 77 | break; /* event processed */ 78 | } 79 | } 80 | } 81 | 82 | /* exit current states and all superstates up to LCA .......................*/ 83 | void HsmExit_(Hsm *me, unsigned char toLca) { 84 | register State *s = me->curr; 85 | while (s != me->source) { 86 | StateOnEvent(s, me, &exitMsg); 87 | s = s->super; 88 | } 89 | while (toLca--) { 90 | StateOnEvent(s, me, &exitMsg); 91 | s = s->super; 92 | } 93 | me->curr = s; 94 | } 95 | 96 | /* find # of levels to Least Common Ancestor................................*/ 97 | unsigned char HsmToLCA_(Hsm *me, State *target) { 98 | State *s, *t; 99 | unsigned char toLca = 0; 100 | if (me->source == target) { 101 | return 1; 102 | } 103 | for (s = me->source; s; ++toLca, s = s->super) { 104 | for (t = target; t; t = t->super) { 105 | if (s == t) { 106 | return toLca; 107 | } 108 | } 109 | } 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /c/hsm.h: -------------------------------------------------------------------------------- 1 | /** hsm.h -- Hierarchical State Machine interface 2 | */ 3 | #ifndef hsm_h 4 | #define hsm_h 5 | 6 | typedef int Event; 7 | typedef struct { 8 | Event evt; 9 | } Msg; 10 | 11 | typedef struct Hsm Hsm; 12 | typedef Msg const *(*EvtHndlr)(Hsm*, Msg const*); 13 | 14 | typedef struct State State; 15 | struct State { 16 | State *super; /* pointer to superstate */ 17 | EvtHndlr hndlr; /* state's handler function */ 18 | char const *name; 19 | }; 20 | 21 | void StateCtor(State *me, char const *name, State *super, EvtHndlr hndlr); 22 | #define StateOnEvent(me_, ctx_, msg_) \ 23 | (*(me_)->hndlr)((ctx_), (msg_)) 24 | 25 | struct Hsm { /* Hierarchical State Machine base class */ 26 | char const *name; /* pointer to static name */ 27 | State *curr; /* current state */ 28 | State *next; /* next state (non 0 if transition taken) */ 29 | State *source; /* source state during last transition */ 30 | State top; /* top-most state object */ 31 | }; 32 | 33 | void HsmCtor(Hsm *me, char const *name, EvtHndlr topHndlr); 34 | void HsmOnStart(Hsm *me); /* enter and start the top state */ 35 | void HsmOnEvent(Hsm *me, Msg const *msg); /* "HSM engine" */ 36 | 37 | /* protected: */ 38 | unsigned char HsmToLCA_(Hsm *me, State *target); 39 | void HsmExit_(Hsm *me, unsigned char toLca); 40 | /* get current state */ 41 | #define STATE_CURR(me_) (((Hsm *)me_)->curr) 42 | /* take start transition (no states need to be exited) */ 43 | #define STATE_START(me_, target_) (((Hsm *)me_)->next = (target_)) 44 | /* take a state transition (exit states up to the LCA) */ 45 | #define STATE_TRAN(me_, target_) if (1) { \ 46 | static unsigned char toLca_ = 0xFF; \ 47 | assert(((Hsm *)me_)->next == 0); \ 48 | if (toLca_ == 0xFF) \ 49 | toLca_ = HsmToLCA_((Hsm *)(me_), (target_)); \ 50 | HsmExit_((Hsm *)(me_), toLca_); \ 51 | ((Hsm *)(me_))->next = (target_); \ 52 | } else ((void)0) 53 | 54 | #define START_EVT ((Event)(-1)) 55 | #define ENTRY_EVT ((Event)(-2)) 56 | #define EXIT_EVT ((Event)(-3)) 57 | 58 | #endif /* hsm_h */ 59 | -------------------------------------------------------------------------------- /c/hsmtst.c: -------------------------------------------------------------------------------- 1 | /** hsmtest.c -- Hierarchical State Machine test harness. 2 | * This is an implementation of the example found in 3 | * Practical StateCharts in C/C++ by Miro Samek. 4 | * Intent of program is to exercise the state machine implementation. 5 | * An earlier implementation published in ESP August 2000 had errors 6 | * that were identified by kevin.fleming@philips.com 7 | * P. Y. Montgomery 021125 8 | */ 9 | 10 | #include 11 | #include 12 | #include "hsm.h" 13 | 14 | typedef struct HsmTest HsmTest; 15 | struct HsmTest { 16 | Hsm super; 17 | State s1; 18 | State s11; 19 | State s2; 20 | State s21; 21 | State s211; 22 | int foo; 23 | }; 24 | 25 | enum HsmTestEvents { 26 | A_SIG, B_SIG, C_SIG, D_SIG, E_SIG, F_SIG, G_SIG, H_SIG 27 | }; 28 | 29 | Msg const *HsmTest_top(HsmTest *me, Msg *msg) { 30 | switch (msg->evt) { 31 | case START_EVT: 32 | printf("top-INIT;"); 33 | STATE_START(me, &me->s1); 34 | return 0; 35 | case ENTRY_EVT: 36 | printf("top-ENTRY;"); 37 | return 0; 38 | case EXIT_EVT: 39 | printf("top-EXIT;"); 40 | return 0; 41 | case E_SIG: 42 | printf("top-E;"); 43 | STATE_TRAN(me, &me->s211); 44 | return 0; 45 | } 46 | return msg; 47 | } 48 | 49 | Msg const *HsmTest_s1(HsmTest *me, Msg *msg) { 50 | switch (msg->evt) { 51 | case START_EVT: 52 | printf("s1-INIT;"); 53 | STATE_START(me, &me->s11); 54 | return 0; 55 | case ENTRY_EVT: 56 | printf("s1-ENTRY;"); 57 | return 0; 58 | case EXIT_EVT: 59 | printf("s1-EXIT;"); 60 | return 0; 61 | case A_SIG: 62 | printf("s1-A;"); 63 | STATE_TRAN(me, &me->s1); 64 | return 0; 65 | case B_SIG: 66 | printf("s1-B;"); 67 | STATE_TRAN(me, &me->s11); 68 | return 0; 69 | case C_SIG: 70 | printf("s1-C;"); 71 | STATE_TRAN(me, &me->s2); 72 | return 0; 73 | case D_SIG: 74 | printf("s1-D;"); 75 | STATE_TRAN(me, &((Hsm *)me)->top); 76 | return 0; 77 | case F_SIG: 78 | printf("s1-F;"); 79 | STATE_TRAN(me, &me->s211); 80 | return 0; 81 | } 82 | return msg; 83 | } 84 | 85 | Msg const *HsmTest_s11(HsmTest *me, Msg *msg) { 86 | switch (msg->evt) { 87 | case ENTRY_EVT: 88 | printf("s11-ENTRY;"); 89 | return 0; 90 | case EXIT_EVT: 91 | printf("s11-EXIT;"); 92 | return 0; 93 | case G_SIG: 94 | printf("s11-G;"); 95 | STATE_TRAN(me, &me->s211); 96 | return 0; 97 | case H_SIG: 98 | if (me->foo) { 99 | printf("s11-H;"); 100 | me->foo = 0; 101 | return 0; 102 | } 103 | break; 104 | } 105 | return msg; 106 | } 107 | 108 | Msg const *HsmTest_s2(HsmTest *me, Msg *msg) { 109 | switch (msg->evt) { 110 | case START_EVT: 111 | printf("s2-INIT;"); 112 | STATE_START(me, &me->s21); 113 | return 0; 114 | case ENTRY_EVT: 115 | printf("s2-ENTRY;"); 116 | return 0; 117 | case EXIT_EVT: 118 | printf("s2-EXIT;"); 119 | return 0; 120 | case C_SIG: 121 | printf("s2-C;"); 122 | STATE_TRAN(me, &me->s1); 123 | return 0; 124 | case F_SIG: 125 | printf("s2-F;"); 126 | STATE_TRAN(me, &me->s11); 127 | return 0; 128 | } 129 | return msg; 130 | } 131 | 132 | Msg const *HsmTest_s21(HsmTest *me, Msg *msg) { 133 | switch (msg->evt) { 134 | case START_EVT: 135 | printf("s21-INIT;"); 136 | STATE_START(me, &me->s211); 137 | return 0; 138 | case ENTRY_EVT: 139 | printf("s21-ENTRY;"); 140 | return 0; 141 | case EXIT_EVT: 142 | printf("s21-EXIT;"); 143 | return 0; 144 | case B_SIG: 145 | printf("s21-B;"); 146 | STATE_TRAN(me, &me->s211); 147 | return 0; 148 | case H_SIG: 149 | if (!me->foo) { 150 | printf("s21-H;"); 151 | me->foo = 1; 152 | STATE_TRAN(me, &me->s21); 153 | return 0; 154 | } 155 | break; 156 | } 157 | return msg; 158 | } 159 | 160 | Msg const *HsmTest_s211(HsmTest *me, Msg *msg) { 161 | switch (msg->evt) { 162 | case ENTRY_EVT: 163 | printf("s211-ENTRY;"); 164 | return 0; 165 | case EXIT_EVT: 166 | printf("s211-EXIT;"); 167 | return 0; 168 | case D_SIG: 169 | printf("s211-D;"); 170 | STATE_TRAN(me, &me->s21); 171 | return 0; 172 | case G_SIG: 173 | printf("s211-G;"); 174 | STATE_TRAN(me, &((Hsm *)me)->top); 175 | return 0; 176 | } 177 | return msg; 178 | } 179 | 180 | void HsmTestCtor(HsmTest *me) { 181 | HsmCtor((Hsm *)me, "HsmTest", (EvtHndlr)HsmTest_top); 182 | StateCtor(&me->s1, "s1", &((Hsm *)me)->top, (EvtHndlr)HsmTest_s1); 183 | StateCtor(&me->s11, "s11", &me->s1, (EvtHndlr)HsmTest_s11); 184 | StateCtor(&me->s2, "s2", &((Hsm *)me)->top, (EvtHndlr)HsmTest_s2); 185 | StateCtor(&me->s21, "s21", &me->s2, (EvtHndlr)HsmTest_s21); 186 | StateCtor(&me->s211, "s211", &me->s21, (EvtHndlr)HsmTest_s211); 187 | me->foo = 0; 188 | } 189 | 190 | const Msg HsmTestMsg[] = { 191 | A_SIG,B_SIG,C_SIG,D_SIG,E_SIG,F_SIG,G_SIG,H_SIG 192 | }; 193 | 194 | int main() { 195 | HsmTest hsmTest; 196 | HsmTestCtor(&hsmTest); 197 | HsmOnStart((Hsm *)&hsmTest); 198 | for (;;) { 199 | char c; 200 | printf("\nEvent<-"); 201 | c = getc(stdin); 202 | getc(stdin); 203 | if (c < 'a' || 'h' < c) { 204 | break; 205 | } 206 | HsmOnEvent((Hsm *)&hsmTest, &HsmTestMsg[c - 'a']); 207 | } 208 | return 0; 209 | } 210 | -------------------------------------------------------------------------------- /c/watch.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple digital watch example 3 | * M. Samek, 01/07/00 4 | */ 5 | #include 6 | #include 7 | #include "hsm.h" 8 | 9 | typedef struct Watch Watch; 10 | struct Watch { 11 | Hsm super; 12 | State timekeeping, time, date; 13 | State setting, hour, minute, day, month; 14 | State *timekeepingHist; 15 | int tsec, tmin, thour, dday, dmonth; 16 | }; 17 | 18 | enum WatchEvents { 19 | Watch_DATE_EVT, 20 | Watch_SET_EVT, 21 | Watch_TICK_EVT 22 | }; 23 | 24 | void WatchShowTime(Watch *me) { 25 | printf("time: %2d:%02d:%02d", 26 | me->thour, me->tmin, me->tsec); 27 | } 28 | 29 | void WatchShowDate(Watch *me) { 30 | printf("date: %02d-%02d", me->dmonth, me->dday); 31 | } 32 | 33 | void WatchTick(Watch *me) { 34 | static int const month[] = { 35 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 36 | }; 37 | if (++me->tsec == 60) { 38 | me->tsec = 0; 39 | if (++me->tmin == 60) { 40 | me->tmin = 0; 41 | if (++me->thour == 24) { 42 | me->thour = 0; 43 | if (++me->dday == month[me->dmonth-1]+1) { 44 | me->dday = 1; 45 | if (++me->dmonth == 13) 46 | me->dmonth = 1; 47 | } 48 | } 49 | } 50 | } 51 | } 52 | 53 | Msg const *Watch_top(Watch *me, Msg *msg) { 54 | switch (msg->evt) { 55 | case START_EVT: 56 | STATE_START(me, &me->setting); 57 | return 0; 58 | case Watch_TICK_EVT: 59 | if (++me->tsec == 60) 60 | me->tsec = 0; 61 | printf("Watch::top-TICK;"); 62 | WatchShowTime(me); 63 | return 0; 64 | } 65 | return msg; 66 | } 67 | 68 | Msg const *Watch_timekeeping(Watch *me, Msg *msg) { 69 | switch (msg->evt) { 70 | case START_EVT: 71 | STATE_START(me, me->timekeepingHist); 72 | return 0; 73 | case Watch_SET_EVT: 74 | STATE_TRAN(me, &me->setting); 75 | printf("Watch::timekeeping-SET;"); 76 | return 0; 77 | case EXIT_EVT: 78 | me->timekeepingHist = STATE_CURR(me); 79 | return 0; 80 | } 81 | return msg; 82 | } 83 | 84 | Msg const *Watch_time(Watch *me, Msg *msg) { 85 | switch (msg->evt) { 86 | case ENTRY_EVT: 87 | WatchShowTime(me); 88 | return 0; 89 | case Watch_DATE_EVT: 90 | STATE_TRAN(me, &me->date); 91 | printf("Watch::time-DATE;"); 92 | return 0; 93 | case Watch_TICK_EVT: 94 | printf("Watch::time-TICK;"); 95 | WatchTick(me); 96 | WatchShowTime(me); 97 | return 0; 98 | } 99 | return msg; 100 | } 101 | 102 | Msg const *Watch_date(Watch *me, Msg *msg) { 103 | switch (msg->evt) { 104 | case ENTRY_EVT: 105 | WatchShowDate(me); 106 | return 0; 107 | case Watch_DATE_EVT: 108 | STATE_TRAN(me, &me->time); 109 | printf("Watch::date-DATE;"); 110 | return 0; 111 | case Watch_TICK_EVT: 112 | printf("Watch::date-TICK;"); 113 | WatchTick(me); 114 | WatchShowDate(me); 115 | return 0; 116 | } 117 | return msg; 118 | } 119 | 120 | Msg const *Watch_setting(Watch *me, Msg *msg) { 121 | switch (msg->evt) { 122 | case START_EVT: 123 | STATE_START(me, &me->hour); 124 | return 0; 125 | } 126 | return msg; 127 | } 128 | 129 | Msg const *Watch_hour(Watch *me, Msg *msg) { 130 | switch (msg->evt) { 131 | case Watch_SET_EVT: 132 | STATE_TRAN(me, &me->minute); 133 | printf("Watch::hour-SET;"); 134 | return 0; 135 | } 136 | return msg; 137 | } 138 | 139 | Msg const *Watch_minute(Watch *me, Msg *msg) { 140 | switch (msg->evt) { 141 | case Watch_SET_EVT: 142 | STATE_TRAN(me, &me->day); 143 | return 0; 144 | } 145 | return msg; 146 | } 147 | 148 | Msg const *Watch_day(Watch *me, Msg *msg) { 149 | switch (msg->evt) { 150 | case Watch_SET_EVT: 151 | STATE_TRAN(me, &me->month); 152 | printf("Watch::day-SET;"); 153 | return 0; 154 | } 155 | return msg; 156 | } 157 | 158 | Msg const *Watch_month(Watch *me, Msg *msg) { 159 | switch (msg->evt) { 160 | case Watch_SET_EVT: 161 | STATE_TRAN(me, &me->timekeeping); 162 | printf("Watch::month-SET;"); 163 | return 0; 164 | } 165 | return msg; 166 | } 167 | 168 | void WatchCtor(Watch *me) { 169 | HsmCtor((Hsm *)me, "Watch", (EvtHndlr)Watch_top); 170 | StateCtor(&me->timekeeping, "timekeeping", 171 | &((Hsm *)me)->top, (EvtHndlr)Watch_timekeeping); 172 | StateCtor(&me->time, "time", &me->timekeeping, 173 | (EvtHndlr)Watch_time); 174 | StateCtor(&me->date, "date", &me->timekeeping, 175 | (EvtHndlr)Watch_date); 176 | StateCtor(&me->setting, "setting", &((Hsm *)me)->top, 177 | (EvtHndlr)Watch_setting); 178 | StateCtor(&me->hour, "hour", &me->setting, 179 | (EvtHndlr)Watch_hour); 180 | StateCtor(&me->minute, "minute", &me->setting, 181 | (EvtHndlr)Watch_minute); 182 | StateCtor(&me->day, "day", &me->setting, 183 | (EvtHndlr)Watch_day); 184 | StateCtor(&me->month, "month", &me->setting, 185 | (EvtHndlr)Watch_month); 186 | 187 | me->timekeepingHist = &me->time; 188 | 189 | me->tsec = me->tmin = me->thour = 0; 190 | me->dday = me->dmonth = 1; 191 | } 192 | 193 | const Msg watchMsg[] = { 194 | Watch_DATE_EVT, 195 | Watch_SET_EVT, 196 | Watch_TICK_EVT 197 | }; 198 | 199 | int main() { 200 | Watch watch; 201 | WatchCtor(&watch); 202 | HsmOnStart((Hsm *)&watch); 203 | for (;;) { 204 | int i; 205 | printf("\nEvent<-"); 206 | scanf("%d", &i); 207 | if (i < 0 || sizeof(watchMsg)/sizeof(Msg) <= i) 208 | break; 209 | HsmOnEvent((Hsm *)&watch, &watchMsg[i]); 210 | } 211 | return 0; 212 | } 213 | -------------------------------------------------------------------------------- /cpp/hsm.cpp: -------------------------------------------------------------------------------- 1 | /** hsm.c -- Hierarchical State Machine implementation 2 | */ 3 | #include 4 | #include "hsm.h" 5 | 6 | static Msg const startMsg = { START_EVT }; 7 | static Msg const entryMsg = { ENTRY_EVT }; 8 | static Msg const exitMsg = { EXIT_EVT }; 9 | #define MAX_STATE_NESTING 8 10 | 11 | /* State Ctor...............................................................*/ 12 | State::State(char const *n, State *s, EvtHndlr h) 13 | : name(n), super(s), hndlr(h) 14 | {} 15 | 16 | /* Hsm Ctor.................................................................*/ 17 | Hsm::Hsm(char const *n, EvtHndlr topHndlr) 18 | : top("top", 0, topHndlr), name(n) 19 | {} 20 | 21 | /* enter and start the top state............................................*/ 22 | void Hsm::onStart() { 23 | curr = ⊤ 24 | next = 0; 25 | curr->onEvent(this, &entryMsg); 26 | while (curr->onEvent(this, &startMsg), next) { 27 | State *entryPath[MAX_STATE_NESTING]; 28 | register State **trace = entryPath; 29 | register State *s; 30 | *trace = 0; 31 | for (s = next; s != curr; s = s->super) { 32 | *(++trace) = s; /* trace path to target */ 33 | } 34 | while (s = *trace--) { /* retrace entry from source */ 35 | s->onEvent(this, &entryMsg); 36 | } 37 | curr = next; 38 | next = 0; 39 | } 40 | } 41 | 42 | /* state machine "engine"...................................................*/ 43 | void Hsm::onEvent(Msg const *msg) { 44 | State *entryPath[MAX_STATE_NESTING]; 45 | register State **trace; 46 | register State *s; 47 | for (s = curr; s; s = s->super) { 48 | source = s; /* level of outermost event handler */ 49 | msg = s->onEvent(this, msg); 50 | if (msg == 0) { /* processed? */ 51 | if (next) { /* state transition taken? */ 52 | trace = entryPath; 53 | *trace = 0; 54 | for (s = next; s != curr; s = s->super) { 55 | *(++trace) = s; /* trace path to target */ 56 | } 57 | while (s = *trace--) { /* retrace entry from LCA */ 58 | s->onEvent(this, &entryMsg); 59 | } 60 | curr = next; 61 | next = 0; 62 | while (curr->onEvent(this, &startMsg), next) { 63 | trace = entryPath; 64 | *trace = 0; 65 | for (s = next; s != curr; s = s->super) { 66 | *(++trace) = s; /* record path to target */ 67 | } 68 | while (s = *trace--) { /* retrace the entry */ 69 | s->onEvent(this, &entryMsg); 70 | } 71 | curr = next; 72 | next = 0; 73 | } 74 | } 75 | break; /* event processed */ 76 | } 77 | } 78 | } 79 | 80 | /* exit current states and all superstates up to LCA .......................*/ 81 | void Hsm::exit_(unsigned char toLca) { 82 | register State *s = curr; 83 | while (s != source) { 84 | s->onEvent(this, &exitMsg); 85 | s = s->super; 86 | } 87 | while (toLca--) { 88 | s->onEvent(this, &exitMsg); 89 | s = s->super; 90 | } 91 | curr = s; 92 | } 93 | 94 | /* find # of levels to Least Common Ancestor................................*/ 95 | unsigned char Hsm::toLCA_(State *target) { 96 | State *s, *t; 97 | unsigned char toLca = 0; 98 | if (source == target) { 99 | return 1; 100 | } 101 | for (s = source; s; ++toLca, s = s->super) { 102 | for (t = target; t; t = t->super) { 103 | if (s == t) { 104 | return toLca; 105 | } 106 | } 107 | } 108 | return 0; 109 | } 110 | -------------------------------------------------------------------------------- /cpp/hsm.h: -------------------------------------------------------------------------------- 1 | /** hsm.h -- Hierarchical State Machine interface 2 | */ 3 | #ifndef hsm_h 4 | #define hsm_h 5 | 6 | typedef int Event; 7 | struct Msg { 8 | Event evt; 9 | }; 10 | 11 | class Hsm; /* forward declaration */ 12 | typedef Msg const *(Hsm::*EvtHndlr)(Msg const *); 13 | 14 | class State { 15 | State *super; /* pointer to superstate */ 16 | EvtHndlr hndlr; /* state's handler function */ 17 | char const *name; 18 | public: 19 | State(char const *name, State *super, EvtHndlr hndlr); 20 | private: 21 | Msg const *onEvent(Hsm *ctx, Msg const *msg) { 22 | return (ctx->*hndlr)(msg); 23 | } 24 | friend class Hsm; 25 | }; 26 | 27 | class Hsm { /* Hierarchical State Machine base class */ 28 | char const *name; /* pointer to static name */ 29 | State *curr; /* current state */ 30 | protected: 31 | State *next; /* next state (non 0 if transition taken) */ 32 | State *source; /* source state during last transition */ 33 | State top; /* top-most state object */ 34 | public: 35 | Hsm(char const *name, EvtHndlr topHndlr); /* Ctor */ 36 | void onStart(); /* enter and start the top state */ 37 | void onEvent(Msg const *msg); /* "state machine engine" */ 38 | protected: 39 | unsigned char toLCA_(State *target); 40 | void exit_(unsigned char toLca); 41 | State *STATE_CURR() { return curr; } 42 | void STATE_START(State *target) { 43 | assert(next == 0); 44 | next = target; 45 | } 46 | # define STATE_TRAN(target_) if (1) { \ 47 | static unsigned char toLca_ = 0xFF; \ 48 | assert(next == 0); \ 49 | if (toLca_ == 0xFF) \ 50 | toLca_ = toLCA_(target_); \ 51 | exit_(toLca_); \ 52 | next = (target_); \ 53 | } else ((void)0) 54 | }; 55 | 56 | #define START_EVT ((Event)(-1)) 57 | #define ENTRY_EVT ((Event)(-2)) 58 | #define EXIT_EVT ((Event)(-3)) 59 | 60 | #endif /* hsm_h */ 61 | -------------------------------------------------------------------------------- /cpp/hsmtst.cpp: -------------------------------------------------------------------------------- 1 | /** hsmtest.c -- Hierarchical State Machine test harness. 2 | * This is an implementation of the example found in 3 | * Practical StateCharts in C/C++ by Miro Samek. 4 | * Intent of program is to exercise the state machine implementation. 5 | * An earlier implementation published in ESP August 2000 had errors 6 | * that were identified by kevin.fleming@philips.com 7 | * P. Y. Montgomery 021125 8 | */ 9 | 10 | #include 11 | #include 12 | #include "hsm.h" 13 | 14 | class HsmTest : public Hsm { 15 | int myFoo; 16 | protected: 17 | State s1; 18 | State s11; 19 | State s2; 20 | State s21; 21 | State s211; 22 | public: 23 | HsmTest(); 24 | Msg const *topHndlr(Msg const *msg); 25 | Msg const *s1Hndlr(Msg const *msg); 26 | Msg const *s11Hndlr(Msg const *msg); 27 | Msg const *s2Hndlr(Msg const *msg); 28 | Msg const *s21Hndlr(Msg const *msg); 29 | Msg const *s211Hndlr(Msg const *msg); 30 | }; 31 | 32 | enum HsmTestEvents { 33 | A_SIG, B_SIG, C_SIG, D_SIG, E_SIG, F_SIG, G_SIG, H_SIG 34 | }; 35 | 36 | Msg const *HsmTest::topHndlr(Msg const *msg) { 37 | switch (msg->evt) { 38 | case START_EVT: 39 | printf("top-INIT;"); 40 | STATE_START(&s1); 41 | return 0; 42 | case ENTRY_EVT: 43 | printf("top-ENTRY;"); 44 | return 0; 45 | case EXIT_EVT: 46 | printf("top-EXIT;"); 47 | return 0; 48 | case E_SIG: 49 | printf("top-E;"); 50 | STATE_TRAN(&s211); 51 | return 0; 52 | } 53 | return msg; 54 | } 55 | 56 | Msg const *HsmTest::s1Hndlr(Msg const *msg) { 57 | switch (msg->evt) { 58 | case START_EVT: 59 | printf("s1-INIT;"); 60 | STATE_START(&s11); 61 | return 0; 62 | case ENTRY_EVT: 63 | printf("s1-ENTRY;"); 64 | return 0; 65 | case EXIT_EVT: 66 | printf("s1-EXIT;"); 67 | return 0; 68 | case A_SIG: 69 | printf("s1-A;"); 70 | STATE_TRAN(&s1); 71 | return 0; 72 | case B_SIG: 73 | printf("s1-B;"); 74 | STATE_TRAN(&s11); 75 | return 0; 76 | case C_SIG: 77 | printf("s1-C;"); 78 | STATE_TRAN(&s2); 79 | return 0; 80 | case D_SIG: 81 | printf("s1-D;"); 82 | STATE_TRAN(&top); 83 | return 0; 84 | case F_SIG: 85 | printf("s1-F;"); 86 | STATE_TRAN(&s211); 87 | return 0; 88 | } 89 | return msg; 90 | } 91 | 92 | Msg const *HsmTest::s11Hndlr(Msg const *msg) { 93 | switch (msg->evt) { 94 | case ENTRY_EVT: 95 | printf("s11-ENTRY;"); 96 | return 0; 97 | case EXIT_EVT: 98 | printf("s11-EXIT;"); 99 | return 0; 100 | case G_SIG: 101 | printf("s11-G;"); 102 | STATE_TRAN(&s211); 103 | return 0; 104 | case H_SIG: 105 | if (myFoo) { 106 | printf("s11-H;"); 107 | myFoo = 0; 108 | return 0; 109 | } 110 | break; 111 | } 112 | return msg; 113 | } 114 | 115 | Msg const *HsmTest::s2Hndlr(Msg const *msg) { 116 | switch (msg->evt) { 117 | case START_EVT: 118 | printf("s2-INIT;"); 119 | STATE_START(&s21); 120 | return 0; 121 | case ENTRY_EVT: 122 | printf("s2-ENTRY;"); 123 | return 0; 124 | case EXIT_EVT: 125 | printf("s2-EXIT;"); 126 | return 0; 127 | case C_SIG: 128 | printf("s2-C;"); 129 | STATE_TRAN(&s1); 130 | return 0; 131 | case F_SIG: 132 | printf("s2-F;"); 133 | STATE_TRAN(&s11); 134 | return 0; 135 | } 136 | return msg; 137 | } 138 | 139 | Msg const *HsmTest::s21Hndlr(Msg const *msg) { 140 | switch (msg->evt) { 141 | case START_EVT: 142 | printf("s21-INIT;"); 143 | STATE_START(&s211); 144 | return 0; 145 | case ENTRY_EVT: 146 | printf("s21-ENTRY;"); 147 | return 0; 148 | case EXIT_EVT: 149 | printf("s21-EXIT;"); 150 | return 0; 151 | case B_SIG: 152 | printf("s21-B;"); 153 | STATE_TRAN(&s211); 154 | return 0; 155 | case H_SIG: 156 | if (!myFoo) { 157 | printf("s21-H;"); 158 | myFoo = 1; 159 | STATE_TRAN(&s21); 160 | return 0; 161 | } 162 | break; 163 | } 164 | return msg; 165 | } 166 | 167 | Msg const *HsmTest::s211Hndlr(Msg const *msg) { 168 | switch (msg->evt) { 169 | case ENTRY_EVT: 170 | printf("s211-ENTRY;"); 171 | return 0; 172 | case EXIT_EVT: 173 | printf("s211-EXIT;"); 174 | return 0; 175 | case D_SIG: 176 | printf("s211-D;"); 177 | STATE_TRAN(&s21); 178 | return 0; 179 | case G_SIG: 180 | printf("s211-G;"); 181 | STATE_TRAN(&top); 182 | return 0; 183 | } 184 | return msg; 185 | } 186 | 187 | HsmTest::HsmTest() 188 | : Hsm("HsmTest", (EvtHndlr)topHndlr), 189 | s1("s1", &top, (EvtHndlr)&HsmTest::s1Hndlr), 190 | s11("s11", &s1, (EvtHndlr)&HsmTest::s11Hndlr), 191 | s2("s2", &top, (EvtHndlr)&HsmTest::s2Hndlr), 192 | s21("s21", &s2, (EvtHndlr)&HsmTest::s21Hndlr), 193 | s211("s211", &s21, (EvtHndlr)&HsmTest::s211Hndlr) 194 | { 195 | myFoo = 0; 196 | } 197 | 198 | const Msg HsmTestMsg[] = { 199 | A_SIG,B_SIG,C_SIG,D_SIG,E_SIG,F_SIG,G_SIG,H_SIG 200 | }; 201 | 202 | int main() { 203 | HsmTest hsmTest; 204 | hsmTest.onStart(); 205 | for (;;) { 206 | char c; 207 | printf("\nEvent<-"); 208 | c = getc(stdin); 209 | getc(stdin); 210 | if (c < 'a' || 'h' < c) { 211 | break; 212 | } 213 | hsmTest.onEvent(&HsmTestMsg[c - 'a']); 214 | } 215 | return 0; 216 | } 217 | -------------------------------------------------------------------------------- /cpp/watch.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple digital watch example 3 | * M. Samek, 01/07/00 4 | */ 5 | #include 6 | #include 7 | #include "hsm.h" 8 | 9 | class Watch : public Hsm { 10 | int tsec, tmin, thour, dday, dmonth; 11 | protected: 12 | State timekeeping, time, date; 13 | State setting, hour, minute, day, month; 14 | State *timekeepingHist; 15 | public: 16 | Watch(); 17 | Msg const *topHndlr(Msg const *msg); 18 | Msg const *timekeepingHndlr(Msg const *msg); 19 | Msg const *timeHndlr(Msg const *msg); 20 | Msg const *dateHndlr(Msg const *msg); 21 | Msg const *settingHndlr(Msg const *msg); 22 | Msg const *hourHndlr(Msg const *msg); 23 | Msg const *minuteHndlr(Msg const *msg); 24 | Msg const *dayHndlr(Msg const *msg); 25 | Msg const *monthHndlr(Msg const *msg); 26 | void tick(); 27 | void showTime(); 28 | void showDate(); 29 | }; 30 | 31 | enum WatchEvents { 32 | Watch_DATE_EVT, 33 | Watch_SET_EVT, 34 | Watch_TICK_EVT 35 | }; 36 | 37 | void Watch::showTime() { 38 | printf("time: %2d:%02d:%02d", thour, tmin, tsec); 39 | } 40 | 41 | void Watch::showDate() { 42 | printf("date: %02d-%02d", dmonth, dday); 43 | } 44 | 45 | void Watch::tick() { 46 | static int const month[] = { 47 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 48 | }; 49 | if (++tsec == 60) { 50 | tsec = 0; 51 | if (++tmin == 60) { 52 | tmin = 0; 53 | if (++thour == 24) { 54 | thour = 0; 55 | if (++dday == month[dmonth-1]+1) { 56 | dday = 1; 57 | if (++dmonth == 13) 58 | dmonth = 1; 59 | } 60 | } 61 | } 62 | } 63 | } 64 | 65 | Msg const *Watch::topHndlr(Msg const *msg) { 66 | switch (msg->evt) { 67 | case START_EVT: 68 | STATE_START(&setting); 69 | return 0; 70 | case Watch_TICK_EVT: 71 | if (++tsec == 60) 72 | tsec = 0; 73 | printf("Watch::top-TICK;"); 74 | showTime(); 75 | return 0; 76 | } 77 | return msg; 78 | } 79 | 80 | Msg const *Watch::timekeepingHndlr(Msg const *msg) { 81 | switch (msg->evt) { 82 | case START_EVT: 83 | STATE_START(timekeepingHist); 84 | return 0; 85 | case Watch_SET_EVT: 86 | STATE_TRAN(&setting); 87 | printf("Watch::timekeeping-SET;"); 88 | return 0; 89 | } 90 | return msg; 91 | } 92 | 93 | Msg const *Watch::timeHndlr(Msg const *msg) { 94 | switch (msg->evt) { 95 | case ENTRY_EVT: 96 | showTime(); 97 | return 0; 98 | case Watch_DATE_EVT: 99 | STATE_TRAN(&date); 100 | printf("Watch::time-DATE;"); 101 | return 0; 102 | case Watch_TICK_EVT: 103 | printf("Watch::time-TICK;"); 104 | tick(); 105 | showTime(); 106 | return 0; 107 | } 108 | return msg; 109 | } 110 | 111 | Msg const *Watch::dateHndlr(Msg const *msg) { 112 | switch (msg->evt) { 113 | case ENTRY_EVT: 114 | showDate(); 115 | return 0; 116 | case Watch_DATE_EVT: 117 | STATE_TRAN(&time); 118 | printf("Watch::date-DATE;"); 119 | return 0; 120 | case Watch_TICK_EVT: 121 | printf("Watch::date-TICK;"); 122 | tick(); 123 | showDate(); 124 | return 0; 125 | } 126 | return msg; 127 | } 128 | 129 | Msg const *Watch::settingHndlr(Msg const *msg) { 130 | switch (msg->evt) { 131 | case START_EVT: 132 | STATE_START(&hour); 133 | return 0; 134 | } 135 | return msg; 136 | } 137 | 138 | Msg const *Watch::hourHndlr(Msg const *msg) { 139 | switch (msg->evt) { 140 | case Watch_SET_EVT: 141 | STATE_TRAN(&minute); 142 | printf("Watch::hour-SET;"); 143 | return 0; 144 | } 145 | return msg; 146 | } 147 | 148 | Msg const *Watch::minuteHndlr(Msg const *msg) { 149 | switch (msg->evt) { 150 | case Watch_SET_EVT: 151 | STATE_TRAN(&day); 152 | return 0; 153 | } 154 | return msg; 155 | } 156 | 157 | Msg const *Watch::dayHndlr(Msg const *msg) { 158 | switch (msg->evt) { 159 | case Watch_SET_EVT: 160 | STATE_TRAN(&month); 161 | printf("Watch::day-SET;"); 162 | return 0; 163 | } 164 | return msg; 165 | } 166 | 167 | Msg const *Watch::monthHndlr(Msg const *msg) { 168 | switch (msg->evt) { 169 | case Watch_SET_EVT: 170 | STATE_TRAN(&timekeeping); 171 | printf("Watch::month-SET;"); 172 | return 0; 173 | } 174 | return msg; 175 | } 176 | 177 | Watch::Watch() 178 | : Hsm("Watch", (EvtHndlr)topHndlr), 179 | timekeeping("timekeeping", &top, 180 | (EvtHndlr)&Watch::timekeepingHndlr), 181 | time("time", &timekeeping, (EvtHndlr)&Watch::timeHndlr), 182 | date("date", &timekeeping, (EvtHndlr)&Watch::dateHndlr), 183 | setting("setting", &top, (EvtHndlr)&Watch::settingHndlr), 184 | hour("hour", &setting, (EvtHndlr)&Watch::hourHndlr), 185 | minute("minute", &setting, (EvtHndlr)&Watch::minuteHndlr), 186 | day("day", &setting, (EvtHndlr)&Watch::dayHndlr), 187 | month("month", &setting, (EvtHndlr)&Watch::monthHndlr), 188 | tsec(0), tmin(0), thour(0), dday(1), dmonth(1) 189 | { 190 | timekeepingHist = &time; 191 | } 192 | 193 | const Msg watchMsg[] = { 194 | Watch_DATE_EVT, 195 | Watch_SET_EVT, 196 | Watch_TICK_EVT 197 | }; 198 | 199 | int main() { 200 | Watch watch; 201 | watch.onStart(); 202 | for (;;) { 203 | int i; 204 | printf("\nEvent<-"); 205 | scanf("%d", &i); 206 | if (i < 0 || sizeof(watchMsg)/sizeof(Msg) <= i) 207 | break; 208 | watch.onEvent(&watchMsg[i]); 209 | } 210 | return 0; 211 | } 212 | -------------------------------------------------------------------------------- /samek0008.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MadsAndreasen/HierarchicalStateMachine/211f67d8835c2cbd2955680fc9f6b014fba8d771/samek0008.pdf --------------------------------------------------------------------------------