0% found this document useful (0 votes)
275 views8 pages

State Machine Design in C++

State Machine Design in C++

Uploaded by

vlad
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF or read online on Scribd
0% found this document useful (0 votes)
275 views8 pages

State Machine Design in C++

State Machine Design in C++

Uploaded by

vlad
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF or read online on Scribd
1B] A Bo. 8 8) rtrd State Machine Design in C++ Its not all that hard to implement a finite-state machine, unless its very large, and you have to worry about multithreading, and . May 01,2000 [URL hw cr. comennlsale machine deson-e-lTBA40%236 ‘A.common design technique in the repertoire of most programmers isthe venerable state machine, Designers use this programming construct to break ‘complex problems into manageable states and state eansitions. There are innumerable ways to implement a state machine, Some ae simple and elegant, folhers are more complex bu offer inereased eror checking and Dexibility. ‘A switch statoment provides one ofthe easiest to implement and most common version of state machine. Here, cach case within the switch statement becomes a state, implemented something ike: suiteh(currentstate) 11 do sorething in the idle stave breaks case’ Stst0: 7/ a3 sorething in the stop state breaks wee > ‘This method i cesinly appropriate fo solving many different design problems, When employed on an even driven, multithreaded project, however, sale machines ofthis form ean be quite limiting “The first problem revolves around controlling what state ranstions are valid and which ones are invalid. There is no wey to enforce the stte transition rules. Any transition is allowed at any time, which is not pertculrly desirable. For most designs only afew transition patterns ae valid. Keay the software design should enforce these predefined sate sequences and prevent the unwanted transitions, Another problem arises when trying to send data to specific stave. Since the entire state machine is located within a single function, sending additional dats to any given sate proves difficult. And lastly these designs are rarely suitable for use in a mulltheaded system. The designer must ensure the state machine is called froma single thread of contol, ‘This article explores state machine design and implements a particular one using C~*, The particular implementation solves the aforementioned problems by including support for bo internal and extemal events, event dat, and state transition validation. I is multhread-sfe. Using this simple sate machine base class, a programmer can easly employ state machines ona system-wide bassin a uniform and thread-safe manner. Why Use a State Maching Implementing code using a state machines isan extremely handy design technique for solving complex engineering problems. State machines break down the design into a series of steps, or what are called states in sate-machine ling. Each sate performs some narrowly defined task. Events, on the other Jang ae the timull which cause the stale machine to move, or transition, between states To take a simple example, which [will use throughout this acl, les say we are designing motor-contol software. We want to start and stop the motor, as well as change the moto’ speed. Simple enough. The ‘motor contol evens to be exposed to the elient software will be a follows: 1. Set Speed — sets the motor going at a specific speed 2. Halt — stops the motor. ‘These events provide the ability to start the motor at whatever speed desired, which aso implies changing the speed of an already moving motor. Or we can stop the motor altogether. To the motor-control class, these two events, or function, are considered extemal events. To a cient using our code, however, these are just plain fanetions within a elas, Thats how we want it— the client blissflly unaware of the aca implementation “These events are not state mechine states The steps required to handle these two events are differen. In this case the states ae: 1. Tle — the motor isnot spinning bu is at rest. + Do nothing 2, Sart —starts the motor from a dead stop ‘+ Turn ox motor power. + Set motor speed 3. Change Speed — adjust the speed ofan already moving motor. ‘+ Change motor speed, 4, Stop — stop a moving motor + Tum off motor power + Goto the idle state, Bach state cates out afew specific tasks. The Start state stars the motor by fist tuming onthe power, then adjusting the speed. When changing the speed of an already moving motor, we don't need totum the power on (its aleady on) so we just change the speed. To stop the motor we turn off the power and transition to the Te state aaiting another command. Therefor, by breaking the motor consol into discret states, as opposed to having one ‘monolithic function, we can mors easily manage the rules of how to operate the motor. ‘To graphically illustrate the states and events, we can use a state diagram. Figure 1 shows the state transitions forthe motor contol class. A box denotes state anda connecting arrow indicates the event transitions. Arrows with the event name listed are external events, whereas unadorned lines are considered internal evens. (I cover the differences between internal and extemal events later in the article.) ‘Sesveed sre 1: State transitions for the motor control class ‘As you can se, when an event comes in the state tasition that oceurs depends on state machine's curent state, When a setSpeed event comes in, for instance, and the motor is i the Tate stat, i transitions to the start state. However, tha same SetSpeed event generated while the current state is start ‘transitions the motor to the ChangeSpeed sat. You can also so that not li sate transitions are valid. For instance, the motor can ansition from ‘angespees to Tale without fist going through the stop stat. In short using a state machine captures and enforces complex interactions which might atherwise be difficult to convey and implement. ‘Nomenclature [Now that Ive touched on some state machine design isues and nomenclature, I want to clarify some ofthe more important attributes of a state machine Every state machine has the concept ofa "current state." This isthe sae the state machine curently oesupies. At any given moment i time, the state machine canbe in only a single sate. Every instance of particular state machine class will ave the same originating state. That origination state, however, des not execute during object eatin, Only an event sent o the state machine causes a state function fo execute, "State fonctions" implement each sate — one state function per state-machine state, Ia thi state-function signatures, which ae as follows implementation al state functions must adhere to one of two vod and efune> are the particular class and function name respectively. For example, you might choose signatures such as wok ryclase:St_Fune(voie). The important thing here is tat the fonction return no dats (has avoid etum type) and that it has al most one input argument of| type Eventostas (or a derived class thereof). The Cventoata pointer can designate an abject of a class that drives from EventOata, Deriving event data ‘fom the eventoata class allows the siate-machine engine to delete the data once it has been used. “The state functions never retum a value. There is no concept ofretuming an error code when a sate function executes because the state machine is designed to handle the event at any time, Therefore, a sate machine must always be ready to accept events Internal and External Events ‘As mentioned earlier, an event isthe stimulus that causes a state machine to transition between sate, For instance, a button press could be an event. Events ean be broken out int two categories: external and intemal. The extemal event, tits most basic level, sa function call ino a state-machine ‘object. These functions are public snd are called fom the outside, or from code external othe state-machine object, Any thread or task within a system ‘an generate an extemal event. If the external event Function call causes a state transition to accu, the sate will execute synchronously within the caller's thread of control. An internal event, on the other hand, is seli-generated by the state machine itself during state execution. A typical scenario consists of an exteral event being generated, which, again, boils down toa function call nt the class's public iterface. Based upon ‘the event being generated and the state machine's eurent sate, a lookup is performed to determine ia transition is required. If so, the state machine transitions to the new state and the code for that state executes. AL the end of the state function, a ebeck is performed wo determine whether an internal {vent was generated, Iso, another transition is performed andthe new state ets a chance to execute. This process continues until the state machine is no Tonger generating intemal events, at whieh time the original extemal event function call returns. The external event and all internal events, ifany, execute ‘within he callers thread of contrl ‘Once the extemal event stars the state machine executing, it cannot be interrupted by another extemal event until the external event and all internal events have completed execution. This provides a mulithread-safe environment forthe state transitions. Semaphores or mutexes canbe usod in the state machine engine to block other threads that might be trying tbe simultancously access the same object, Event Data ‘When an event is generated it can optionally attach event data to be used by te sate during execution. Once the sate has completed execution, the event data is considered used up and mast be delet, Therefore, any event data sent fo a state machine mist be created onthe heap via operator new 50 that the state machine an delete it once wed. Tn edition, for our particular implementation the event data must inherit from the Eventbata base elas (see Listing One). This gives the state machine engine a common base class for which to delet all event data, Listing One: The EventData class ‘ifndef event DATA. ‘deine EVENTDRTACH Glass Evertoata virtual ~eventoatad) 05 fendse //event_oaTAM ‘Creating event data on the heap may seem lke @ needless step, but it allows a pointer tothe event data o travel through operating system message queues ntl it arives ais destination. AL that point, the daa wil be used by the stale machine and subsequently deleted. This eliminates the need to send the entire data stricture through the queue when just a pointer will do, Is aso another reason shat event function calls donot return dat, such asa status ‘ode. When the event finally aries at its destination, the call being made may have been intited from a diferent ask, or even a diferent processor, such that asynchronous return cade will have no meaning to the calling thread, State Transitions ‘When an extemal event i generated, a lookup is performed to determine the state transition course of action. There ave thee possible outcomes to an event: new sate, event ignored, o cannot happen. A new sate causes a transition to a new state where its allowed to execute. Transitions to the existing state are alo possible, which means the curent state is e-executed. Fr an ignored event, no state executes. However, the event daa, i any, i deleted ‘The last possiblity, cannot happen, i reserved for situations where the event isnot valid given the eureat sate ofthe state machine, If his occurs, the software faults, In this implementation, internal events are not required to perform a validating transition Jookup. The sate transition i assumed to be valid, You could check for both valid intemal and external event transitions, but in practice this ust takes more storage space and generates busywork for very litle bene ‘The real need for validating transitions Ties inthe synchronous, extemal events where a client ean cause an event to occur at a inappropriate time, Once the state machine is execuling, i canaot be interupled. Iie under the control of he clase’ private implementation, thereby making transition checks "unnecessary: This gives the designer the freedoms to change states, via internal evens, without the burden of updating transition tables. State Machine Implementation Two base classes are necessary to use a sate machine object: Statetachine and cventoata, A class inherits from Statetachine to obtain the necessary mechanisms to support sate transitions and event handling, The stateHacnine class alsa contains various preprocessor macros to ease implementation of| the state machin. To send data structures or classes tothe state funetions, the structure must inherit fom the Eventoata base class | fist present look atthe intemals ofthe Statetaenine clas, Then I show how to use it correctly, statesachine isthe hase class used for handling state ‘wansitions (se Listings Two and Three). Any clas implemented asa state machine inberits from ths class. The interface is contained within three fanetions: void ExternalEvent(unsigned char, FventDatat = WIL) vod rnternalevent(unsigned char, Evereoatar = NLL) Vintual const Statestruce® cetstatemsa0) = 8 isting Two: Defines base class for state machines ‘indo STATE MACHINE 4 fedatine STATERACHENEDS ‘include “Eventbata. Uf base class for state machines ‘ pubite: ‘Statevachine(int naxseates); ‘intent sstaterachineG) () protectea fsrun { EVENT TGWORED ~ OxFE, OMNOT HAPPEN 5 Unsigned char curreneseates oie! bxternaicvent(wisigned char, EventDatat = NULL): ‘oid Internalevent (unsigned char, EventDatar ~ NULL): virtual const statestruct™ Getststetap() = 8; private: ‘Const int _saxStates; bool _evertsenerated; Eventutar _peventoata; vote stateehgine(voia); typoder void (Statedachine:stateFune) Eventbata #5 statetune pstateFune; » ‘define BEIK_STATE HAP \ pubic: Const Statestruct® Getstatekap() (\ ‘static const stateservct statenapl] = ‘dine STATE MP _ENTRVCentey)\ {Ueinterpret_easteseaterume> (entry) )y AHdefEng EAD_STATE MAP \ (Creincerpret_eaetestaterunc>(WLL) )\ BN Fetten astatenep[@]5 AtdefEne SEGIN_TRANSITION HP static const unsigned char TRANSITIONS[T = (\ AideFEne TRANSITION MAP_eNTAYCentry)\ AideFEne ENO_TRANSETION, MAO (data) \ eo: Externalevent(TRANsiTIONs[currentstate], data); endif //STATE_PACHINE A List 1g Three: Implements StateMachine class sinetude cassert-hy ‘ineluge “Seaterachane.h” Statotochine::Statetachine( int maxstates) anaestates(raystetes), Gineeneseateta), receneratedt alse), TpeversDaraqwitt) < } 11 generates an external event. called ence por external event U1 fo'stare the state nacnine executing ‘od stetamachine:sexternalevent(ursighed char neaState, ventosta™ posts) ‘ 1/3 we ave supposed to ignore this event EF (nentate == EVENT TenoKeD) { 71 just delete tne event cata, if any 4 (poata) delete posta; » ase ( U1 generate the event and execute the state engine Irceralevert(nenstate, pO3"3); ‘statetngine(): > > UJ generates an internal event. catted fro 11 Einetion to tranestion toa new state ‘roid Statemacnine:Taternalevent (ursigned char newState, ventoata” poata) vsthin a state _pfventbata = posta; TeversGeneratee = t¢5 1/ the state engine executes the state machine states osd stataiachinesistatetngine (vote) « 5 11 18 ~ dock semaphore here Uf ite events are being generated Keep executing states hale CoventGenerated) oakatenp = -peventOata; 7 copy of event data pointer peventData = NLL; // event dota used up, reset ptr Teventcenenated = False; // event used up, reset #05 assert(currentstate < naxstates); 11 execute the state passing in event deta, if any Const statestruct" pstatemap = cetstaterap(); (ihis-stpstatota[currentstate) pstateFune)(e0ataTenp); 11 Af event cata was used, then delete it {Se (gostatenp) delete poocaterp; poatatenp = NULL; > } 11 To ~ uniock senaphoce here ? [ExternalE5vent generates an external event to the sate machine using as arguments the new state anda pointer to an Eventoata object, ifany. The InternalEvent function generates internal events using the same set of arguments, The GetStatevsp function retumns an array of statefunction pointers ‘which willbe retrieved by the sate engine when appropriate. This function must be implemented bythe inheriting class sine itis pure virtual. However, :macros are provided to implement this function for us, as Iwill demonstrate shorty StateMachine Usage StateMachine is used as an inherited base class. The notor class i an example of how to use it (se Listings Four and Five). Yotor implements our Ibypothetical motor-contol state machine, where clients can start the motor, at & specific spesd, and stop the motor. The SetSpeed and int public Tunetions are the extemal events into the otor sate machine, Note that to the caller an extemal event is just function eal. Te state machine {implementation details are hidden from the cliem’ view ofthis class. Setspeed takes event data, as evidenced by the pointer tothe ntonbata structure, ‘whieh contains the motor speed. This data structure willbe deleted upon completion of the state processing o ts imperative thatthe structure inerit {rom tventData and be created using operator new before the function cal s made Listing Four: Defines Motor state machine class #5indef nor08 4 ‘define MOTOR ‘include "seaekachine.t" UY stoucture to hold event data passed into state achine Steet Motoroata's public eventoote nt speeds » Uf the wotor state machine class ‘lass motor = pubite Statekachine ‘ public: otor() + Statetachine(ST_MaK STATES) <) 17 external events taken by this state machine foie mares oid SetSpeed(Motorostat); private! 71 state machine state functions ‘oie st-tale() ose stost090): oie SIastart(otoroatat): Voss StLchangespeed(Wotorbata’); 11 state nap to ‘STATE.MaP TRY (ST_1dle) Statt_pap_eurav{stostop) STATE_MaP_enTRY(ST-stare) ine state function order ‘STATE_paP ENTRY (ST_changespeed) c_ state pa 11 state enumeration order must match the order of state 1) method entries in She state TP ee able 6 x ED, & fendey 1100108 Listing Five: Implements Motor class ‘ineluge *hotor-A 11 ant motor external event ‘oid Motor: aletoose) « 11 piven the Watt event, transition to now state based upon U1 Bre current. state of the state nachine BEGIN TRANSTTZON HAP 11 = currant state TRANSITION WaP-ENTAY (EVENT_TenoReD) // ST_Idle ToansrizOn_pap_ewiay (CaMOT HAPPEN) /7 SITStOP Taansizon_pae_ewtaY (St-ST0P) U1 sistant Teak TZ0n-pae~evTaY (SILSTOP) 11 Si-enangespees e_ TRANSITION MAPCWULL) UV set motor speed external event ‘roid Motors setspecd(Metordata posta) THBNSITEON MAP ENTRY (SI_START) Jf SIIale . Teas TLOWoaerentay (CAIMOTAHAPPEN) 77 SITStOP TaaNsITION_waP_esTaY (Sr_CaNNce_SPEED)// STstart TaaNsiTZOW-Wae~ewTRY {SI_CARNGE_SPEED)// SIchangespeed n_TANSITTOLMAP(pD3t3) 11 state nacnine sits hore shen notor As not running oid moter! 51 Edie) < } 11 stop the rotor ‘ose nator :51-St0p0) « 11 perform the stop motor processing here ij Reansition to st tle via an incernal. evert Snvernalevent(St_i002);, > 11 start the motor going ‘sd NotorssiStart(Hetoroatat posta) 11 set inition motor speed processing here ? 11 changes the rotor speed once the motor 1s moving ‘old Motor: :51_Changespeed(hatordats™ posta) 11 perform the change nator speed to poata-repeed here ‘Whoa the Hotor elas is created, its intial stat is ST_tte. The first call to Set Speed transitions the state machine tothe St_star® state, where the motor is initially se into motion, Subsequent sevspeed events transition tothe st_changespeed state, where the speed ofan already moving motor is adjusted. The ait event transitions to ST_stop, where, during state execution, an internal even is genericd to transition back to the St_saie state ‘The state-machine engine knows which state function to call by using the state map, The state map maps the currentstate varable toa specific state ‘unetion. For instance, if currentstate is 2, then the tir state-map function pointer entry will be called (counting (rom zero). The state map is ereated using these thece macros: aon STATE pa ‘zn, STATE_ya® starts the stale map sequence. Each STATE_fao_en7RY that follows has as an argument a slate function, whichis added tothe sate map, xp_STATE_pa? terminates the map. The completed state map jut implements the pure virtual function Geeseatekap dafined within the Ststemachine base class, Now the Statevachine base class ca ask fr all the state Function pointes Va this eal [Notice that we have to use the dastardly reinterpret_cast> operator within the STATE_M4P_eNTRY macro to cast the derived clas member function pointer {oa statevachine member function pointer. I is necessary to perform this upeast since the Statehacnine base class has 20 idea what the derived classi So, it is imperative tht the entries provided to STATz_taP_ENTRY are really member functions of an inheriting class and that they conform tothe state unetion signature discussed eater. Otherwise bad things will happen, “Each state function must have an enumeration associated with it. These enumeration are used to store the current state of the state machine. In Hator, «states provides these enumerations, which are used later in the transition map. Is important thatthe enumeration order matches the order provided Within the state map. This way, we can tie a state enumeration to a particular state function call EVENT_ToNoRED and cAMWOT_MAPPEN are two other constants ‘used in conjunction with these state enumerations.EveNT_TGIORED tells the state engine not to execute any state, just returs and do nothing, CANNOT. HAPPEN fells the state engine to ful. This is an abnormal catastrophic fllure condition that is never suppose to occu “The last detail to altend to ere the state transition rules. How does the state machine know whet transitions should occur? The answer isthe transition map, ‘whieh is eeated using these macros: 4, TRANSITION HAP Exp. TRANSITION [Each enteral even function has transition map. BEGIN_TRANSLTZON, MAP starts the map. Each TRANSITZON_MaP_eNTRY tat follows indicates whet the state machine should do based upon the current state, The umber of entees in each tansition map must match the number of state functions exactly. In our ‘example we have four state functions, so we need four entries. The location of each entry matches the order of state functions defined within the state ‘ap. Thus, the first entry within the Hate funetion indicates an Event_1GAo2eD. This is interpreted to mean, "Ifa Halt event occurs while the current state is Sale Ie, just ignore the eves." Similarly, the thied entry means, "L'a Halt event occurs while current is state Start, thea transition to slate Stop.” ENO_TRANGITION_M4P terminates the map. The argument to this end macro is the event dat, if any. vat has no event data so the argument is a nll pointer, Dut enangespeed has data soit is pessed in here “The transition map is basically a lookup table indexed by the currentstate variable to determine the course of action, Ths information is passed tothe xternalevent fuetion as an argument slong with the event data. When the Statetngine Function exceutes, it looks up the correct state function pointer by calling Geeseatenap: pstatetap = etStaterep(); “Then, based upon the curventstate variable, it calls one of the state functions inthe map: (this->epstatenplcurrentstate].pstateFunc) (pbstsTeng) ‘After the state funetion has a chance to execute it deletes the event data, iTany, before checking to see if any intemal events were generated Generating Events At this point we have a working state machine class. Let's sce how to generate evens to it, An extemal event is generated by ercatng the event data structure on th heap using ne, assigning the structure member variables, and calling the exteral event Function. Obviously, if te exteral event doesnt fake event data then a data structure isnot created, The following code fragment shows how a synchronous calle made. You could, ofcourse, send the pointe to the data structure along with some means of identifying the even, through an operating system message queue tobe hanled asynchronously by the destination tsk: otordatat posta Doata-sspeed = 3 fotor.setSpeed(pData)s ‘To generate an intemal event ftom within a state function, call Internalevent. Ifthe destination doesn't accept event data, then the Function is called with conly the state you want to transition te: Internalevent(St_2018) In the example above, once the state funetion completes execution the state machine will transition tothe St_tdle state If, on the other hand, event data needs tobe fen tothe destination stat, then the data structure needs tobe crested onthe heap and passed in as an argument Potorbatat pbata = new Motorbata: poate-ospeed = 1 nternalfvent(sT UNE SPCED, pata); Multthread Safety ‘To prevent preemption by another tread when the state machine is nthe process af execution, the StateMachine clas uses semaphores within the statetngine function. Before the external event is allowed fo execute, the semaphore is locked. When the exierua event and all internal events have been processed, the semaphore is unlocked, allowing another external even to enter the state machine instance. ‘Comments indicate where the semaphore lock and unlock should be placed ifthe application is multithreaded. Note that cach Staterachéne object should have ils own instance of a semaphore. This prevents a singe instance from locking & semaphore and preveating all ober Statetachine objects fora exeeuting. Benefits Timplementng a state machine using this method as opposed tothe old switch statement style may seem lke extea effort, However, the payosTis in a more robust design that is capable of being employed uniformly over an entre multithreaded system, Having each sate in is own function provides easier reading than a single huge switch statement, and allows unique event data tobe sen to each sat, In addition, validating state transitions prevents client misuse by eliminating the side effects caused by unswanted state transitions, ‘This implementation offers easy use for the inheriting classes. With the macros it lets you just "ur the crank" without much thought given tothe underlying mectanies of how the state engine operates This allows you more time to concentrate on more important things, lke the design ofthe state transitions and state function implementation Reference [1] Sally Shacr. Object Lifecycles (Prentice Englewood Cliffs, ND), 1992. David Lafreniere has designed hardware and software over the past ten years. He currently works at PropHtcad Development, Inc. a software-consulting frm, designing software for a variety of embedded and PC-based applications. He can be reached a afrenipacbell net.

You might also like