24 using namespace basis;
37 #ifndef DEBUG_STATE_MACHINE
38 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
40 #define LOG(to_print) {}
46 #define CHECK_VALID(m) \
47 if (!m._current) return false; \
48 if (!m._last) return false
51 #define CHECK_STATES \
52 if (!current) return false; \
53 if (!next) return false; \
54 int indy = state_index(current); \
55 if (negative(indy)) return false; \
56 if (negative(state_index(next))) return false
59 #define MOVE_STATE(m, next, type, trigger) \
60 m._last = m._current; \
67 #define FIND_STATE(state) \
68 int indy = state_index(state); \
69 if (negative(indy)) return
73 struct override {
int current;
int next;
int duration;
74 override(
int _current = 0,
int _next = 0,
int _duration = 0)
75 : current(_current), next(_next), duration(_duration) {}
78 struct transition_info
86 int low_trigger, high_trigger;
91 : type(RANGE), next_state(0), low_trigger(0), high_trigger(0), time_span(0)
95 transition_info(
int next)
96 : type(SIMPLE), next_state(next),
97 low_trigger(0), high_trigger(0), time_span(0)
101 transition_info(
int next,
int time)
102 : type(TIMED), next_state(next), time_span(time), low_trigger(0), high_trigger(0)
106 transition_info(
int next,
int low,
int high)
107 : type(RANGE), next_state(next), low_trigger(low), high_trigger(high), time_span(0)
116 state_info() : state_id(0) {}
117 state_info(
int state_id_in) : state_id(state_id_in) {}
122 class state_machine_override_array :
public array<override> {};
123 class state_machine_state_array :
public array<state_info> {};
127 state_machine::state_machine()
134 _overrides(new state_machine_override_array)
145 _overrides(new state_machine_override_array)
163 if (&to_copy ==
this)
return *
this;
164 _current = to_copy._current;
165 _last = to_copy._last;
166 _trig = to_copy._trig;
167 _type = to_copy._type;
168 *_start = *to_copy._start;
169 *_name = *to_copy._name;
170 *_overrides = *to_copy._overrides;
175 int state_machine::duration_index(
int current,
int next)
const
177 for (
int i = 0; i < _overrides->length(); i++)
179 && ((*_overrides)[i].next == next) )
181 return common::NOT_FOUND;
187 _current = new_current;
196 int indy = duration_index(
current, next);
201 _overrides->zap(indy, indy);
205 (*_overrides)[indy].duration = duration;
209 *_overrides +=
override(
current, next, duration);
214 int indy = duration_index(
current, next);
216 return (*_overrides)[indy].duration;
226 _state_list(new state_machine_state_array)
239 int transition_map::state_index(
int state_id)
const
241 for (
int i = 0; i <
states(); i++)
242 if ((*_state_list)[i].state_id == state_id)
return i;
243 return common::NOT_FOUND;
246 int transition_map::transition_index(
int state_index,
int next,
int &start)
249 state_info &state = (*_state_list)[state_index];
250 bounds_return(start, 0, state.transitions.length() - 1, common::BAD_INPUT);
252 for (; start < state.transitions.length(); start++)
253 if (state.transitions[start].next_state == next) {
257 return common::NOT_FOUND;
267 examine = _start_state;
270 if (!check_reachability(examine))
return UNREACHABLE;
272 if (!check_overlapping(examine))
return OVERLAPPING_RANGES;
280 if (
valid())
return false;
281 if (!state_number)
return false;
282 if (
non_negative(state_index(state_number)))
return false;
283 *_state_list += state_info(state_number);
289 if (
valid())
return false;
290 if (!starting_state)
return false;
294 _start_state = starting_state;
300 if (
valid())
return false;
302 (*_state_list)[indy].transitions += transition_info(next);
308 if (
valid())
return false;
310 (*_state_list)[indy].transitions += transition_info(next, low, high);
316 if (
valid())
return false;
318 state_info &found = (*_state_list)[indy];
320 for (
int i = 0; i < found.transitions.length(); i++)
321 if (found.transitions[i].type == transition_info::TIMED) {
322 found.transitions[i] = transition_info(next, length);
326 (*_state_list)[indy].transitions += transition_info(next, length);
334 if (!
valid())
return false;
336 #ifdef DEBUG_STATE_MACHINE
337 if (
negative(state_index(m._current)))
338 LOG(
astring(
"(%s) transition_map::make_transition: bad logic error; machine's "
339 "state is missing.", m._name->
s()));
341 LOG(
astring(
"(%s) transition_map::make_transition: next state parameter is invalid.",
346 if (
negative(transition_index(indy, next, start)))
return false;
350 if (trig)
return pulse(m, trig);
356 if (!
valid())
return false;
358 #ifdef DEBUG_STATE_MACHINE
359 if (
negative(state_index(m._current)))
360 LOG(
astring(
"(%s) transition_map::pulse: bad logic error; state is missing.", m._name->
s()));
363 state_info &found = (*_state_list)[indy];
364 for (
int i = 0; i < found.transitions.length(); i++) {
365 if (found.transitions[i].type == transition_info::RANGE) {
367 transition_info &tran = found.transitions[i];
368 if ( (tran.low_trigger <= trigger)
369 && (tran.high_trigger >= trigger) ) {
373 if (trig)
return pulse(m, trig);
383 if (!
valid())
return false;
386 #ifdef DEBUG_STATE_MACHINE
387 if (
negative(state_index(m._current)))
388 LOG(
astring(
"(%s) transition_map::time_slice: bad logic error; state "
389 "is missing.", m._name->
s()));
393 state_info &found = (*_state_list)[indy];
394 for (
int i = 0; i < found.transitions.length(); i++) {
395 if (found.transitions[i].type == transition_info::TIMED) {
397 transition_info &tran = found.transitions[i];
398 int duration = tran.time_span;
400 if (
override) duration =
override;
405 if (trig)
return pulse(m, trig);
415 if (!
valid())
return false;
416 m._current = _start_state;
417 m._last = _start_state;
424 bool transition_map::check_overlapping(
int &examine)
428 for (
int i = 0; i <
states(); i++) {
430 for (
int j = 0; j < (*_state_list)[i].transitions.length(); j++) {
431 transition_info &a = (*_state_list)[i].transitions[j];
432 if (a.type != transition_info::RANGE)
continue;
433 for (
int k = j + 1; k < (*_state_list)[i].transitions.length(); k++) {
434 transition_info &b = (*_state_list)[i].transitions[k];
435 if (b.type != transition_info::RANGE)
continue;
436 if ( (b.low_trigger <= a.low_trigger)
437 && (b.high_trigger >= a.low_trigger) ) {
438 LOG(
astring(
"intersecting range on low end!"));
441 if ( (b.low_trigger <= a.high_trigger)
442 && (b.high_trigger >= a.high_trigger) ) {
443 LOG(
astring(
"intersecting range on high end!"));
452 bool transition_map::check_reachability(
int &examine)
460 list_to_add += _start_state;
465 for (
int i = 0; i < already_checked.length(); i++)
466 already_checked[i] =
false;
468 while (list_to_add.
length()) {
471 examine = list_to_add[0];
472 int indy = state_index(examine);
474 LOG(
a_sprintf(
"bad state index for state %d!", examine));
477 #ifdef DEBUG_STATE_MACHINE
478 LOG(
a_sprintf(
"state to add is %d, list size=%d.", examine,
482 list_to_add.
zap(0, 0);
486 if (already_checked[indy])
continue;
490 already_checked[indy] =
true;
492 state_info &found = (*_state_list)[indy];
495 for (
int i = 0; i < found.transitions.length(); i++) {
496 #ifdef DEBUG_STATE_MACHINE
499 int now_reachable = found.transitions[i].next_state;
500 #ifdef DEBUG_STATE_MACHINE
501 LOG(
astring(
"now reaching %d.", now_reachable));
503 if (now_reachable == examine)
continue;
505 int indy = state_index(now_reachable);
506 if (already_checked[indy])
continue;
508 list_to_add += now_reachable;
511 #ifdef DEBUG_STATE_MACHINE
512 LOG(
"done checking reachability.");
514 for (
int j = 0; j < already_checked.length(); j++)
515 if (!already_checked.get(j)) {
516 examine = (*_state_list)[j].state_id;
a_sprintf is a specialization of astring that provides printf style support.
int length() const
Returns the current reported length of the allocated C array.
outcome zap(int start, int end)
Deletes from "this" the objects inclusively between "start" and "end".
Provides a dynamically resizable ASCII character string.
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
A simple object that wraps a templated array of ints.
Outcomes describe the state of completion for an operation.
Monitors objects with multiple states and the transitions between states.
void override_timing(int current, int next, int duration)
modifies the recorded timing for timed transitions.
virtual int update()
this is the main implementation function provided by derived classes.
int trigger() const
returns the trigger that caused this state.
basis::astring get_name() const
retrieves the object's current name.
int current() const
returns the current state.
void set_state(int new_current, int new_last, int trigger, transition_types type)
sets the current state to "new_current" and the previous state to "new_last".
timely::time_stamp start() const
start time for the current state.
state_machine()
sets up the machine in a blank state.
int duration_override(int current, int next) const
reports if there's a duration override for a timed transition.
void set_name(const basis::astring &name)
sets the name to be printed in any debugging information for "this".
state_machine & operator=(const state_machine &to_copy)
assigns this to a copy of the machine provided in "to_copy".
bool add_range_transition(int current, int next, int low, int high)
adds a transition that listens to triggers in the pulse() method.
bool time_slice(state_machine &m)
bool set_start(int starting_state)
assigns a state as the first state.
virtual ~transition_map()
bool add_simple_transition(int current, int next)
adds a transition between "current" and "next".
bool make_transition(state_machine &m, int next)
changes the state to the "next" listed for "m" given the current state.
bool pulse(state_machine &m, int trigger)
applies a "trigger" to possibly cause a range transition.
bool valid() const
returns true if the transition_map is valid and ready for operation.
bool reset(state_machine &m)
void reconfigure()
puts the transition_map back into an unvalidated state.
int states() const
returns the current number of states.
bool add_state(int state_number)
registers a legal state in the transition_map.
basis::outcome validate(int &examine)
checks to that all required transition conditions are satisfied.
bool add_timed_transition(int current, int next, int duration)
adds a transition that occurs after a timeout with no other activity.
Represents a point in time relative to the operating system startup time.
void reset()
sets the stamp time back to now.
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
#define bounds_return(value, low, high, to_return)
Verifies that "value" is between "low" and "high", inclusive.
The guards collection helps in testing preconditions and reporting errors.
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
bool non_negative(const type &a)
non_negative returns true if "a" is greater than or equal to zero.
bool negative(const type &a)
negative returns true if "a" is less than zero.
A logger that sends to the console screen using the standard output device.
#define FIND_STATE(state)
#define MOVE_STATE(m, next, type, trigger)