tdaq-develop-2025-02-12
FiniteStateMachine.cc
1 #include "otsdaq/FiniteStateMachine/FiniteStateMachine.h"
2 #include "otsdaq/MessageFacility/MessageFacility.h"
3 
4 #include "otsdaq/Macros/CoutMacros.h"
5 
6 #include <map>
7 #include <sstream>
8 
9 using namespace ots;
10 
11 #undef __MF_SUBJECT__
12 #define __MF_SUBJECT__ "FSM"
13 #define mfSubject_ std::string("FSM-") + getStateMachineName()
14 
15 const char FiniteStateMachine::FAILED_STATE = 'F';
16 const std::string FiniteStateMachine::FAILED_STATE_NAME = "Failed";
17 const std::string FiniteStateMachine::ERROR_TRANSITION_NAME = "Error";
18 
19 //==============================================================================
20 FiniteStateMachine::FiniteStateMachine(const std::string& stateMachineName)
21  : stateEntranceTime_(0)
22  , inTransition_(false)
23  , provenanceState_('X')
24  , theErrorMessage_("")
25  , stateMachineName_(stateMachineName)
26 {
27  __GEN_COUT__ << "Constructing FiniteStateMachine" << __E__;
28 } // end constructor()
29 
30 //==============================================================================
31 FiniteStateMachine::~FiniteStateMachine(void) {}
32 
33 //==============================================================================
34 toolbox::fsm::State FiniteStateMachine::getProvenanceState(void)
35 {
36  return provenanceState_;
37 }
38 
39 //==============================================================================
40 toolbox::fsm::State FiniteStateMachine::getTransitionFinalState(
41  const std::string& transition)
42 {
43  if(stateTransitionTable_[currentState_].find(transition) !=
44  stateTransitionTable_[currentState_].end())
45  return stateTransitionTable_[currentState_][transition];
46  else
47  {
48  if(transition == FiniteStateMachine::ERROR_TRANSITION_NAME)
49  {
50  __GEN_COUT__ << FiniteStateMachine::ERROR_TRANSITION_NAME << "'ing to "
51  << FiniteStateMachine::FAILED_STATE_NAME << __E__;
52  return stateTransitionTable_[FiniteStateMachine::FAILED_STATE]
53  [FiniteStateMachine::ERROR_TRANSITION_NAME];
54  }
55  __GEN_SS__ << "Cannot find transition name for transition '" << transition
56  << "' from current state '" << currentState_ << ".'" << __E__;
57  __GEN_COUT__ << ss.str();
58  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
59  }
60 } // end getTransitionFinalState()
61 
62 //==============================================================================
63 std::string FiniteStateMachine::getProvenanceStateName(void)
64 {
65  return getStateName(getProvenanceState());
66 }
67 
68 //==============================================================================
69 std::string FiniteStateMachine::getCurrentStateName(void)
70 {
71  return getStateName(getCurrentState());
72 }
73 
74 //==============================================================================
80 {
81  return stateEntranceTime_ ? (time(0) - stateEntranceTime_) : 0;
82 }
83 
84 //==============================================================================
85 std::string FiniteStateMachine::getCurrentTransitionName(const std::string& transition)
86 {
87  // __GEN_COUTV__(stateTransitionNameTable_.size());
88  // __GEN_SS__ << currentState_ << " " << transition << " " << currentTransition_ << __E__;
89  // __GEN_COUT__ << ss.str();
90  // __GEN_COUT__ << (stateTransitionNameTable_.find(currentState_) == stateTransitionNameTable_.end()?1:0);
91  // __GEN_COUT__ << (stateTransitionNameTable_.at(currentState_).find(transition) ==
92  // stateTransitionNameTable_.at(currentState_).end()?1:0);
93 
94  // for(auto startState : stateTransitionNameTable_)
95  // {
96  // __GEN_COUTV__(getStateName(startState.first));
97  // std::cout << startState.first << __E__;
98  // for(auto trans : stateTransitionNameTable_.at(startState.first))
99  // __GEN_COUT__ << "\t" << trans.first << " " << trans.second << __E__;
100  // }
101 
102  // if looking for current active transition, calculate from provenance state
103  if(transition == "")
104  {
105  if(stateTransitionNameTable_.at(provenanceState_).find(currentTransition_) !=
106  stateTransitionNameTable_.at(provenanceState_).end())
107  return stateTransitionNameTable_.at(provenanceState_).at(currentTransition_);
108  else
109  {
110  if(currentTransition_ == FiniteStateMachine::ERROR_TRANSITION_NAME)
111  {
112  __GEN_COUT__ << FiniteStateMachine::ERROR_TRANSITION_NAME << "'ing to "
113  << FiniteStateMachine::FAILED_STATE_NAME << __E__;
114  return currentTransition_;
115  }
116  __GEN_SS__ << "Cannot find transition name from '" << getProvenanceStateName()
117  << "' for command: " << currentTransition_ << "...";
118  __GEN_COUT_WARN__
119  << ss.str(); //reduce to warning because transitions like 'Configure' can jump multiple states, e.g. from Initial
120  return currentTransition_;
121  }
122  }
123 
124  if(stateTransitionNameTable_.at(currentState_).find(transition) !=
125  stateTransitionNameTable_.at(currentState_).end())
126  {
127  return stateTransitionNameTable_.at(currentState_).at(transition);
128  }
129  else
130  {
131  if(transition == FiniteStateMachine::ERROR_TRANSITION_NAME)
132  {
133  __GEN_COUT__ << FiniteStateMachine::ERROR_TRANSITION_NAME << "'ing to "
134  << FiniteStateMachine::FAILED_STATE_NAME << __E__;
135  return transition;
136  }
137  __GEN_SS__ << "Cannot find transition name from '" << getCurrentStateName()
138  << "' for command: " << transition << "...";
139  __GEN_COUT_WARN__
140  << ss.str(); //reduce to warning because transitions like 'Configure' can jump multiple states, e.g. from Initial
141  return transition;
142  }
143 } // end getCurrentTransitionName()
144 
145 //==============================================================================
146 std::string FiniteStateMachine::getTransitionName(const toolbox::fsm::State from,
147  const std::string& transition)
148 {
149  if(stateTransitionNameTable_[from].find(transition) !=
150  stateTransitionNameTable_[from].end())
151  {
152  return stateTransitionNameTable_[from][transition];
153  }
154  else
155  {
156  if(transition == FiniteStateMachine::ERROR_TRANSITION_NAME)
157  {
158  __GEN_COUT__ << FiniteStateMachine::ERROR_TRANSITION_NAME << "'ing to "
159  << FiniteStateMachine::FAILED_STATE_NAME << __E__;
160  return transition;
161  }
162  std::ostringstream error;
163  error << "Cannot find transition name from '" << from
164  << "' for command: " << transition << __E__;
165  XCEPT_RAISE(toolbox::fsm::exception::Exception, error.str());
166  }
167 } // end getTransitionName()
168 
169 //==============================================================================
170 std::string FiniteStateMachine::getTransitionParameter(const toolbox::fsm::State from,
171  const std::string& transition)
172 {
173  if(stateTransitionParameterTable_[from].find(transition) !=
174  stateTransitionParameterTable_[from].end())
175  {
176  return stateTransitionParameterTable_[from][transition];
177  }
178  return "";
179 } // end getTransitionParameter()
180 
181 //==============================================================================
182 std::string FiniteStateMachine::getTransitionFinalStateName(const std::string& transition)
183 {
184  return getStateName(getTransitionFinalState(transition));
185 }
186 
187 //==============================================================================
188 bool FiniteStateMachine::execTransition(const std::string& transition)
189 {
190  const xoap::MessageReference message;
191  return execTransition(transition, message);
192 } // end execTransition()
193 
194 //==============================================================================
202 bool FiniteStateMachine::execTransition(const std::string& transition,
203  const xoap::MessageReference& message)
204 {
205  __GEN_COUTV__(transition);
206 
207  if(transition == "fail")
208  {
209  while(inTransition_)
210  {
211  __GEN_COUT__ << "Currently in transition '" << currentTransition_
212  << "' executed from current state " << getProvenanceStateName()
213  << ". Attempting to wait for the transition to complete."
214  << __E__;
215  sleep(1);
216  }
217  sleep(1);
218 
219  if(getStateName(getCurrentState()) == FiniteStateMachine::FAILED_STATE_NAME)
220  {
221  __GEN_COUT_INFO__ << "Already failed. Current state: "
222  << getStateName(getCurrentState())
223  << " last state: " << getProvenanceStateName() << __E__;
224  return true;
225  }
226  __GEN_COUT_INFO__ << "Failing now!! Current state: "
227  << getStateName(getCurrentState())
228  << " last state: " << getProvenanceStateName() << __E__;
229 
230  // find any valid transition and take it..
231  // all transition functions must check for a failure
232  // flag, and throw an exception to go to Fail state
233 
234  std::map<std::string, toolbox::fsm::State> transitions =
235  getTransitions(getCurrentState());
236  for(const auto& transitionPair : transitions)
237  {
238  __GEN_COUT__ << "Taking any transition to indirect failure.. found '"
239  << transitionPair.first << "'" << __E__;
240  __GEN_COUT__ << "Taking fail transition from Current state: "
241  << getStateName(getCurrentState())
242  << " last state: " << getProvenanceStateName() << __E__;
243  toolbox::Event::Reference event(
244  new toolbox::Event(transitionPair.first, this));
245 
246  try
247  {
248  this->fireEvent(event);
249  }
250  catch(toolbox::fsm::exception::Exception& e)
251  {
252  std::ostringstream error;
253  error << "Transition " << transition
254  << " was not executed from current state "
255  << getStateName(getCurrentState())
256  << ". There was an error: " << e.what();
257  __GEN_COUT_ERR__ << error.str() << __E__;
258  }
259  inTransition_ = false;
260  stateEntranceTime_ = time(0);
261  return true;
262  }
263  // //XCEPT_RAISE (toolbox::fsm::exception::Exception, transition);
264  // theMessage_ = message;
265  // toolbox::Event::Reference event(new toolbox::Event(, this));
266  //
267  }
268 
269  if(inTransition_)
270  {
271  __GEN_COUT_WARN__ << "In transition, and received another transition: "
272  << transition << ". Ignoring..." << __E__;
273  return false;
274  }
275  inTransition_ = true;
276  bool transitionSuccessful = true;
277  provenanceState_ = getCurrentState();
278 
279  std::map<std::string, toolbox::fsm::State> transitions =
280  getTransitions(getCurrentState());
281  if(transitions.find(transition) == transitions.end())
282  {
283  inTransition_ = false;
284  std::ostringstream error;
285  error << transition
286  << " is not in the list of the transitions from current state "
287  << getStateName(getCurrentState());
288  __GEN_COUT_ERR__ << error.str() << __E__;
289  __GEN_COUTV__(getErrorMessage());
290  XCEPT_RAISE(toolbox::fsm::exception::Exception, error.str());
291  //__GEN_COUT__ << error << __E__;
292  // __GEN_COUT__ << "Transition?" << inTransition_ << __E__;
293  return false;
294  }
295 
296  // fire FSM event by calling mapped function
297  // (e.g. mapped by RunControlStateMachine and function implemented by
298  // CoreSupervisorBase class inheriting from RunControlStateMachine)
299  try
300  {
301  toolbox::Event::Reference event(new toolbox::Event(transition, this));
302  theMessage_ =
303  message; // Even if it is bad, there can only be 1 transition at a time
304  // so this parameter should not change during all transition
305  currentTransition_ = transition;
306 
307  this->fireEvent(event);
308  }
309  catch(toolbox::fsm::exception::Exception& e)
310  {
311  inTransition_ = false;
312  transitionSuccessful = false;
313  std::ostringstream error;
314  __GEN_SS__ << "Transition " << transition
315  << " was not executed from current state "
316  << getStateName(getCurrentState())
317  << ". There was an error: " << e.what();
318  __GEN_COUT_ERR__ << ss.str() << __E__;
319  // diagService_->reportError(err.str(),DIAGERROR);
320 
321  // send state machine to error
322  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
323  }
324  catch(...)
325  {
326  inTransition_ = false;
327  transitionSuccessful = false;
328  __GEN_SS__ << "Transition " << transition
329  << " was not executed from current state "
330  << getStateName(getCurrentState()) << ". There was an unknown error.";
331  try
332  {
333  throw;
334  } //one more try to printout extra info
335  catch(const std::exception& e)
336  {
337  ss << "Exception message: " << e.what();
338  }
339  catch(...)
340  {
341  }
342  __GEN_COUT_ERR__ << ss.str() << __E__;
343  // diagService_->reportError(err.str(),DIAGERROR);
344 
345  // send state machine to error
346  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
347  }
348 
349  inTransition_ = false;
350  stateEntranceTime_ = time(0);
351  return transitionSuccessful;
352 } // end execTransition()
353 
354 //==============================================================================
355 bool FiniteStateMachine::isInTransition(void) { return inTransition_; }
356 
357 //==============================================================================
358 void FiniteStateMachine::setErrorMessage(const std::string& errMessage, bool append)
359 {
360  if(append)
361  theErrorMessage_ += errMessage;
362  else
363  theErrorMessage_ = errMessage;
364 } // end setErrorMessage()
365 
366 //==============================================================================
367 const std::string& FiniteStateMachine::getErrorMessage() const
368 {
369  return theErrorMessage_;
370 }
371 
372 //==============================================================================
373 void FiniteStateMachine::setInitialState(toolbox::fsm::State state)
374 {
375  toolbox::fsm::FiniteStateMachine::setInitialState(state);
376  provenanceState_ = state;
377  stateEntranceTime_ = time(0);
378 } // end setInitialState()
time_t getTimeInState(void) const