state_machine.cpp

Go to the documentation of this file.
00001 /*****************************************************************************\
00002 *                                                                             *
00003 *  Name   : state_machine                                                     *
00004 *  Author : Chris Koeritz                                                     *
00005 *                                                                             *
00006 *******************************************************************************
00007 * Copyright (c) 1992-$now By Author.  This program is free software; you can  *
00008 * redistribute it and/or modify it under the terms of the GNU General Public  *
00009 * License as published by the Free Software Foundation; either version 2 of   *
00010 * the License or (at your option) any later version.  This is online at:      *
00011 *     http://www.fsf.org/copyleft/gpl.html                                    *
00012 * Please send any updates to: fred@gruntose.com                               *
00013 \*****************************************************************************/
00014 
00015 #include "state_machine.h"
00016 
00017 #include <basis/array.h>
00018 #include <basis/functions.h>
00019 #include <basis/guards.h>
00020 #include <basis/astring.h>
00021 #include <loggers/program_wide_logger.h>
00022 #include <timely/time_stamp.h>
00023 
00024 using namespace basis;
00025 using namespace loggers;
00026 using namespace timely;
00027 
00028 namespace processes {
00029 
00031 
00032 //#define DEBUG_STATE_MACHINE
00033   // uncomment if you want the debugging version.
00034 
00035 //hmmm: implement logging...
00036 #undef LOG
00037 #ifndef DEBUG_STATE_MACHINE
00038   #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
00039 #else
00040   #define LOG(to_print) {}
00041 #endif
00042 
00044 
00045 // checks whether the machine passed in is valid or not.
00046 #define CHECK_VALID(m) \
00047   if (!m._current) return false; \
00048   if (!m._last) return false
00049 
00050 // checks whether the current and next states exist or not.
00051 #define CHECK_STATES \
00052   if (!current) return false; \
00053   if (!next) return false; \
00054   int indy = state_index(current); \
00055   if (negative(indy)) return false; \
00056   if (negative(state_index(next))) return false
00057 
00058 // moves to a new state and resets the new state's timestamp.
00059 #define MOVE_STATE(m, next, type, trigger) \
00060   m._last = m._current; \
00061   m._current = next; \
00062   m._type = type; \
00063   m._trig = trigger; \
00064   m._start->reset()
00065 
00066 // locates a state or returns.
00067 #define FIND_STATE(state) \
00068   int indy = state_index(state); \
00069   if (negative(indy)) return
00070 
00072 
00073 struct override { int current; int next; int duration;
00074   override(int _current = 0, int _next = 0, int _duration = 0)
00075     : current(_current), next(_next), duration(_duration) {}
00076 };
00077 
00078 struct transition_info {
00079   enum transition_type { SIMPLE, RANGE, TIMED };
00080   transition_type type;
00081   int next_state;
00082   int low_trigger, high_trigger;
00083   int time_span;
00084 
00085   transition_info() {}  // blank.
00086   transition_info(int next) : type(SIMPLE), next_state(next) {}
00087   transition_info(int next, int time) : type(TIMED), next_state(next),
00088       time_span(time) {}
00089   transition_info(int next, int low, int high) : type(RANGE),
00090       next_state(next), low_trigger(low), high_trigger(high) {}
00091 };
00092 
00093 struct state_info {
00094   int state_id;  // id for this state.
00095   array<transition_info> transitions;
00096 
00097   state_info() : state_id(0) {}  // blank.
00098   state_info(int state_id_in) : state_id(state_id_in) {}
00099 };
00100 
00102 
00103 class state_machine_override_array : public array<override> {};
00104 class state_machine_state_array : public array<state_info> {};
00105 
00107 
00108 state_machine::state_machine()
00109 : _current(0),
00110   _last(0),
00111   _trig(0),
00112   _type(SIMPLE),
00113   _start(new time_stamp()),
00114   _name(new astring()),
00115   _overrides(new state_machine_override_array)
00116 {}
00117 
00118 state_machine::state_machine(const state_machine &to_copy)
00119 : root_object(),
00120   _current(0),
00121   _last(0),
00122   _trig(0),
00123   _type(SIMPLE),
00124   _start(new time_stamp()),
00125   _name(new astring()),
00126   _overrides(new state_machine_override_array)
00127 { *this = to_copy; }
00128 
00129 state_machine::~state_machine()
00130 {
00131   WHACK(_start);
00132   WHACK(_name);
00133   WHACK(_overrides);
00134 }
00135 
00136 int state_machine::update() { return 0; }
00137 
00138 void state_machine::set_name(const astring &name) { *_name = name; }
00139 
00140 astring state_machine::get_name() const { return *_name; }
00141 
00142 state_machine &state_machine::operator = (const state_machine &to_copy)
00143 {
00144   if (&to_copy == this) return *this;
00145   _current = to_copy._current;
00146   _last = to_copy._last;
00147   _trig = to_copy._trig;
00148   _type = to_copy._type;
00149   *_start = *to_copy._start;
00150   *_name = *to_copy._name;
00151   *_overrides = *to_copy._overrides;
00152 //careful when overrides becomes hidden internal type.
00153   return *this;
00154 }
00155 
00156 int state_machine::duration_index(int current, int next) const
00157 {
00158   for (int i = 0; i < _overrides->length(); i++)
00159     if ( ((*_overrides)[i].current == current)
00160         && ((*_overrides)[i].next == next) )
00161       return i;
00162   return common::NOT_FOUND;
00163 }
00164 
00165 void state_machine::set_state(int new_current, int new_last, int trigger,
00166     transition_types type)
00167 {
00168   _current = new_current;
00169   _last = new_last;
00170   _trig = trigger;
00171   _type = type;
00172   _start->reset();
00173 }
00174 
00175 void state_machine::override_timing(int current, int next, int duration)
00176 {
00177   int indy = duration_index(current, next);
00178   if (non_negative(indy)) {
00179     // found an existing record for this current/next pair.
00180     if (!duration) {
00181       // zero duration means removal.
00182       _overrides->zap(indy, indy);
00183       return;
00184     }
00185     // reset the duration.
00186     (*_overrides)[indy].duration = duration;
00187     return;
00188   }
00189   // no existing record, so add one.
00190   *_overrides += override(current, next, duration);
00191 }
00192 
00193 int state_machine::duration_override(int current, int next) const
00194 {
00195   int indy = duration_index(current, next);
00196   if (negative(indy)) return 0;
00197   return (*_overrides)[indy].duration;
00198 }
00199 
00200 time_stamp state_machine::start() const { return *_start; }
00201 
00203 
00204 transition_map::transition_map()
00205 : _valid(false),
00206   _start_state(0),
00207   _state_list(new state_machine_state_array)
00208 {}
00209 
00210 transition_map::~transition_map()
00211 {
00212   _valid = false;
00213   WHACK(_state_list);
00214 }
00215 
00216 // informational functions:
00217 
00218 int transition_map::states() const { return _state_list->length(); }
00219 
00220 int transition_map::state_index(int state_id) const
00221 {
00222   for (int i = 0; i < states(); i++)
00223     if ((*_state_list)[i].state_id == state_id) return i;
00224   return common::NOT_FOUND;
00225 }
00226 
00227 int transition_map::transition_index(int state_index, int next, int &start)
00228 {
00229   bounds_return(state_index, 0, states() - 1, common::BAD_INPUT);
00230   state_info &state = (*_state_list)[state_index];
00231   bounds_return(start, 0, state.transitions.length() - 1, common::BAD_INPUT);
00232   // loop over the transitions by using our external index.
00233   for (start = start; start < state.transitions.length(); start++)
00234     if (state.transitions[start].next_state == next) {
00235       start++;  // position it after this index.
00236       return start - 1;  // return this index.
00237     }
00238   return common::NOT_FOUND;
00239 }
00240 
00241 // configurational functions:
00242 
00243 void transition_map::reconfigure() { _valid = false; }
00244 
00245 outcome transition_map::validate(int &examine)
00246 {
00247   // check for a bad starting state...
00248   examine = _start_state;
00249   FIND_STATE(_start_state) BAD_START;
00250 
00251   if (!check_reachability(examine)) return UNREACHABLE;
00252     // a state is unreachable from the starting state.
00253   if (!check_overlapping(examine)) return OVERLAPPING_RANGES;
00254     // bad (overlapping) ranges were found in one state.
00255   _valid = true;  // set us to operational.
00256   return OKAY;
00257 }
00258 
00259 bool transition_map::add_state(int state_number)
00260 {
00261   if (valid()) return false;  // this is operational; no more config!
00262   if (!state_number) return false;  // zero is disallowed.
00263   if (non_negative(state_index(state_number))) return false;  // already exists.
00264   *_state_list += state_info(state_number);
00265   return true;
00266 }
00267 
00268 bool transition_map::set_start(int starting_state)
00269 {
00270   if (valid()) return false;  // this is operational; no more config!
00271   if (!starting_state) return false;
00272 
00273   FIND_STATE(starting_state) false;  // doesn't exist.
00274 
00275   _start_state = starting_state;
00276   return true;
00277 }
00278 
00279 bool transition_map::add_simple_transition(int current, int next)
00280 {
00281   if (valid()) return false;  // this is operational; no more config!
00282   CHECK_STATES;
00283   (*_state_list)[indy].transitions += transition_info(next);
00284   return true;
00285 }
00286 
00287 bool transition_map::add_range_transition(int current, int next, int low, int high)
00288 {
00289   if (valid()) return false;  // this is operational; no more config!
00290   CHECK_STATES;
00291   (*_state_list)[indy].transitions += transition_info(next, low, high);
00292   return true;
00293 }
00294 
00295 bool transition_map::add_timed_transition(int current, int next, int length)
00296 {
00297   if (valid()) return false;  // this is operational; no more config!
00298   CHECK_STATES;
00299   state_info &found = (*_state_list)[indy];
00300   // locates any existing timed transition and re-uses its slot.
00301   for (int i = 0; i < found.transitions.length(); i++)
00302     if (found.transitions[i].type == transition_info::TIMED) {
00303       found.transitions[i] = transition_info(next, length);
00304       return true;
00305     }
00306   // no existing timed transition found, so add a new one.
00307   (*_state_list)[indy].transitions += transition_info(next, length);
00308   return true;
00309 }
00310 
00311 // operational functions:
00312 
00313 bool transition_map::make_transition(state_machine &m, int next)
00314 {
00315   if (!valid()) return false;  // this is not operational yet!
00316   CHECK_VALID(m);
00317 #ifdef DEBUG_STATE_MACHINE
00318   if (negative(state_index(m._current)))
00319     LOG(astring("(%s) transition_map::make_transition: bad logic error; machine's "
00320         "state is missing.", m._name->s()));
00321   if (negative(state_index(next)))
00322     LOG(astring("(%s) transition_map::make_transition: next state parameter is invalid.",
00323         m._name->s()));
00324 #endif
00325   FIND_STATE(m._current) false;  // bad next state.
00326   int start = 0;
00327   if (negative(transition_index(indy, next, start))) return false;
00328     // no transition exists for that next state.
00329   MOVE_STATE(m, next, state_machine::SIMPLE, 0);
00330   int trig = m.update();
00331   if (trig) return pulse(m, trig);
00332   return true;
00333 }
00334 
00335 bool transition_map::pulse(state_machine &m, int trigger)
00336 {
00337   if (!valid()) return false;  // this is not operational yet!
00338   CHECK_VALID(m);
00339 #ifdef DEBUG_STATE_MACHINE
00340   if (negative(state_index(m._current)))
00341     LOG(astring("(%s) transition_map::pulse: bad logic error; state is missing.", m._name->s()));
00342 #endif
00343   FIND_STATE(m._current) false;  // logic error!
00344   state_info &found = (*_state_list)[indy];
00345   for (int i = 0; i < found.transitions.length(); i++) {
00346     if (found.transitions[i].type == transition_info::RANGE) {
00347       // found the right type of transition.
00348       transition_info &tran = found.transitions[i];
00349       if ( (tran.low_trigger <= trigger)
00350             && (tran.high_trigger >= trigger) ) {
00351         // found a transition with an acceptable range.
00352         MOVE_STATE(m, tran.next_state, state_machine::RANGE, trigger);
00353         int trig = m.update();
00354         if (trig) return pulse(m, trig);
00355         return true;
00356       }
00357     }
00358   }
00359   return false;
00360 }
00361 
00362 bool transition_map::time_slice(state_machine &m)
00363 {
00364   if (!valid()) return false;  // this is not operational yet!
00365   CHECK_VALID(m);
00366 
00367 #ifdef DEBUG_STATE_MACHINE
00368   if (negative(state_index(m._current)))
00369     LOG(astring("(%s) transition_map::time_slice: bad logic error; state "
00370         "is missing.", m._name->s()));
00371 #endif
00372   FIND_STATE(m._current) false;  // logic error!
00373 
00374   state_info &found = (*_state_list)[indy];
00375   for (int i = 0; i < found.transitions.length(); i++) {
00376     if (found.transitions[i].type == transition_info::TIMED) {
00377       // found the right type of transition.
00378       transition_info &tran = found.transitions[i];
00379       int duration = tran.time_span;
00380       int override = m.duration_override(m._current, tran.next_state);
00381       if (override) duration = override;
00382       if (*m._start < time_stamp(-duration)) {
00383         // found a transition with an expired time.
00384         MOVE_STATE(m, tran.next_state, state_machine::TIMED, 0);
00385         int trig = m.update();
00386         if (trig) return pulse(m, trig);
00387         return true;
00388       }
00389     }
00390   }
00391   return false;
00392 }
00393 
00394 bool transition_map::reset(state_machine &m)
00395 {
00396   if (!valid()) return false;  // this is not operational yet!
00397   m._current = _start_state;
00398   m._last = _start_state;
00399   m._trig = 0;
00400   m._type = state_machine::SIMPLE;
00401   m._start->reset();
00402   return true;
00403 }
00404 
00405 bool transition_map::check_overlapping(int &examine)
00406 {
00407   FUNCDEF("check_overlapping");
00408   examine = 0;
00409   for (int i = 0; i < states(); i++) {
00410     examine = i;
00411     for (int j = 0; j < (*_state_list)[i].transitions.length(); j++) {
00412       transition_info &a = (*_state_list)[i].transitions[j];
00413       if (a.type != transition_info::RANGE) continue;
00414       for (int k = j + 1; k < (*_state_list)[i].transitions.length(); k++) {
00415         transition_info &b = (*_state_list)[i].transitions[k];
00416         if (b.type != transition_info::RANGE) continue;
00417         if ( (b.low_trigger <= a.low_trigger)
00418               && (b.high_trigger >= a.low_trigger) ) {
00419           LOG(astring("intersecting range on low end!"));
00420           return false;
00421         }
00422         if ( (b.low_trigger <= a.high_trigger)
00423               && (b.high_trigger >= a.high_trigger) ) {
00424           LOG(astring("intersecting range on high end!"));
00425           return false;
00426         }
00427       }
00428     }
00429   }
00430   return true;
00431 }
00432 
00433 bool transition_map::check_reachability(int &examine)
00434 {
00435   FUNCDEF("check_reachability");
00436   examine = 0;
00437 
00438   // list_to_add: the list of states that are definitely reachable and that
00439   // need to be considered.
00440   int_array list_to_add;
00441   list_to_add += _start_state;
00442 
00443   // already_checked: those states that have already been completely considered
00444   // as to their reachability.
00445   array<bool> already_checked(states());
00446   for (int i = 0; i < already_checked.length(); i++)
00447     already_checked[i] = false;
00448 
00449   while (list_to_add.length()) {
00450     // the next state that IS reachable has all of the states reachable from
00451     // it added to the list of states that are to be checked...
00452     examine = list_to_add[0];
00453     int indy = state_index(examine);
00454     if (negative(indy)) {
00455       LOG(a_sprintf("bad state index for state %d!", examine));
00456       return false;
00457     }
00458 #ifdef DEBUG_STATE_MACHINE
00459     LOG(a_sprintf("state to add is %d, list size=%d.", examine,
00460         list_to_add.length()));
00461 #endif
00462     // delete the item that we are now checking.
00463     list_to_add.zap(0, 0);
00464 
00465     // check whether this item has already had its kids (those states reachable
00466     // from it) added to the list to add.  if so, skip it.
00467     if (already_checked[indy]) continue;
00468 
00469     // update the information for this state we are considering in the list of
00470     // already checked states.
00471     already_checked[indy] = true;
00472 
00473     state_info &found = (*_state_list)[indy];
00474 
00475     // all states this one can reach are added to the list to check.
00476     for (int i = 0; i < found.transitions.length(); i++) {
00477 #ifdef DEBUG_STATE_MACHINE
00478       LOG(astring("checking transition %d: ", i));
00479 #endif
00480       int now_reachable = found.transitions[i].next_state;
00481 #ifdef DEBUG_STATE_MACHINE
00482       LOG(astring("now reaching %d.", now_reachable));
00483 #endif
00484       if (now_reachable == examine) continue;
00485       else {
00486         int indy = state_index(now_reachable);
00487         if (already_checked[indy]) continue;
00488       }
00489       list_to_add += now_reachable;
00490     }
00491   }
00492 #ifdef DEBUG_STATE_MACHINE
00493   LOG("done checking reachability.");
00494 #endif
00495   for (int j = 0; j < already_checked.length(); j++)
00496     if (!already_checked.get(j)) {
00497       examine = (*_state_list)[j].state_id;
00498       LOG(a_sprintf("state %d is not reachable", examine));
00499       return false;
00500     }
00501   return true;  // all states are reachable.
00502 }
00503 
00504 } //namespace.
00505 
Generated on Sat Jan 28 04:22:24 2012 for hoople2 project by  doxygen 1.6.3