| 1 | // ************************************************************************** | 
|---|
| 2 | /** @class StateMachineImp | 
|---|
| 3 |  | 
|---|
| 4 | @brief Base class for a state machine implementation | 
|---|
| 5 |  | 
|---|
| 6 | \dot | 
|---|
| 7 | digraph example { | 
|---|
| 8 | node [shape=record, fontname=Helvetica, fontsize=10]; | 
|---|
| 9 | s [ label="Constructor" style="rounded"  color="red"   URL="\ref StateMachineImp::StateMachineImp"]; | 
|---|
| 10 | a [ label="State -3 (kSM_NotReady)"      color="red"   URL="\ref StateMachineImp::StateMachineImp"]; | 
|---|
| 11 | b [ label="State -2 (kSM_Initializing)"  color="red"   URL="\ref StateMachineImp::StateMachineImp"]; | 
|---|
| 12 | c [ label="State -1 (kSM_Configuring)"   color="red"   URL="\ref StateMachineImp::StateMachineImp"]; | 
|---|
| 13 | y [ label="State 0 (kSM_Ready)"                        URL="\ref StateMachineImp::Run"]; | 
|---|
| 14 | r [ label="User states (Running)" ]; | 
|---|
| 15 | e [ label="State 256 (kSM_Error)" ]; | 
|---|
| 16 | f [ label="State 65535 (kSM_FatalError)" color="red"   URL="\ref StateMachineImp::Run"]; | 
|---|
| 17 |  | 
|---|
| 18 | // ---- manual means: command or program introduced ---- | 
|---|
| 19 |  | 
|---|
| 20 | // Startup from Run() to Ready | 
|---|
| 21 | s -> a [ arrowhead="open" color="red"  style="solid"  ]; // automatic (mandatory) | 
|---|
| 22 | a -> b [ arrowhead="open" color="red"  style="solid"  ]; // automatic (mandatory) | 
|---|
| 23 | b -> c [ arrowhead="open" color="red"  style="solid"  ]; // automatic (mandatory) | 
|---|
| 24 |  | 
|---|
| 25 | c -> y [ arrowhead="open" color="red"  style="solid" URL="\ref StateMachineImp::Run" ]; // prg: Run() | 
|---|
| 26 |  | 
|---|
| 27 | y -> c [ arrowhead="open" style="dashed" URL="\ref StateMachineDim::exitHandler" ]; // CMD: EXIT | 
|---|
| 28 | r -> c [ arrowhead="open" style="dashed" URL="\ref StateMachineDim::exitHandler" ]; // CMD: EXIT | 
|---|
| 29 | e -> c [ arrowhead="open" style="dashed" URL="\ref StateMachineDim::exitHandler" ]; // CMD: EXIT | 
|---|
| 30 |  | 
|---|
| 31 | e -> y [ arrowhead="open" color="red"  style="dashed" ]; // CMD: RESET (e.g.) | 
|---|
| 32 |  | 
|---|
| 33 | y -> e [ arrowhead="open" color="blue" style="solid"  ]; // prg | 
|---|
| 34 | r -> e [ arrowhead="open" color="blue" style="solid"  ]; // prg | 
|---|
| 35 |  | 
|---|
| 36 | y -> r [ arrowhead="open" color="blue" style="dashed" ]; // CMD/PRG | 
|---|
| 37 | r -> y [ arrowhead="open" color="blue" style="dashed" ]; // CMD/PRG | 
|---|
| 38 |  | 
|---|
| 39 | y -> f [ arrowhead="open" color="blue" style="solid"  ]; // prg | 
|---|
| 40 | r -> f [ arrowhead="open" color="blue" style="solid"  ]; // prg | 
|---|
| 41 | e -> f [ arrowhead="open" color="blue" style="solid"  ]; // prg | 
|---|
| 42 | } | 
|---|
| 43 | \enddot | 
|---|
| 44 |  | 
|---|
| 45 | - <B>Red box</B>: Internal states. Events which are received are | 
|---|
| 46 | discarded. | 
|---|
| 47 | - <B>Black box</B>: State machine running. Events are accepted and | 
|---|
| 48 | processed according to the implemented functions Transition(), | 
|---|
| 49 | Configuration() and Execute(). Events are accepted accoding to the | 
|---|
| 50 | lookup table of allowed transitions. | 
|---|
| 51 | - <B>Red solid arrow</B>: A transition initiated by the program itself. | 
|---|
| 52 | - <b>Dashed arrows in general</b>: Transitions which can be initiated | 
|---|
| 53 | by a dim-command or get inistiated by the program. | 
|---|
| 54 | - <b>Solid arrows in general</b>: These transitions are always initiated by | 
|---|
| 55 | the program. | 
|---|
| 56 | - <B>Red dashed</B>: Suggested RESET event (should be implemented by | 
|---|
| 57 | the derived class) | 
|---|
| 58 | - <B>Black dashed arrow</B>: Exit from the main loop. This can either | 
|---|
| 59 | happen by the Dim-provided EXIT-command or a call to StateMachineDim::Stop. | 
|---|
| 60 | - <B>Black arrows</B>: Other events or transitions which can be | 
|---|
| 61 | implemented by the derived class. | 
|---|
| 62 | - <B>Dotted black arrow</B>: Exit from the main-loop which is initiated | 
|---|
| 63 | by the program itself through StateMachineDim::Stop() and not by the | 
|---|
| 64 | state machine itself (Execute(), Configure() and Transition()) | 
|---|
| 65 | - <b>Blue dashed arrows</b>: Transitions which happen either by receiving | 
|---|
| 66 | a event or are initiated from the state machine itself | 
|---|
| 67 | (by return values of (Execute(), Configure() and Transition()) | 
|---|
| 68 | - <b>Blue solid</b>: Transitions which cannot be initiated by dim | 
|---|
| 69 | event but only by the state machine itself. | 
|---|
| 70 | - From the program point of view the fatal error is identical with | 
|---|
| 71 | the kSM_Configuring state, i.e. it is returned from the main-loop. | 
|---|
| 72 | Usually this will result in program termination. However, depending | 
|---|
| 73 | on the state the program might decide to use different cleaning | 
|---|
| 74 | routines. | 
|---|
| 75 |  | 
|---|
| 76 | @todo | 
|---|
| 77 | - A proper and correct cleanup after an EXIT or Stop() is missing. | 
|---|
| 78 | maybe we have to force a state 0 first? | 
|---|
| 79 | */ | 
|---|
| 80 | // ************************************************************************** | 
|---|
| 81 | #include "StateMachineImp.h" | 
|---|
| 82 |  | 
|---|
| 83 | #include "Time.h" | 
|---|
| 84 | #include "Event.h" | 
|---|
| 85 |  | 
|---|
| 86 | #include "WindowLog.h" | 
|---|
| 87 | #include "Converter.h" | 
|---|
| 88 |  | 
|---|
| 89 | #include "tools.h" | 
|---|
| 90 |  | 
|---|
| 91 | using namespace std; | 
|---|
| 92 |  | 
|---|
| 93 | // -------------------------------------------------------------------------- | 
|---|
| 94 | // | 
|---|
| 95 | //! The state of the state machine (fCurrentState) is initialized with | 
|---|
| 96 | //! kSM_NotReady | 
|---|
| 97 | //! | 
|---|
| 98 | //! Default state names for kSM_NotReady, kSM_Ready, kSM_Error and | 
|---|
| 99 | //! kSM_FatalError are set via AddStateName. | 
|---|
| 100 | //! | 
|---|
| 101 | //! fExitRequested is set to 0, fRunning to false. | 
|---|
| 102 | //! | 
|---|
| 103 | //! Furthermore, the ostream is propagated to MessageImp, as well as | 
|---|
| 104 | //! stored in fOut. | 
|---|
| 105 | //! | 
|---|
| 106 | //! MessageImp is used for messages which are distributed (e.g. via DIM), | 
|---|
| 107 | //! fOut is used for messages which are only displayed on the local console. | 
|---|
| 108 | //! | 
|---|
| 109 | //! Subsequent, i.e. derived classes should setup all allowed state | 
|---|
| 110 | //! transitions as well as all allowed configuration event by | 
|---|
| 111 | //! AddEvent and AddStateName. | 
|---|
| 112 | //! | 
|---|
| 113 | //! @param out | 
|---|
| 114 | //!    A refrence to an ostream which allows to redirect the log-output | 
|---|
| 115 | //!    to something else than cout. The default is cout. The reference | 
|---|
| 116 | //!    is propagated to fLog | 
|---|
| 117 | //! | 
|---|
| 118 | //! @param name | 
|---|
| 119 | //!    The server name stored in fName | 
|---|
| 120 | //! | 
|---|
| 121 | // | 
|---|
| 122 | StateMachineImp::StateMachineImp(ostream &out, const std::string &name) | 
|---|
| 123 | : MessageImp(out), fName(name), fCurrentState(kSM_NotReady), | 
|---|
| 124 | fRunning(false), fExitRequested(0) | 
|---|
| 125 | { | 
|---|
| 126 | SetDefaultStateNames(); | 
|---|
| 127 | } | 
|---|
| 128 |  | 
|---|
| 129 | // -------------------------------------------------------------------------- | 
|---|
| 130 | // | 
|---|
| 131 | //! delete all object stored in fListOfEvent and in fEventQueue | 
|---|
| 132 | // | 
|---|
| 133 | StateMachineImp::~StateMachineImp() | 
|---|
| 134 | { | 
|---|
| 135 | // For this to work EventImp must be the first class from which | 
|---|
| 136 | // the object inherits | 
|---|
| 137 | for (vector<EventImp*>::iterator cmd=fListOfEvents.begin(); cmd!=fListOfEvents.end(); cmd++) | 
|---|
| 138 | delete *cmd; | 
|---|
| 139 |  | 
|---|
| 140 | // Unfortunately, front() doesn't necessarily return 0 if | 
|---|
| 141 | // queue is empty | 
|---|
| 142 | if (fEventQueue.size()) | 
|---|
| 143 | { | 
|---|
| 144 | while (1) | 
|---|
| 145 | { | 
|---|
| 146 | Event *q=fEventQueue.front(); | 
|---|
| 147 | if (!q) | 
|---|
| 148 | break; | 
|---|
| 149 |  | 
|---|
| 150 | fEventQueue.pop(); | 
|---|
| 151 | delete q; | 
|---|
| 152 | } | 
|---|
| 153 | } | 
|---|
| 154 | } | 
|---|
| 155 |  | 
|---|
| 156 | // -------------------------------------------------------------------------- | 
|---|
| 157 | // | 
|---|
| 158 | //! Sets the default state names. This function should be called in | 
|---|
| 159 | //! derived classes again if they overwrite SetStateName(). | 
|---|
| 160 | // | 
|---|
| 161 | void StateMachineImp::SetDefaultStateNames() | 
|---|
| 162 | { | 
|---|
| 163 | AddStateName(kSM_NotReady,   "NotReady", "State machine not ready, events are ignored."); | 
|---|
| 164 | AddStateName(kSM_Ready,      "Ready",    "State machine ready to receive events."); | 
|---|
| 165 | AddStateName(kSM_Error,      "ERROR",    "Common error state."); | 
|---|
| 166 | AddStateName(kSM_FatalError, "FATAL",    "A fatal error occured, the eventloop is stopped."); | 
|---|
| 167 | } | 
|---|
| 168 |  | 
|---|
| 169 | // -------------------------------------------------------------------------- | 
|---|
| 170 | // | 
|---|
| 171 | //! Puts the given event into the fifo. The fifo will take over ownership. | 
|---|
| 172 | //! Access to fEventQueue is encapsulated by fMutex. | 
|---|
| 173 | //! | 
|---|
| 174 | //! @param cmd | 
|---|
| 175 | //!    Pointer to an object of type Event to be stored in the fifo | 
|---|
| 176 | //! | 
|---|
| 177 | //! @todo | 
|---|
| 178 | //!    Can we also allow EventImp? | 
|---|
| 179 | // | 
|---|
| 180 | void StateMachineImp::PushEvent(Event *cmd) | 
|---|
| 181 | { | 
|---|
| 182 | fMutex.lock(); | 
|---|
| 183 | fEventQueue.push(cmd); | 
|---|
| 184 | fMutex.unlock(); | 
|---|
| 185 | } | 
|---|
| 186 |  | 
|---|
| 187 | // -------------------------------------------------------------------------- | 
|---|
| 188 | // | 
|---|
| 189 | //! Get an event from the fifo. We will take over the owenership of the | 
|---|
| 190 | //! object. The pointer is deleted from the fifo. Access of fEventQueue | 
|---|
| 191 | //! is encapsulated by fMutex. | 
|---|
| 192 | //! | 
|---|
| 193 | //! @returns | 
|---|
| 194 | //!    A pointer to an Event object | 
|---|
| 195 | // | 
|---|
| 196 | Event *StateMachineImp::PopEvent() | 
|---|
| 197 | { | 
|---|
| 198 | fMutex.lock(); | 
|---|
| 199 |  | 
|---|
| 200 | // Get the next event from the stack | 
|---|
| 201 | // and remove event from the stack | 
|---|
| 202 | Event *cmd = fEventQueue.front(); | 
|---|
| 203 | fEventQueue.pop(); | 
|---|
| 204 |  | 
|---|
| 205 | fMutex.unlock(); | 
|---|
| 206 |  | 
|---|
| 207 | return cmd; | 
|---|
| 208 | } | 
|---|
| 209 |  | 
|---|
| 210 | // -------------------------------------------------------------------------- | 
|---|
| 211 | // | 
|---|
| 212 | //! With this function commands are posted to the event queue. The data | 
|---|
| 213 | //! is not given as binary data but as a string instead. It is converted | 
|---|
| 214 | //! according to the format of the corresponding event and an event | 
|---|
| 215 | //! is posted to the queue if successfull. | 
|---|
| 216 | //! | 
|---|
| 217 | //! @param lout | 
|---|
| 218 | //!    Stream to which output should be redirected | 
|---|
| 219 | //!    event should be for. | 
|---|
| 220 | //! | 
|---|
| 221 | //! @param str | 
|---|
| 222 | //!    Command with data, e.g. "COMMAND 1 2 3 4 5 test" | 
|---|
| 223 | //! | 
|---|
| 224 | //! @returns | 
|---|
| 225 | //!    false if no event was posted to the queue. If | 
|---|
| 226 | //!    PostEvent(EventImp&,const char*, size_t) was called return its | 
|---|
| 227 | //!    return value | 
|---|
| 228 | // | 
|---|
| 229 | bool StateMachineImp::PostEvent(ostream &lout, const string &str) | 
|---|
| 230 | { | 
|---|
| 231 | // Find the delimiter between the command name and the data | 
|---|
| 232 | size_t p0 = str.find_first_of(' '); | 
|---|
| 233 | if (p0==string::npos) | 
|---|
| 234 | p0 = str.length(); | 
|---|
| 235 |  | 
|---|
| 236 | // Compile the command which will be sent to the state-machine | 
|---|
| 237 | const string name = fName + "/" + str.substr(0, p0); | 
|---|
| 238 |  | 
|---|
| 239 | // Check if this command is existing at all | 
|---|
| 240 | EventImp *evt = FindEvent(name); | 
|---|
| 241 | if (!evt) | 
|---|
| 242 | { | 
|---|
| 243 | lout << kRed << "Unknown command '" << name << "'" << endl; | 
|---|
| 244 | return false; | 
|---|
| 245 | } | 
|---|
| 246 |  | 
|---|
| 247 | // Get the format of the event data | 
|---|
| 248 | const string fmt = evt->GetFormat(); | 
|---|
| 249 |  | 
|---|
| 250 | // Convert the user entered data according to the format string | 
|---|
| 251 | // into a data block which will be attached to the event | 
|---|
| 252 | const Converter conv(lout, fmt, false); | 
|---|
| 253 | if (!conv) | 
|---|
| 254 | { | 
|---|
| 255 | lout << kRed << "Couldn't properly parse the format... ignored." << endl; | 
|---|
| 256 | return false; | 
|---|
| 257 | } | 
|---|
| 258 |  | 
|---|
| 259 | try | 
|---|
| 260 | { | 
|---|
| 261 | lout << kBlue << name; | 
|---|
| 262 | const vector<char> v = conv.GetVector(str.substr(p0)); | 
|---|
| 263 | lout << endl; | 
|---|
| 264 |  | 
|---|
| 265 | return PostEvent(*evt, v.data(), v.size()); | 
|---|
| 266 | } | 
|---|
| 267 | catch (const std::runtime_error &e) | 
|---|
| 268 | { | 
|---|
| 269 | lout << endl << kRed << e.what() << endl; | 
|---|
| 270 | } | 
|---|
| 271 |  | 
|---|
| 272 | return false; | 
|---|
| 273 | } | 
|---|
| 274 |  | 
|---|
| 275 | // -------------------------------------------------------------------------- | 
|---|
| 276 | // | 
|---|
| 277 | //! With this function commands are posted to the event queue. If the | 
|---|
| 278 | //! event loop has not yet been started with Run() the command is directly | 
|---|
| 279 | //! handled by HandleEvent. | 
|---|
| 280 | //! | 
|---|
| 281 | //! Events posted when the state machine is in a negative state or | 
|---|
| 282 | //! kSM_FatalError are ignored. | 
|---|
| 283 | //! | 
|---|
| 284 | //! A new event is created and its data contents initialized with the | 
|---|
| 285 | //! specified memory. | 
|---|
| 286 | //! | 
|---|
| 287 | //! @param evt | 
|---|
| 288 | //!    The event to be posted. The precise contents depend on what the | 
|---|
| 289 | //!    event should be for. | 
|---|
| 290 | //! | 
|---|
| 291 | //! @param ptr | 
|---|
| 292 | //!    pointer to the memory which should be attached to the event | 
|---|
| 293 | //! | 
|---|
| 294 | //! @param siz | 
|---|
| 295 | //!    size of the memory which should be attached to the event | 
|---|
| 296 | //! | 
|---|
| 297 | //! @returns | 
|---|
| 298 | //!    false if the event is ignored, true otherwise. | 
|---|
| 299 | //! | 
|---|
| 300 | //! @todo | 
|---|
| 301 | //!    - Shell we check for the validity of a command at the current state, too? | 
|---|
| 302 | //!    - should we also get the output stream as an argument here? | 
|---|
| 303 | // | 
|---|
| 304 | bool StateMachineImp::PostEvent(const EventImp &evt, const char *ptr, size_t siz) | 
|---|
| 305 | { | 
|---|
| 306 | if (GetCurrentState()<0 || GetCurrentState()==kSM_FatalError) | 
|---|
| 307 | { | 
|---|
| 308 | Out() << kYellow << "State<0 or FatalError: Event ignored." << endl; | 
|---|
| 309 | return false; | 
|---|
| 310 | } | 
|---|
| 311 |  | 
|---|
| 312 | if (IsRunning()) | 
|---|
| 313 | { | 
|---|
| 314 | Event *event = new Event(evt, ptr, siz); | 
|---|
| 315 | Debug("Posted: "+event->GetName()); | 
|---|
| 316 | PushEvent(event); | 
|---|
| 317 | } | 
|---|
| 318 | else | 
|---|
| 319 | { | 
|---|
| 320 | // FIXME: Is this thread safe? (Yes, because the data is copied) | 
|---|
| 321 | // But two handlers could be called at the same time. Do we | 
|---|
| 322 | // need to lock the handlers? (Dim + console) | 
|---|
| 323 | // FIXME: Is copying of the data necessary? | 
|---|
| 324 | const Event event(evt, ptr, siz); | 
|---|
| 325 | Lock(); | 
|---|
| 326 | HandleEvent(event); | 
|---|
| 327 | UnLock(); | 
|---|
| 328 | } | 
|---|
| 329 | return true; | 
|---|
| 330 | } | 
|---|
| 331 |  | 
|---|
| 332 | // -------------------------------------------------------------------------- | 
|---|
| 333 | // | 
|---|
| 334 | //! With this function commands are posted to the event queue. If the | 
|---|
| 335 | //! event loop has not yet been started with Run() the command is directly | 
|---|
| 336 | //! handled by HandleEvent. | 
|---|
| 337 | //! | 
|---|
| 338 | //! Events posted when the state machine is in a negative state or | 
|---|
| 339 | //! kSM_FatalError are ignored. | 
|---|
| 340 | //! | 
|---|
| 341 | //! @param evt | 
|---|
| 342 | //!    The event to be posted. The precise contents depend on what the | 
|---|
| 343 | //!    event should be for. | 
|---|
| 344 | //! | 
|---|
| 345 | //! @returns | 
|---|
| 346 | //!    false if the event is ignored, true otherwise. | 
|---|
| 347 | //! | 
|---|
| 348 | //! @todo | 
|---|
| 349 | //!    - Shell we check for the validity of a command at the current state, too? | 
|---|
| 350 | //!    - should we also get the output stream as an argument here? | 
|---|
| 351 | // | 
|---|
| 352 | bool StateMachineImp::PostEvent(const EventImp &evt) | 
|---|
| 353 | { | 
|---|
| 354 | if (GetCurrentState()<0 || GetCurrentState()==kSM_FatalError) | 
|---|
| 355 | { | 
|---|
| 356 | Out() << kYellow << "State<0 or FatalError: Event ignored." << endl; | 
|---|
| 357 | return false; | 
|---|
| 358 | } | 
|---|
| 359 |  | 
|---|
| 360 | if (IsRunning()) | 
|---|
| 361 | PushEvent(new Event(evt)); | 
|---|
| 362 | else | 
|---|
| 363 | { | 
|---|
| 364 | // FIXME: Is this thread safe? (Yes, because it is only used | 
|---|
| 365 | // by Dim and this is thread safe) But two handlers could | 
|---|
| 366 | // be called at the same time. Do we need to lock the handlers? | 
|---|
| 367 | HandleEvent(evt); | 
|---|
| 368 | } | 
|---|
| 369 | return true; | 
|---|
| 370 | } | 
|---|
| 371 |  | 
|---|
| 372 | // -------------------------------------------------------------------------- | 
|---|
| 373 | // | 
|---|
| 374 | //! Return all event names of the StateMachine | 
|---|
| 375 | //! | 
|---|
| 376 | //! @returns | 
|---|
| 377 | //!    A vector of strings with all event names of the state machine. | 
|---|
| 378 | //!    The event names all have the SERVER/ pre-fix removed. | 
|---|
| 379 | // | 
|---|
| 380 | const vector<string> StateMachineImp::GetEventNames() const | 
|---|
| 381 | { | 
|---|
| 382 | vector<string> v; | 
|---|
| 383 |  | 
|---|
| 384 | const string &name = fName + "/"; | 
|---|
| 385 | const int     len  = name.length(); | 
|---|
| 386 |  | 
|---|
| 387 | for (vector<EventImp*>::const_iterator i=fListOfEvents.begin(); | 
|---|
| 388 | i!=fListOfEvents.end(); i++) | 
|---|
| 389 | { | 
|---|
| 390 | const string evt = (*i)->GetName(); | 
|---|
| 391 |  | 
|---|
| 392 | v.push_back(evt.substr(0, len)==name ? evt.substr(len) : evt); | 
|---|
| 393 | } | 
|---|
| 394 |  | 
|---|
| 395 | return v; | 
|---|
| 396 | } | 
|---|
| 397 |  | 
|---|
| 398 | // -------------------------------------------------------------------------- | 
|---|
| 399 | // | 
|---|
| 400 | //! Call for each event in fListEvents its Print function with the given | 
|---|
| 401 | //! stream. | 
|---|
| 402 | //! | 
|---|
| 403 | //! @param out | 
|---|
| 404 | //!    ostream to which the output should be redirected | 
|---|
| 405 | //! | 
|---|
| 406 | //! @param evt | 
|---|
| 407 | //!    if given only the given event is selected | 
|---|
| 408 | // | 
|---|
| 409 | void StateMachineImp::PrintListOfEvents(ostream &out, const string &evt) const | 
|---|
| 410 | { | 
|---|
| 411 | for (vector<EventImp*>::const_iterator c=fListOfEvents.begin(); c!=fListOfEvents.end(); c++) | 
|---|
| 412 | if (evt.empty() || GetName()+'/'+evt==(*c)->GetName()) | 
|---|
| 413 | (*c)->Print(out, true); | 
|---|
| 414 | } | 
|---|
| 415 |  | 
|---|
| 416 | // -------------------------------------------------------------------------- | 
|---|
| 417 | // | 
|---|
| 418 | //! Call for each event in fListEvents its Print function with the given | 
|---|
| 419 | //! stream if it is an allowed event in the current state. | 
|---|
| 420 | //! | 
|---|
| 421 | //! @param out | 
|---|
| 422 | //!    ostream to which the output should be redirected | 
|---|
| 423 | //! | 
|---|
| 424 | // | 
|---|
| 425 | void StateMachineImp::PrintListOfAllowedEvents(ostream &out) const | 
|---|
| 426 | { | 
|---|
| 427 | for (vector<EventImp*>::const_iterator c=fListOfEvents.begin(); c!=fListOfEvents.end(); c++) | 
|---|
| 428 | if ((*c)->IsStateAllowed(fCurrentState)) | 
|---|
| 429 | (*c)->Print(out, true); | 
|---|
| 430 | } | 
|---|
| 431 |  | 
|---|
| 432 | // -------------------------------------------------------------------------- | 
|---|
| 433 | // | 
|---|
| 434 | //! Call PrintListOfEvents with fOut as the output stream | 
|---|
| 435 | //! | 
|---|
| 436 | //! @param str | 
|---|
| 437 | //!    if given only the given event is selected | 
|---|
| 438 | // | 
|---|
| 439 | // | 
|---|
| 440 | void StateMachineImp::PrintListOfEvents(const string &str) const | 
|---|
| 441 | { | 
|---|
| 442 | PrintListOfEvents(Out(), str); | 
|---|
| 443 | } | 
|---|
| 444 |  | 
|---|
| 445 | // -------------------------------------------------------------------------- | 
|---|
| 446 | // | 
|---|
| 447 | //! Print a list of all states with descriptions. | 
|---|
| 448 | //! | 
|---|
| 449 | //! @param out | 
|---|
| 450 | //!    ostream to which the output should be redirected | 
|---|
| 451 | // | 
|---|
| 452 | void StateMachineImp::PrintListOfStates(std::ostream &out) const | 
|---|
| 453 | { | 
|---|
| 454 | out << endl; | 
|---|
| 455 | out << kBold << "List of available states:" << endl; | 
|---|
| 456 | out << endl; | 
|---|
| 457 | for (StateNames::const_iterator i=fStateNames.begin(); i!=fStateNames.end(); i++) | 
|---|
| 458 | out << kBold << setw(5) << i->first << kReset << ": " << kYellow << i->second.first << kBlue << " (" << i->second.second << ")" << endl; | 
|---|
| 459 | out << endl; | 
|---|
| 460 | } | 
|---|
| 461 |  | 
|---|
| 462 | // -------------------------------------------------------------------------- | 
|---|
| 463 | // | 
|---|
| 464 | //! Print a list of all states with descriptions. | 
|---|
| 465 | // | 
|---|
| 466 | void StateMachineImp::PrintListOfStates() const | 
|---|
| 467 | { | 
|---|
| 468 | PrintListOfStates(Out()); | 
|---|
| 469 | } | 
|---|
| 470 |  | 
|---|
| 471 | // -------------------------------------------------------------------------- | 
|---|
| 472 | // | 
|---|
| 473 | //! Check whether an event (same pointer!) is in fListOfEvents | 
|---|
| 474 | //! | 
|---|
| 475 | //! @returns | 
|---|
| 476 | //!    true if the event was found, false otherwise | 
|---|
| 477 | // | 
|---|
| 478 | bool StateMachineImp::HasEvent(const EventImp *cmd) const | 
|---|
| 479 | { | 
|---|
| 480 | // Find the event from the list of commands and queue it | 
|---|
| 481 | return find(fListOfEvents.begin(), fListOfEvents.end(), cmd)!=fListOfEvents.end(); | 
|---|
| 482 | } | 
|---|
| 483 |  | 
|---|
| 484 | // -------------------------------------------------------------------------- | 
|---|
| 485 | // | 
|---|
| 486 | //! Check whether an event with the given name is found in fListOfEvents. | 
|---|
| 487 | //! Note that currently there is no mechanism which ensures that not two | 
|---|
| 488 | //! events have the same name. | 
|---|
| 489 | //! | 
|---|
| 490 | //! @returns | 
|---|
| 491 | //!    true if the event was found, false otherwise | 
|---|
| 492 | // | 
|---|
| 493 | EventImp *StateMachineImp::FindEvent(const std::string &evt) const | 
|---|
| 494 | { | 
|---|
| 495 | // Find the command from the list of commands and queue it | 
|---|
| 496 | for (vector<EventImp*>::const_iterator c=fListOfEvents.begin(); c!=fListOfEvents.end(); c++) | 
|---|
| 497 | if (evt == (*c)->GetName()) | 
|---|
| 498 | return *c; | 
|---|
| 499 |  | 
|---|
| 500 | return 0; | 
|---|
| 501 | } | 
|---|
| 502 |  | 
|---|
| 503 | // -------------------------------------------------------------------------- | 
|---|
| 504 | // | 
|---|
| 505 | //! Returns a pointer to a newly allocated object of base EventImp. | 
|---|
| 506 | //! It is meant to be overloaded by derived classes to create their | 
|---|
| 507 | //! own kind of events. | 
|---|
| 508 | //! | 
|---|
| 509 | //! @param targetstate | 
|---|
| 510 | //!    Defines the target state of the new transition. If \b must be | 
|---|
| 511 | //!    greater or equal zero. A negative target state is used to flag | 
|---|
| 512 | //!    commands which do not initiate a state transition. If this is | 
|---|
| 513 | //!    desired use AddEvent instead. | 
|---|
| 514 | //! | 
|---|
| 515 | //! @param name | 
|---|
| 516 | //!    The command name which should initiate the transition. The DimCommand | 
|---|
| 517 | //!    will be constructed with the name given to the constructor and this | 
|---|
| 518 | //!    name, e.g. "DRIVE/CHANGE_STATE_TO_NEW_STATE" | 
|---|
| 519 | //! | 
|---|
| 520 | //! @param fmt | 
|---|
| 521 | //!    A format as defined by the dim system can be given for the command. | 
|---|
| 522 | //!    However, it has no real meaning except that it is stored within the | 
|---|
| 523 | //!    DimCommand object. However, the user must make sure that the data of | 
|---|
| 524 | //!    received commands is properly extracted. No check is done. | 
|---|
| 525 | // | 
|---|
| 526 | EventImp *StateMachineImp::CreateEvent(int targetstate, const char *, const char *) | 
|---|
| 527 | { | 
|---|
| 528 | return new EventImp(targetstate); | 
|---|
| 529 | } | 
|---|
| 530 |  | 
|---|
| 531 | // -------------------------------------------------------------------------- | 
|---|
| 532 | // | 
|---|
| 533 | //! Calling this function, a new (named) event is added to the state | 
|---|
| 534 | //! machine. Via a call to CreateEvent a new event is created with the | 
|---|
| 535 | //! given targetstate, name and format. | 
|---|
| 536 | //! | 
|---|
| 537 | //! The allowed states are passed to the new event and a message | 
|---|
| 538 | //! is written to the output-stream. | 
|---|
| 539 | //! | 
|---|
| 540 | //! @param targetstate | 
|---|
| 541 | //!    Defines the target state (or name) of the new event. If \b must be | 
|---|
| 542 | //!    greater or equal zero. A negative target state is used to flag | 
|---|
| 543 | //!    commands which do not initiate a state transition. If this is | 
|---|
| 544 | //!    desired use the unnamed version of AddEvent instead. | 
|---|
| 545 | //! | 
|---|
| 546 | //! @param name | 
|---|
| 547 | //!    The command name which should initiate the transition. The DimCommand | 
|---|
| 548 | //!    will be constructed with the name given to the constructor and this | 
|---|
| 549 | //!    name, e.g. "DRIVE/CHANGE_STATE_TO_NEW_STATE" | 
|---|
| 550 | //! | 
|---|
| 551 | //! @param states | 
|---|
| 552 | //!    A comma sepeareted list of ints, e.g. "1, 4, 5, 9" with states | 
|---|
| 553 | //!    in which this new state transition is allowed and will be accepted. | 
|---|
| 554 | //! | 
|---|
| 555 | //! @param fmt | 
|---|
| 556 | //!    A format as defined by the dim system can be given for the command. | 
|---|
| 557 | //!    However, it has no real meaning except that it is stored within the | 
|---|
| 558 | //!    DimCommand object. However, the user must make sure that the data of | 
|---|
| 559 | //!    received commands is properly extracted. No check is done. | 
|---|
| 560 | // | 
|---|
| 561 | EventImp &StateMachineImp::AddEvent(int targetstate, const char *name, const char *states, const char *fmt) | 
|---|
| 562 | { | 
|---|
| 563 | EventImp *evt = CreateEvent(targetstate, name, fmt); | 
|---|
| 564 |  | 
|---|
| 565 | evt->AddAllowedStates(states); | 
|---|
| 566 |  | 
|---|
| 567 | #ifdef DEBUG | 
|---|
| 568 | Out() << ":   " << Time().GetAsStr("%H:%M:%S.%f"); | 
|---|
| 569 | Out() << " - Adding command " << evt->GetName(); | 
|---|
| 570 | if (evt->GetTargetState()>=0) | 
|---|
| 571 | Out() << " (transition to " << GetStateDescription(evt->GetTargetState()) << ")"; | 
|---|
| 572 | Out() << endl; | 
|---|
| 573 | #endif | 
|---|
| 574 |  | 
|---|
| 575 | fListOfEvents.push_back(evt); | 
|---|
| 576 |  | 
|---|
| 577 | return *evt; | 
|---|
| 578 | } | 
|---|
| 579 |  | 
|---|
| 580 | // -------------------------------------------------------------------------- | 
|---|
| 581 | // | 
|---|
| 582 | //! Calling this function, a new (named) event is added to the state | 
|---|
| 583 | //! machine. Therefore an instance of type DimEvent is created and added | 
|---|
| 584 | //! to the list of available commands fListOfEvents. | 
|---|
| 585 | //! | 
|---|
| 586 | //! @param targetstate | 
|---|
| 587 | //!    Defines the target state (or name) of the new event. If \b must be | 
|---|
| 588 | //!    greater or equal zero. A negative target state is used to flag | 
|---|
| 589 | //!    commands which do not initiate a state transition. If this is | 
|---|
| 590 | //!    desired use the unnamed version of AddEvent instead. | 
|---|
| 591 | //! | 
|---|
| 592 | //! @param name | 
|---|
| 593 | //!    The command name which should initiate the transition. The DimCommand | 
|---|
| 594 | //!    will be constructed with the name given to the constructor and this | 
|---|
| 595 | //!    name, e.g. "DRIVE/CHANGE_STATE_TO_NEW_STATE" | 
|---|
| 596 | //! | 
|---|
| 597 | //! @param s1, s2, s3, s4, s5 | 
|---|
| 598 | //!    A list of states from which a transition to targetstate is allowed | 
|---|
| 599 | //!    by this command. | 
|---|
| 600 | // | 
|---|
| 601 | EventImp &StateMachineImp::AddEvent(int targetstate, const char *name, int s1, int s2, int s3, int s4, int s5) | 
|---|
| 602 | { | 
|---|
| 603 | ostringstream str; | 
|---|
| 604 | str << s1 << ' '  << s2 << ' ' << s3 << ' ' << s4 << ' ' << s5; | 
|---|
| 605 | return AddEvent(targetstate, name, str.str().c_str(), ""); | 
|---|
| 606 | } | 
|---|
| 607 |  | 
|---|
| 608 | // -------------------------------------------------------------------------- | 
|---|
| 609 | // | 
|---|
| 610 | //! Calling this function, a new (named) event is added to the state | 
|---|
| 611 | //! machine. Therefore an instance of type DimEvent is created and added | 
|---|
| 612 | //! to the list of available commands fListOfEvents. | 
|---|
| 613 | //! | 
|---|
| 614 | //! @param targetstate | 
|---|
| 615 | //!    Defines the target state (or name) of the new event. If \b must be | 
|---|
| 616 | //!    greater or equal zero. A negative target state is used to flag | 
|---|
| 617 | //!    commands which do not initiate a state transition. If this is | 
|---|
| 618 | //!    desired use the unnamed version of AddEvent instead. | 
|---|
| 619 | //! | 
|---|
| 620 | //! @param name | 
|---|
| 621 | //!    The command name which should initiate the transition. The DimCommand | 
|---|
| 622 | //!    will be constructed with the name given to the constructor and this | 
|---|
| 623 | //!    name, e.g. "DRIVE/CHANGE_STATE_TO_NEW_STATE" | 
|---|
| 624 | //! | 
|---|
| 625 | //! @param fmt | 
|---|
| 626 | //!    A format as defined by the dim system can be given for the command. | 
|---|
| 627 | //!    However, it has no real meaning except that it is stored within the | 
|---|
| 628 | //!    DimCommand object. However, the user must make sure that the data of | 
|---|
| 629 | //!    received commands is properly extracted. No check is done. | 
|---|
| 630 | //! | 
|---|
| 631 | //! @param s1, s2, s3, s4, s5 | 
|---|
| 632 | //!    A list of states from which a transition to targetstate is allowed | 
|---|
| 633 | //!    by this command. | 
|---|
| 634 | // | 
|---|
| 635 | EventImp &StateMachineImp::AddEvent(int targetstate, const char *name, const char *fmt, int s1, int s2, int s3, int s4, int s5) | 
|---|
| 636 | { | 
|---|
| 637 | ostringstream str; | 
|---|
| 638 | str << s1 << ' '  << s2 << ' ' << s3 << ' ' << s4 << ' ' << s5; | 
|---|
| 639 | return AddEvent(targetstate, name, str.str().c_str(), fmt); | 
|---|
| 640 | } | 
|---|
| 641 |  | 
|---|
| 642 | // -------------------------------------------------------------------------- | 
|---|
| 643 | // | 
|---|
| 644 | //! This function calls AddEvent with a target-state of -1 (unnamed | 
|---|
| 645 | //! event). This shell be used for configuration commands. As well as | 
|---|
| 646 | //! in AddEvent the states in which such a configuration command is | 
|---|
| 647 | //! accepted can be given. | 
|---|
| 648 | //! | 
|---|
| 649 | //! @param name | 
|---|
| 650 | //!    The command name which should initiate the transition. The DimCommand | 
|---|
| 651 | //!    will be constructed with the name given to the constructor and this | 
|---|
| 652 | //!    name, e.g. "DRIVE/CHANGE_STATE_TO_NEW_STATE" | 
|---|
| 653 | //! | 
|---|
| 654 | //! @param states | 
|---|
| 655 | //!    A comma sepeareted list of ints, e.g. "1, 4, 5, 9" with states | 
|---|
| 656 | //!    in which this new state transition is allowed and will be accepted. | 
|---|
| 657 | //! | 
|---|
| 658 | //! @param fmt | 
|---|
| 659 | //!    A format as defined by the dim system can be given for the command. | 
|---|
| 660 | //!    However, it has no real meaning except that it is stored within the | 
|---|
| 661 | //!    DimCommand object. However, the user must make sure that the data of | 
|---|
| 662 | //!    received commands is properly extracted. No check is done. | 
|---|
| 663 | //! | 
|---|
| 664 | EventImp &StateMachineImp::AddEvent(const char *name, const char *states, const char *fmt) | 
|---|
| 665 | { | 
|---|
| 666 | return AddEvent(-1, name, states, fmt); | 
|---|
| 667 | } | 
|---|
| 668 |  | 
|---|
| 669 | // -------------------------------------------------------------------------- | 
|---|
| 670 | // | 
|---|
| 671 | //! This function calls AddEvent with a target-state of -1 (unnamed | 
|---|
| 672 | //! event). This shell be used for configuration commands. As well as | 
|---|
| 673 | //! in AddEvent the states in which such a configuration command is | 
|---|
| 674 | //! accepted can be given. | 
|---|
| 675 | //! | 
|---|
| 676 | //! @param name | 
|---|
| 677 | //!    The command name which should initiate the transition. The DimCommand | 
|---|
| 678 | //!    will be constructed with the name given to the constructor and this | 
|---|
| 679 | //!    name, e.g. "DRIVE/CHANGE_STATE_TO_NEW_STATE" | 
|---|
| 680 | //! | 
|---|
| 681 | //! @param s1, s2, s3, s4, s5 | 
|---|
| 682 | //!    A list of states from which a transition to targetstate is allowed | 
|---|
| 683 | //!    by this command. | 
|---|
| 684 | // | 
|---|
| 685 | EventImp &StateMachineImp::AddEvent(const char *name, int s1, int s2, int s3, int s4, int s5) | 
|---|
| 686 | { | 
|---|
| 687 | return AddEvent(-1, name, s1, s2, s3, s4, s5); | 
|---|
| 688 | } | 
|---|
| 689 |  | 
|---|
| 690 | // -------------------------------------------------------------------------- | 
|---|
| 691 | // | 
|---|
| 692 | //! This function calls AddEvent with a target-state of -1 (unnamed | 
|---|
| 693 | //! event). This shell be used for configuration commands. As well as | 
|---|
| 694 | //! in AddEvent the states in which such a configuration command is | 
|---|
| 695 | //! accepted can be given. | 
|---|
| 696 | //! | 
|---|
| 697 | //! @param name | 
|---|
| 698 | //!    The command name which should initiate the transition. The DimCommand | 
|---|
| 699 | //!    will be constructed with the name given to the constructor and this | 
|---|
| 700 | //!    name, e.g. "DRIVE/CHANGE_STATE_TO_NEW_STATE" | 
|---|
| 701 | //! | 
|---|
| 702 | //! @param fmt | 
|---|
| 703 | //!    A format as defined by the dim system can be given for the command. | 
|---|
| 704 | //!    However, it has no real meaning except that it is stored within the | 
|---|
| 705 | //!    DimCommand object. However, the user must make sure that the data of | 
|---|
| 706 | //!    received commands is properly extracted. No check is done. | 
|---|
| 707 | //! | 
|---|
| 708 | //! @param s1, s2, s3, s4, s5 | 
|---|
| 709 | //!    A list of states from which a transition to targetstate is allowed | 
|---|
| 710 | //!    by this command. | 
|---|
| 711 | // | 
|---|
| 712 | EventImp &StateMachineImp::AddEvent(const char *name, const char *fmt, int s1, int s2, int s3, int s4, int s5) | 
|---|
| 713 | { | 
|---|
| 714 | return AddEvent(-1, name, fmt, s1, s2, s3, s4, s5); | 
|---|
| 715 | } | 
|---|
| 716 |  | 
|---|
| 717 | // -------------------------------------------------------------------------- | 
|---|
| 718 | // | 
|---|
| 719 | //! To be able to name states, i.e. present the current state in human | 
|---|
| 720 | //! readable for to the user, a string can be assigned to each state. | 
|---|
| 721 | //! For each state this function can be called only once, i.e. state name | 
|---|
| 722 | //! cannot be overwritten. | 
|---|
| 723 | //! | 
|---|
| 724 | //! Be aware that two states should not have the same name! | 
|---|
| 725 | //! | 
|---|
| 726 | //! @param state | 
|---|
| 727 | //!    Number of the state to which a name should be assigned | 
|---|
| 728 | //! | 
|---|
| 729 | //! @param name | 
|---|
| 730 | //!    A name which should be assigned to the state, e.g. "Tracking" | 
|---|
| 731 | //! | 
|---|
| 732 | //! @param doc | 
|---|
| 733 | //!    A explanatory text describing the state | 
|---|
| 734 | //! | 
|---|
| 735 | void StateMachineImp::AddStateName(const int state, const std::string &name, const std::string &doc) | 
|---|
| 736 | { | 
|---|
| 737 | if (fStateNames[state].first.empty()) | 
|---|
| 738 | fStateNames[state] = make_pair(name, doc); | 
|---|
| 739 | } | 
|---|
| 740 |  | 
|---|
| 741 | // -------------------------------------------------------------------------- | 
|---|
| 742 | // | 
|---|
| 743 | //! @param state | 
|---|
| 744 | //!    The state for which the name should be returned. | 
|---|
| 745 | //! | 
|---|
| 746 | //! @returns | 
|---|
| 747 | //!    The state name as stored in fStateNames is returned, corresponding | 
|---|
| 748 | //!    to the state given. If no name exists the number is returned | 
|---|
| 749 | //!    as string. | 
|---|
| 750 | //! | 
|---|
| 751 | const string StateMachineImp::GetStateName(int state) const | 
|---|
| 752 | { | 
|---|
| 753 | const StateNames::const_iterator i = fStateNames.find(state); | 
|---|
| 754 |  | 
|---|
| 755 | ostringstream s; | 
|---|
| 756 | s << state; | 
|---|
| 757 | return i==fStateNames.end() || i->second.first.empty() ? s.str() : i->second.first; | 
|---|
| 758 | } | 
|---|
| 759 |  | 
|---|
| 760 | // -------------------------------------------------------------------------- | 
|---|
| 761 | // | 
|---|
| 762 | //! @param state | 
|---|
| 763 | //!    The state for which the name should be returned. | 
|---|
| 764 | //! | 
|---|
| 765 | //! @returns | 
|---|
| 766 | //!    The description of a state name as stored in fStateNames is returned, | 
|---|
| 767 | //!    corresponding to the state given. If no name exists an empty string is | 
|---|
| 768 | //!    returned. | 
|---|
| 769 | //! | 
|---|
| 770 | const string StateMachineImp::GetStateDesc(int state) const | 
|---|
| 771 | { | 
|---|
| 772 | const StateNames::const_iterator i = fStateNames.find(state); | 
|---|
| 773 | return i==fStateNames.end() ? "" : i->second.second; | 
|---|
| 774 | } | 
|---|
| 775 |  | 
|---|
| 776 | // -------------------------------------------------------------------------- | 
|---|
| 777 | // | 
|---|
| 778 | //! This functions works in analogy to GetStateName, but the state number | 
|---|
| 779 | //! is added in []-parenthesis after the state name if it is available. | 
|---|
| 780 | //! | 
|---|
| 781 | //! @param state | 
|---|
| 782 | //!    The state for which the name should be returned. | 
|---|
| 783 | //! | 
|---|
| 784 | //! @returns | 
|---|
| 785 | //!    The state name as stored in fStateName is returned corresponding | 
|---|
| 786 | //!    to the state given plus the state number added in []-parenthesis. | 
|---|
| 787 | //!    If no name exists the number is returned as string. | 
|---|
| 788 | //! | 
|---|
| 789 | // | 
|---|
| 790 | const string StateMachineImp::GetStateDescription(int state) const | 
|---|
| 791 | { | 
|---|
| 792 | const string &str = GetStateName(state); | 
|---|
| 793 |  | 
|---|
| 794 | ostringstream s; | 
|---|
| 795 | s << state; | 
|---|
| 796 | if (str==s.str()) | 
|---|
| 797 | return str; | 
|---|
| 798 |  | 
|---|
| 799 | return str.empty() ? s.str() : (str+'['+s.str()+']'); | 
|---|
| 800 | } | 
|---|
| 801 |  | 
|---|
| 802 | // -------------------------------------------------------------------------- | 
|---|
| 803 | // | 
|---|
| 804 | //! This function is a helpter function to do all the corresponding action | 
|---|
| 805 | //! if the state machine decides to change its state. | 
|---|
| 806 | //! | 
|---|
| 807 | //! If state is equal to the current state (fCurrentState) nothing is done. | 
|---|
| 808 | //! Then the service STATE (fSrcState) is updated with the new state | 
|---|
| 809 | //! and the text message and updateService() is called to distribute | 
|---|
| 810 | //! the update to all clients. | 
|---|
| 811 | //! | 
|---|
| 812 | //! In addition a log message is created and set via UpdateMsg. | 
|---|
| 813 | //! | 
|---|
| 814 | //! @param state | 
|---|
| 815 | //!    The new state which should be applied | 
|---|
| 816 | //! | 
|---|
| 817 | //! @param txt | 
|---|
| 818 | //!    A text corresponding to the state change which is distributed | 
|---|
| 819 | //!    together with the state itself for convinience. | 
|---|
| 820 | //! | 
|---|
| 821 | //! @param cmd | 
|---|
| 822 | //!    This argument can be used to give an additional name of the function | 
|---|
| 823 | //!    which is reponsible for the state change. It will be included in the | 
|---|
| 824 | //!    message | 
|---|
| 825 | //! | 
|---|
| 826 | //! @return | 
|---|
| 827 | //!    return the new state which was set or -1 in case of no change | 
|---|
| 828 | // | 
|---|
| 829 | string StateMachineImp::SetCurrentState(int state, const char *txt, const std::string &cmd) | 
|---|
| 830 | { | 
|---|
| 831 | if (state==fCurrentState) | 
|---|
| 832 | { | 
|---|
| 833 | Out() << " -- " << Time().GetAsStr() << ": State " << GetStateDescription(state) << " already set... "; | 
|---|
| 834 | if (!cmd.empty()) | 
|---|
| 835 | Out() << "'" << cmd << "' ignored."; | 
|---|
| 836 | Out() << endl; | 
|---|
| 837 | return ""; | 
|---|
| 838 | } | 
|---|
| 839 |  | 
|---|
| 840 | const int old = fCurrentState; | 
|---|
| 841 |  | 
|---|
| 842 | const string nold = GetStateDescription(old); | 
|---|
| 843 | const string nnew = GetStateDescription(state); | 
|---|
| 844 |  | 
|---|
| 845 | string msg = nnew + " " + txt; | 
|---|
| 846 | if (!cmd.empty()) | 
|---|
| 847 | msg += " (" + cmd + ")"; | 
|---|
| 848 |  | 
|---|
| 849 | fCurrentState = state; | 
|---|
| 850 |  | 
|---|
| 851 | // State might have changed already again... | 
|---|
| 852 | // Not very likely, but possible. That's why state is used | 
|---|
| 853 | // instead of fCurrentState. | 
|---|
| 854 |  | 
|---|
| 855 | ostringstream str; | 
|---|
| 856 | str << "State Transition from " << nold << " to " << nnew << " (" << txt; | 
|---|
| 857 | if (!cmd.empty()) | 
|---|
| 858 | str << ": " << cmd; | 
|---|
| 859 | str << ")"; | 
|---|
| 860 | Message(str); | 
|---|
| 861 |  | 
|---|
| 862 | return msg; | 
|---|
| 863 | } | 
|---|
| 864 |  | 
|---|
| 865 | // -------------------------------------------------------------------------- | 
|---|
| 866 | // | 
|---|
| 867 | //! This function handles a new state issued by one of the event handlers. | 
|---|
| 868 | //! | 
|---|
| 869 | //! @param newstate | 
|---|
| 870 | //!    A possible new state | 
|---|
| 871 | //! | 
|---|
| 872 | //! @param evt | 
|---|
| 873 | //!    A pointer to the event which was responsible for the state change, | 
|---|
| 874 | //!    NULL if no event was responsible. | 
|---|
| 875 | //! | 
|---|
| 876 | //! @param txt | 
|---|
| 877 | //!    Text which is issued if the current state has changed and the new | 
|---|
| 878 | //!    state is identical to the target state as stored in the event | 
|---|
| 879 | //!    reference, or when no alternative text was given, or the pointer to | 
|---|
| 880 | //!    evt is NULL. | 
|---|
| 881 | //! | 
|---|
| 882 | //! @param alt | 
|---|
| 883 | //!    An alternative text which is issues when the newstate of a state change | 
|---|
| 884 | //!    doesn't match the expected target state. | 
|---|
| 885 | //! | 
|---|
| 886 | //! @returns | 
|---|
| 887 | //!    false if newstate is kSM_FatalError, true otherwise | 
|---|
| 888 | // | 
|---|
| 889 | bool StateMachineImp::HandleNewState(int newstate, const EventImp *evt, | 
|---|
| 890 | const char *txt, const char *alt) | 
|---|
| 891 | { | 
|---|
| 892 | if (newstate==kSM_FatalError) | 
|---|
| 893 | return false; | 
|---|
| 894 |  | 
|---|
| 895 | if (newstate==fCurrentState) | 
|---|
| 896 | return true; | 
|---|
| 897 |  | 
|---|
| 898 | if (!evt || !alt || newstate==evt->GetTargetState()) | 
|---|
| 899 | SetCurrentState(newstate, txt, evt ? evt->GetName() : ""); | 
|---|
| 900 | else | 
|---|
| 901 | SetCurrentState(newstate, alt, evt->GetName()); | 
|---|
| 902 |  | 
|---|
| 903 | return true; | 
|---|
| 904 | } | 
|---|
| 905 |  | 
|---|
| 906 | // -------------------------------------------------------------------------- | 
|---|
| 907 | // | 
|---|
| 908 | //! This is the event handler. Depending on the type of event it calles | 
|---|
| 909 | //! the function associated with the event, the Transition() or | 
|---|
| 910 | //! Configure() function. | 
|---|
| 911 | //! | 
|---|
| 912 | //! It first checks if the given even is valid in the current state. If | 
|---|
| 913 | //! it is not valid the function returns with true. | 
|---|
| 914 | //! | 
|---|
| 915 | //! If it is valid, it is checked whether a function is associated with | 
|---|
| 916 | //! the event. If this is the case, evt.Exec() is called and HandleNewState | 
|---|
| 917 | //! called with its return value. | 
|---|
| 918 | //! | 
|---|
| 919 | //! If the event's target state is negative (unnamed Event) the Configure() | 
|---|
| 920 | //! function is called with the event as argument and HandleNewState with | 
|---|
| 921 | //! its returned new state. | 
|---|
| 922 | //! | 
|---|
| 923 | //! If the event's target state is 0 or positive (named Event) the | 
|---|
| 924 | //! Transition() function is called with the event as argument and | 
|---|
| 925 | //! HandleNewState with its returned new state. | 
|---|
| 926 | //! | 
|---|
| 927 | //! In all three cases the return value of HandleNewState is returned. | 
|---|
| 928 | //! | 
|---|
| 929 | //! Any of the three commands should usually return the current state | 
|---|
| 930 | //! or (in case of the Transition() command) return the new state. However, | 
|---|
| 931 | //! all three command can issue a state change by returning a new state. | 
|---|
| 932 | //! However, this will just change the internal state. Any action which | 
|---|
| 933 | //! is connected with the state change must have been executed already. | 
|---|
| 934 | //! | 
|---|
| 935 | //! @param evt | 
|---|
| 936 | //!    a reference to the event which should be handled | 
|---|
| 937 | //! | 
|---|
| 938 | //! @returns | 
|---|
| 939 | //!    false in case one of the commands changed the state to kSM_FataError, | 
|---|
| 940 | //!    true otherwise | 
|---|
| 941 | // | 
|---|
| 942 | bool StateMachineImp::HandleEvent(const EventImp &evt) | 
|---|
| 943 | { | 
|---|
| 944 | Debug("Handle: "+evt.GetName()); | 
|---|
| 945 |  | 
|---|
| 946 | // Get the new state from the command | 
|---|
| 947 | const int commandstate = evt.GetTargetState(); | 
|---|
| 948 |  | 
|---|
| 949 | // Check if the received command is allow in the current state | 
|---|
| 950 | if (!evt.IsStateAllowed(fCurrentState)) | 
|---|
| 951 | { | 
|---|
| 952 | ostringstream msg; | 
|---|
| 953 | msg << evt.GetName() << ": Not allowed in state "; | 
|---|
| 954 | msg << GetStateDescription() << "... ignored."; | 
|---|
| 955 | Warn(msg); | 
|---|
| 956 | return true; | 
|---|
| 957 | } | 
|---|
| 958 |  | 
|---|
| 959 | if (evt.HasFunc()) | 
|---|
| 960 | return HandleNewState(evt.ExecFunc(), &evt, | 
|---|
| 961 | "by ExecFunc function-call"); | 
|---|
| 962 |  | 
|---|
| 963 | // Check if this is a configuration command (a command which | 
|---|
| 964 | // intention is not to change the state of our state-machine | 
|---|
| 965 | if (commandstate<0) | 
|---|
| 966 | return HandleNewState(Configure(evt), &evt, "by Configure-command"); | 
|---|
| 967 | else | 
|---|
| 968 | return HandleNewState(Transition(evt), &evt, | 
|---|
| 969 | "by Transition-command (expected)", | 
|---|
| 970 | "by Transition-command (unexpected)"); | 
|---|
| 971 |  | 
|---|
| 972 | // This is a fatal error, because it can never happen | 
|---|
| 973 | return false; | 
|---|
| 974 | } | 
|---|
| 975 |  | 
|---|
| 976 | // -------------------------------------------------------------------------- | 
|---|
| 977 | // | 
|---|
| 978 | //! This is the main loop, or what could be called the running state | 
|---|
| 979 | //! machine. The flow diagram below shows what the loop is actually doing. | 
|---|
| 980 | //! It's main purpose is to serialize command excecution and the main | 
|---|
| 981 | //! loop in the state machine (e.g. the tracking loop) | 
|---|
| 982 | //! | 
|---|
| 983 | //! Leaving the loop can be forced by setting fExitRequested to another | 
|---|
| 984 | //! value than zero. This is done automatically if dim's EXIT command | 
|---|
| 985 | //! is received or can be forced by calling Stop(). | 
|---|
| 986 | //! | 
|---|
| 987 | //! As long as no new command arrives the Execute() command is called | 
|---|
| 988 | //! continously. This should implement the current action which | 
|---|
| 989 | //! should be performed in the current state, e.g. calculating a | 
|---|
| 990 | //! new command value and sending it to the hardware. | 
|---|
| 991 | //! | 
|---|
| 992 | //! If a command is received it is put into the fifo by the commandHandler(). | 
|---|
| 993 | //! The main loop now checks the fifo. If commands are in the fifo, it is | 
|---|
| 994 | //! checked whether the command is valid ithin this state or not. If it is | 
|---|
| 995 | //! not valid it is ignored. If it is valid the corresponding action | 
|---|
| 996 | //! is performed. This can either be a call to Configure() (when no state | 
|---|
| 997 | //! change is connected to the command) or Transition() (if the command | 
|---|
| 998 | //! involves a state change). | 
|---|
| 999 | //! In both cases areference to the received command (Command) is | 
|---|
| 1000 | //! passed to the function. Note that after the functions have finished | 
|---|
| 1001 | //! the command will go out of scope and be deleted. | 
|---|
| 1002 | //! | 
|---|
| 1003 | //! None of the commands should take to long for execution. Otherwise the | 
|---|
| 1004 | //! response time of the main loop will become too slow. | 
|---|
| 1005 | //! | 
|---|
| 1006 | //! Any of the three commands should usually return the current state | 
|---|
| 1007 | //! or (in case of the Transition() command) return the new state. However, | 
|---|
| 1008 | //! all three command can issue a state change by returning a new state. | 
|---|
| 1009 | //! However, this will just change the internal state. Any action which | 
|---|
| 1010 | //! is connected with the state change must have been executed already. | 
|---|
| 1011 | //! | 
|---|
| 1012 | //! | 
|---|
| 1013 | //! | 
|---|
| 1014 | //!  \dot | 
|---|
| 1015 | //!   digraph Run { | 
|---|
| 1016 | //!       node  [ shape=record, fontname=Helvetica, fontsize=10 ]; | 
|---|
| 1017 | //!       edge  [ labelfontname=Helvetica, labelfontsize=8 ]; | 
|---|
| 1018 | //!       start0 [ label="Run()" style="rounded"]; | 
|---|
| 1019 | //!       start1 [ label="fExitRequested=0\nfRunning=true\nSetCurrentState(kSM_Ready)"]; | 
|---|
| 1020 | //!       cond1  [ label="Is fExitRequested==0?"]; | 
|---|
| 1021 | //!       exec   [ label="HandleNewState(Execute())"]; | 
|---|
| 1022 | //!       fifo   [ label="Any event in FIFO?"]; | 
|---|
| 1023 | //!       get    [ label="Get event from FIFO\n Is event allowed within the current state?" ]; | 
|---|
| 1024 | //!       handle [ label="HandleEvent()" ]; | 
|---|
| 1025 | //!       exit1  [ label="fRunning=false\nSetCurrentState(kSM_FatalError)\n return -1" style="rounded"]; | 
|---|
| 1026 | //!       exit2  [ label="fRunning=false\nSetCurrentState(kSM_NotReady)\n return fExitRequested-1" style="rounded"]; | 
|---|
| 1027 | //! | 
|---|
| 1028 | //!       start0   -> start1   [ weight=8 ]; | 
|---|
| 1029 | //!       start1   -> cond1    [ weight=8 ]; | 
|---|
| 1030 | //! | 
|---|
| 1031 | //!       cond1:e  -> exit2:n  [ taillabel="true"  ]; | 
|---|
| 1032 | //!       cond1    -> exec     [ taillabel="false"  weight=8 ]; | 
|---|
| 1033 | //! | 
|---|
| 1034 | //!       exec     -> fifo     [ taillabel="true"   weight=8 ]; | 
|---|
| 1035 | //!       exec:e   -> exit1:e  [ taillabel="false" ]; | 
|---|
| 1036 | //! | 
|---|
| 1037 | //!       fifo     -> cond1    [ taillabel="false" ]; | 
|---|
| 1038 | //!       fifo     -> get      [ taillabel="true"   weight=8 ]; | 
|---|
| 1039 | //! | 
|---|
| 1040 | //!       get      -> handle   [ taillabel="true"  ]; | 
|---|
| 1041 | //! | 
|---|
| 1042 | //!       handle:s -> exit1:n  [ taillabel="false"  weight=8 ]; | 
|---|
| 1043 | //!       handle   -> cond1    [ taillabel="true"  ]; | 
|---|
| 1044 | //!   } | 
|---|
| 1045 | //!   \enddot | 
|---|
| 1046 | //! | 
|---|
| 1047 | //! @param dummy | 
|---|
| 1048 | //!    If this parameter is set to treu then no action is executed | 
|---|
| 1049 | //!    and now events are dispatched from the event list. It is usefull | 
|---|
| 1050 | //!    if functions are assigned directly to any event to simulate | 
|---|
| 1051 | //!    a running loop (e.g. block until Stop() was called or fExitRequested | 
|---|
| 1052 | //!    was set by an EXIT command.  If dummy==true, fRunning is not set | 
|---|
| 1053 | //!    to true to allow handling events directly from the event handler. | 
|---|
| 1054 | //! | 
|---|
| 1055 | //! @returns | 
|---|
| 1056 | //!    In the case of a a fatal error -1 is returned, fExitRequested-1 in all | 
|---|
| 1057 | //!    other cases (This corresponds to the exit code either received by the | 
|---|
| 1058 | //!    EXIT event or given to the Stop() function) | 
|---|
| 1059 | //! | 
|---|
| 1060 | //! @todo  Fix docu (kSM_SetReady, HandleEvent) | 
|---|
| 1061 | // | 
|---|
| 1062 | int StateMachineImp::Run(bool dummy) | 
|---|
| 1063 | { | 
|---|
| 1064 | if (fCurrentState>=kSM_Ready) | 
|---|
| 1065 | { | 
|---|
| 1066 | Error("Run() can only be called in the NotReady state."); | 
|---|
| 1067 | return -1; | 
|---|
| 1068 | } | 
|---|
| 1069 |  | 
|---|
| 1070 | if (!fExitRequested) | 
|---|
| 1071 | { | 
|---|
| 1072 | fRunning = !dummy; | 
|---|
| 1073 |  | 
|---|
| 1074 | SetCurrentState(kSM_Ready, "by Run()"); | 
|---|
| 1075 |  | 
|---|
| 1076 | while (!fExitRequested) | 
|---|
| 1077 | { | 
|---|
| 1078 | usleep(1); | 
|---|
| 1079 | if (dummy) | 
|---|
| 1080 | continue; | 
|---|
| 1081 |  | 
|---|
| 1082 | // Execute a step in the current state of the state machine | 
|---|
| 1083 | if (!HandleNewState(Execute(), "by Execute-command")) | 
|---|
| 1084 | break; | 
|---|
| 1085 |  | 
|---|
| 1086 | // If the command stack is empty go on with processing in the | 
|---|
| 1087 | // current state | 
|---|
| 1088 | if (IsQueueEmpty()) | 
|---|
| 1089 | continue; | 
|---|
| 1090 |  | 
|---|
| 1091 | // Pop the next command which arrived from the stack | 
|---|
| 1092 | const auto_ptr<Event> cmd(PopEvent()); | 
|---|
| 1093 |  | 
|---|
| 1094 | if (!HandleEvent(*cmd)) | 
|---|
| 1095 | break; | 
|---|
| 1096 | } | 
|---|
| 1097 |  | 
|---|
| 1098 | fRunning = false; | 
|---|
| 1099 |  | 
|---|
| 1100 | if (!fExitRequested) | 
|---|
| 1101 | { | 
|---|
| 1102 | Fatal("Fatal Error occured... shutting down."); | 
|---|
| 1103 | return -1; | 
|---|
| 1104 | } | 
|---|
| 1105 |  | 
|---|
| 1106 | SetCurrentState(kSM_NotReady, "due to return from Run()."); | 
|---|
| 1107 | } | 
|---|
| 1108 |  | 
|---|
| 1109 | const int exitcode = fExitRequested-1; | 
|---|
| 1110 |  | 
|---|
| 1111 | // Prepare for next call | 
|---|
| 1112 | fExitRequested = 0; | 
|---|
| 1113 |  | 
|---|
| 1114 | return exitcode; | 
|---|
| 1115 | } | 
|---|
| 1116 |  | 
|---|
| 1117 | // -------------------------------------------------------------------------- | 
|---|
| 1118 | // | 
|---|
| 1119 | //! This function can be called to stop the loop of a running state machine. | 
|---|
| 1120 | //! Run() will then return with a return value corresponding to the value | 
|---|
| 1121 | //! given as argument. | 
|---|
| 1122 | //! | 
|---|
| 1123 | //! Note that this is a dangerous operation, because as soon as one of the | 
|---|
| 1124 | //! three state machine commands returns (Execute(), Configure() and | 
|---|
| 1125 | //! Transition()) the loop will be left and Run(9 will return. The program | 
|---|
| 1126 | //! is then responsible of correctly cleaning up the mess which might be left | 
|---|
| 1127 | //! behind. | 
|---|
| 1128 | //! | 
|---|
| 1129 | //! @param code | 
|---|
| 1130 | //!    int with which Run() should return when returning. | 
|---|
| 1131 | // | 
|---|
| 1132 | void StateMachineImp::Stop(int code) | 
|---|
| 1133 | { | 
|---|
| 1134 | fExitRequested = code+1; | 
|---|
| 1135 | } | 
|---|