state_machine.cpp

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

Generated on Fri Nov 21 04:29:50 2008 for HOOPLE Libraries by  doxygen 1.5.1