appkit  1.5.1
RoadNarrows Robotics Application Kit
StateMach.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: RoadNarrows Robotics Application Tool Kit
4 //
5 // Library: librnr_appkit
6 //
7 // File: StateMach.cxx
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2013-05-06 10:03:14 -0600 (Mon, 06 May 2013) $
12  * $Rev: 2907 $
13  *
14  * \brief Finite State Machine implementation.
15  *
16  * \author Robin Knight (robin.knight@roadnarrows.com)
17  * \author Daniel Packard (daniel@roadnarrows.com)
18  *
19  * \par Copyright
20  * \h_copy 2012-2017. RoadNarrows LLC.\n
21  * http://www.roadnarrows.com\n
22  * All Rights Reserved
23  */
24 /*
25  * @EulaBegin@
26  *
27  * Permission is hereby granted, without written agreement and without
28  * license or royalty fees, to use, copy, modify, and distribute this
29  * software and its documentation for any purpose, provided that
30  * (1) The above copyright notice and the following two paragraphs
31  * appear in all copies of the source code and (2) redistributions
32  * including binaries reproduces these notices in the supporting
33  * documentation. Substantial modifications to this software may be
34  * copyrighted by their authors and need not follow the licensing terms
35  * described here, provided that the new terms are clearly indicated in
36  * all files where they apply.
37  *
38  * IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY MEMBERS/EMPLOYEES
39  * OF ROADNARROW LLC OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
40  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
41  * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
42  * EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
43  * THE POSSIBILITY OF SUCH DAMAGE.
44  *
45  * THE AUTHOR AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
46  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
47  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
48  * "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
49  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
50  *
51  * @EulaEnd@
52  */
53 ////////////////////////////////////////////////////////////////////////////////
54 
55 #include <stdio.h>
56 #include <stdarg.h>
57 
58 #include <string>
59 #include <map>
60 
61 #include "rnr/rnrconfig.h"
62 #include "rnr/log.h"
63 
64 #include "rnr/appkit/StateMach.h"
65 #include "rnr/appkit/State.h"
66 
67 
68 using namespace std;
69 using namespace rnr;
70 
71 /*!
72  * \brief Log state transition at diagnostic level 3.
73  *
74  * \param nCurrStateId Current state id.
75  * \param nEventId Received event id.
76  * \param nNextStateId Next (new) state id.
77  */
78 #define LOGTRANSITION(nCurrStateId, nEventId, nNextStateId) \
79  do \
80  { \
81  if( LOGABLE(LOG_LEVEL_DIAG3) ) \
82  { \
83  logTransition(nCurrStateId, nEventId, nNextStateId); \
84  } \
85  } while(0)
86 
87 
88 //----------------------------------------------------------------------------
89 // StateMach Class
90 //----------------------------------------------------------------------------
91 
92 StateMach::StateMach(int nStateMachId, const string &strStateMachName) :
93  m_strStateMachName(strStateMachName)
94 {
95  m_nStateMachId = nStateMachId;
96  m_nStartStateId = State::StateIdStart;
97  m_nCurrStateId = State::StateIdUndef;
98  m_nMarkStateId = State::StateIdUndef;
99  m_bIsRecording = false;
100 }
101 
102 StateMach::StateMach(int nStateMachId,
103  const string &strStateMachName,
104  int nStartStateId,
105  State *listStates[]) :
106  m_strStateMachName(strStateMachName)
107 {
108  int i;
109 
110  m_nStateMachId = nStateMachId;
111  m_nStartStateId = nStartStateId;
112  m_nCurrStateId = State::StateIdUndef;
113  m_nMarkStateId = State::StateIdUndef;
114  m_bIsRecording = false;
115 
116  for(i=0; listStates[i] != NULL; ++i)
117  {
118  addState(listStates[i]);
119  }
120 }
121 
122 StateMach::~StateMach()
123 {
124  StateTblMap_T::iterator iter;
125 
126  for(iter=m_mapStateTbl.begin(); iter!=m_mapStateTbl.end(); ++iter)
127  {
128  delete iter->second;
129  }
130 
131  m_mapStateTbl.clear();
132 }
133 
134 int StateMach::addStates(State *pState, ...)
135 {
136  va_list ap;
137  int i;
138 
139  va_start(ap, pState);
140 
141  for(i=0; pState != NULL; pState = va_arg(ap, State *), ++i)
142  {
143  addState(pState);
144  }
145 
146  va_end(ap);
147 
148  return i;
149 }
150 
152 {
153  StateTblMap_T::iterator pos;
154 
155  if( pState != NULL )
156  {
157  if((pos = m_mapStateTbl.find(pState->getStateId())) != m_mapStateTbl.end())
158  {
159  LOGWARN("%s.%d: State [%s.%d] already exists - replacing.",
161  pos->second->getStateName().c_str(), pos->second->getStateId());
162  delete pos->second;
163  m_mapStateTbl.erase(pos);
164  }
165 
166  m_mapStateTbl[pState->getStateId()] = pState;
167 
168  LOGDIAG3("%s.%d: [%s.%d] state added.",
170  pState->getStateName().c_str(), pState->getStateId());
171  }
172 }
173 
174 bool StateMach::deleteState(int nStateId)
175 {
176  StateTblMap_T::iterator pos;
177 
178  if( (pos = m_mapStateTbl.find(nStateId)) != m_mapStateTbl.end() )
179  {
180  delete pos->second;
181  m_mapStateTbl.erase(pos);
182  return true;
183  }
184  else
185  {
186  LOGERROR("%s.%d: No state found in state table with state id=%d.",
187  m_strStateMachName.c_str(), m_nStateMachId, nStateId);
188  return false;
189  }
190 }
191 
193 {
194  StateTblMap_T::iterator pos;
195  State *pState;
196  int nEventId;
197  int nNextStateId;
198 
201 
202  // run from the start
204  {
205  //
206  // Found the state in state machine. (Block) receive event and dispatch.
207  //
208  if( (pos = m_mapStateTbl.find(m_nCurrStateId)) != m_mapStateTbl.end() )
209  {
210  pState = pos->second;
211 
212  // entering this state, execute any 'enter state' action
214  {
215  pState->actionEnterState(m_nMarkStateId, nEventId);
216  }
217 
218  // (block) receive event
219  nEventId = pState->receiveEvent();
220 
221  // dispatch event
222  nNextStateId = pState->dispatchEvent(m_nMarkStateId, nEventId);
223 
224  // this state
225  if( nNextStateId == State::StateIdThis )
226  {
227  nNextStateId = m_nCurrStateId;
228  }
229 
230  // previous state
231  else if( nNextStateId == State::StateIdPrev )
232  {
233  nNextStateId = popStateId();
234  }
235 
236  // exiting this state, execute any 'exit state' action
237  if( m_nCurrStateId != nNextStateId )
238  {
239  pState->actionExitState(nNextStateId, nEventId);
240  }
241 
242  // log
243  LOGTRANSITION(m_nCurrStateId, nEventId, nNextStateId);
244 
246  m_nCurrStateId = nNextStateId;
247 
248  // record state (if enabled)
250  }
251 
252  //
253  // Badly formed state machine. No state associated with state id. Terminate.
254  //
255  else
256  {
257  LOGERROR("%s.%d: No state found in state table with state id=%d.",
261  }
262  }
263 }
264 
265 void StateMach::pushStateId(int nStateId)
266 {
267  size_t n;
268 
269  if( m_bIsRecording )
270  {
271  if( (n = m_stackStateIds.size()) == 0 )
272  {
273  m_stackStateIds.push_back(nStateId);
274  }
275  else if( (m_uMaxRecording != NO_LIMIT) && (n >= m_uMaxRecording) )
276  {
277  m_stackStateIds.erase(m_stackStateIds.begin());
278  m_stackStateIds.push_back(nStateId);
279  }
280  else if( m_stackStateIds[n-1] != nStateId )
281  {
282  m_stackStateIds.push_back(nStateId);
283  }
284  }
285 }
286 
288 {
289  int nStateId;
290 
291  if( m_bIsRecording && (m_stackStateIds.size() > 0) )
292  {
293  nStateId = m_stackStateIds.back();
294  m_stackStateIds.pop_back();
295  return nStateId;
296  }
297  else
298  {
299  return State::StateIdUndef;
300  }
301 }
302 
304 {
305  StateTblMap_T::iterator iter;
306 
307  fprintf(fp, "%*sState Machine: %s.%d\n",
308  indent, "", m_strStateMachName.c_str(), m_nStateMachId);
309 
310  for(iter = m_mapStateTbl.begin(); iter != m_mapStateTbl.end(); ++iter)
311  {
312  iter->second->printState(fp, indent+2);
313  }
314 }
315 
316 void StateMach::logTransition(int nCurrStateId, int nEventId, int nNextStateId)
317 {
318  static const char *sUndef = "#undef";
319 
320  StateTblMap_T::iterator pos;
321  string strCurr;
322  string strEvent;
323  string strAction;
324  string strNext;
325 
326  if( (pos = m_mapStateTbl.find(nCurrStateId)) != m_mapStateTbl.end() )
327  {
328  strCurr = pos->second->getStateName();
329  strEvent = pos->second->getEventName(nEventId);
330  strAction = pos->second->getActionName(nEventId);
331  }
332  else
333  {
334  strCurr = sUndef;
335  strEvent = sUndef;
336  strAction = sUndef;
337  }
338 
339  if( (pos = m_mapStateTbl.find(nNextStateId)) != m_mapStateTbl.end() )
340  {
341  strNext = pos->second->getStateName();
342  }
343  else
344  {
345  strNext = "#undef";
346  }
347 
348  LOGDIAG3("%s.%d: [%s.%d] -- %s(%s.%d) --> [%s.%d]",
350  strCurr.c_str(), nCurrStateId,
351  strAction.c_str(), strEvent.c_str(), nEventId,
352  strNext.c_str(), nNextStateId);
353 }
std::string m_strStateMachName
state machine name
Definition: StateMach.h:280
int m_nMarkStateId
mark state id of last run cycle
Definition: StateMach.h:284
void pushStateId(int nStateid)
Push state id onto stack of state ids.
Definition: StateMach.cxx:265
virtual void actionEnterState(int nPrevStateId, int nEventId)
Execute &#39;enter state&#39; action.
Definition: State.h:305
std::vector< int > m_stackStateIds
stack of previous state ids
Definition: StateMach.h:285
virtual int receiveEvent()
Receive next event.
Definition: State.cxx:204
osManipIndent indent()
Left indent at current stream indentation level.
Definition: IOManip.cxx:115
bool deleteState(int nStateId)
Delete state from this state machine.
Definition: StateMach.cxx:174
int getStateId() const
Get state id.
Definition: State.h:337
Finite State Machine interface.
int m_nCurrStateId
current state id
Definition: StateMach.h:283
virtual int dispatchEvent(int nPrevStateId, int nEventId)
Dispatch received event by executing associated action and transitioning to the next state...
Definition: State.cxx:225
virtual void printStateMach(FILE *fp=stdout, int indent=0)
Print out state machine.
Definition: StateMach.cxx:303
virtual void run()
Run the state machine from the start.
Definition: StateMach.cxx:192
int m_nStartStateId
starting state id
Definition: StateMach.h:282
bool m_bIsRecording
is [not] recording work flow
Definition: StateMach.h:287
int addStates(State *pState,...)
Add a list of states to this state machine.
Definition: StateMach.cxx:134
static const int StateIdUndef
undefined state
Definition: State.h:166
static const int StateIdTerminate
terminate state
Definition: State.h:170
std::string getStateName() const
Get state name.
Definition: State.h:347
int m_nStateMachId
state machine id
Definition: StateMach.h:279
State base class interface.
StateTblMap_T m_mapStateTbl
state table
Definition: StateMach.h:281
static const int StateIdThis
the current (this) state
Definition: State.h:169
void addState(State *pState)
Add state to this state machine.
Definition: StateMach.cxx:151
#define LOGTRANSITION(nCurrStateId, nEventId, nNextStateId)
Log state transition at diagnostic level 3.
Definition: StateMach.cxx:78
RoadNarrows Robotics.
Definition: Camera.h:74
virtual void logTransition(int nCurrStateId, int nEventId, int nNextStateId)
Log state transition.
Definition: StateMach.cxx:316
static const size_t NO_LIMIT
no limit on length of recording
Definition: StateMach.h:88
virtual void actionExitState(int nNextStateId, int nEventId)
Execute &#39;exit state&#39; action.
Definition: State.h:315
int popStateId()
Pop state id from stack of state ids.
Definition: StateMach.cxx:287
size_t m_uMaxRecording
max recording length
Definition: StateMach.h:286
static const int StateIdPrev
the previous state
Definition: State.h:168