+++ /dev/null
-#ifndef STATE_MACHINE_CLASS
-#define STATE_MACHINE_CLASS
-
-/*****************************************************************************\
-* *
-* Name : state_machine *
-* Author : Chris Koeritz *
-* *
-*******************************************************************************
-* Copyright (c) 1992-$now By Author. This program is free software; you can *
-* redistribute it and/or modify it under the terms of the GNU General Public *
-* License as published by the Free Software Foundation; either version 2 of *
-* the License or (at your option) any later version. This is online at: *
-* http://www.fsf.org/copyleft/gpl.html *
-* Please send any updates to: fred@gruntose.com *
-\*****************************************************************************/
-
-#include <basis/contracts.h>
-#include <timely/time_stamp.h>
-
-namespace processes {
-
-class state_machine_override_array;
-class state_machine_state_array;
-
-//! Monitors objects with multiple states and the transitions between states.
-/*!
- A state machine is a computational abstraction for any control process
- that has discrete states and transitions between the states.
- As used here, the "state_machine" is a base class for objects that can be
- manipulated by the "transition_map" class. Your transition map must be
- validated and you must use it to reset() your state_machine object before
- any activity can occur. (One aspect of validation is that all states must be
- reachable from the user-specified starting state. The other requirements
- are documented below for transition_map.) Once validation is done, the
- transition map manages the machine through the various states that are
- defined and applies trigger values to modify the current state when asked
- to do so. It also applies any defined timed transitions automatically.
- This implementation of state machines is configured once (by defining the
- transition_map object), but then the machines can be run repeatedly (via
- the state_machine object).
-*/
-
-class state_machine : public virtual basis::root_object
-{
-public:
- state_machine();
- //!< sets up the machine in a blank state.
-
- state_machine(const state_machine &to_copy);
- //!< copies the machine provided in "to_copy".
-
- virtual ~state_machine();
-
- DEFINE_CLASS_NAME("state_machine");
-
- state_machine &operator =(const state_machine &to_copy);
- //!< assigns this to a copy of the machine provided in "to_copy".
-
- virtual int update();
- //!< this is the main implementation function provided by derived classes.
- /*!< this is implemented by derived classes that want to perform an action
- when the state machine is pulsed (see below in transition_map), which is
- why it is not pure virtual; a state machine might still be useful as a
- state tracking object rather than needing to implement extended
- functionality. this function is invoked whenever the state changes due to
- a timed, range based or simple transition. one must be careful when
- servicing this transition not to enmesh oneself in a snarled invocation
- situation; if make_transition or time_slice or pulse are invoked from this
- update function and conditions are right for another transition, then the
- update function will be re-entered recursively. if the value returned
- from update is non-zero, it is used to pulse the state machine again as a
- new trigger value (this is safer than explicitly calling one of the
- transition causing functions). note that this assumes that zero is an
- invalid trigger. if your triggers include zero as a valid value, don't
- try returning it through update; use pulse to force the triggering to
- accept the invalid trigger instead. */
-
- int current() const { return _current; }
- //!< returns the current state.
- /*!< NOTE: a zero value for a state means that it is uninitialized. */
-
- int last() const { return _last; }
- //!< returns the last active state.
-
- int trigger() const { return _trig; }
- //!< returns the trigger that caused this state.
- /*!< this is only meaningful when the transition was caused by a range
- transition. if it was, then ranged() will return true. */
-
- enum transition_types { SIMPLE, RANGE, TIMED };
- //!< the three types of transitions supported.
-
- bool simple() const { return _type == SIMPLE; }
- //!< returns true if the last transition was a simple type.
- bool ranged() const { return _type == RANGE; }
- //!< returns true if the last transition was a ranged type.
- bool timed() const { return _type == TIMED; }
- //!< returns true if the last transition was a timed type.
-
- 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".
- /*!< the "trigger" causing the transition, if any, will be stored also.
- the "type" of transition is stored also. the time stamp for time spent in
- the current state is reset. be very careful with this; if the two states
- do not conform to the legal transitions in your map, then the state
- machine will not behave properly. */
-
- timely::time_stamp start() const;
- //!< start time for the current state.
- /*!< this records how long we've been in this state. */
-
- void set_name(const basis::astring &name);
- //!< sets the name to be printed in any debugging information for "this".
-
- basis::astring get_name() const;
- //!< retrieves the object's current name.
-
- void override_timing(int current, int next, int duration);
- //!< modifies the recorded timing for timed transitions.
- /*!< this overrides the transition_map's time-out value for the transition
- between the states "current" and "next". a time-out of "duration"
- will be used instead of whatever was specified when the map was set
- up. to erase an override, use zero as the "duration". */
-
- int duration_override(int current, int next) const;
- //!< reports if there's a duration override for a timed transition.
- /*!< this returns the amount of time that this particular state_machine is
- allowed to exist in the "current" state before progressing to the "next"
- state. this has nothing to do with the transition_map; it is valid only
- for this state_machine object. zero is returned if no override exists. */
-
-private:
- friend class transition_map;
- //!< manager buddy object.
- int _current; //!< the current state of the state machine.
- int _last; //!< the previous state for the state machine.
- int _trig; //!< the last trigger value, if _ranged is true.
- transition_types _type; //!< what kind of transition just occurred.
- timely::time_stamp *_start; //!< the time this state started.
- basis::astring *_name; //!< the name this state_machine reports itself as.
- state_machine_override_array *_overrides; //!< the timing overrides.
-
- int duration_index(int current, int next) const;
- //!< finds the index of a duration override in our list.
- /*!< this locates the duration override specified for the transition
- between "current" and "next". it returns the index of that override, or
- a negative number if the override is not found. */
-};
-
-//////////////
-
-//! The transition_map manages state machines and causes state changes to occur.
-/*!
- The transition_map is a heavyweight class that is a repository for all
- information about transitions and which manages pushing the state machines
- through the proper states.
-
- The transition_map guarantees these characteristics:
-
- 0) the below characteristics are checked (in validate) and no state
- machine object is allowed to operate until they are satisfied,
-
- 1) the machine starts in the specified starting state,
-
- 2) the current state is always one that has been added and approved,
-
- 3) transitions are allowed between states only if the transition has
- been added and approved,
-
- 4) the update() function is invoked every time the machine hits a
- transition between states (even if it is a transition to the same
- state),
-
- 5) range transitions are non-intersecting within one state,
-*** 5 is unimplemented ***
-
- 6) all states are reachable from the starting state by some valid
- transition, and
-
- 7) each state has no more than one timed transition.
-
- if any of these conditions are violated, then validate() will fail.
- the machine will also not operate properly (at all) until the conditions
- are satisfied by validate(). the states and transitions should
- thus be carefully examined before turning them into a state machine....
-*/
-
-class transition_map : public virtual basis::root_object
-{
-public:
- transition_map();
- virtual ~transition_map();
-
- // informational functions...
-
- DEFINE_CLASS_NAME("transition_map");
-
- bool valid() const { return _valid; }
- //!< returns true if the transition_map is valid and ready for operation.
- /*!< once the validate() call has succeeded and valid() is true, no more
- configuration functions (see below) may be called until the reconfigure()
- function is invoked. */
-
- int states() const;
- //!< returns the current number of states.
-
- // validation functions...
-
- enum outcomes {
- OKAY = basis::common::OKAY,
- DEFINE_OUTCOME(BAD_START, -49, "The start state has not been properly "
- "specified"),
- DEFINE_OUTCOME(OVERLAPPING_RANGES, -50, "The ranges overlap for two "
- "transitions from a state"),
- DEFINE_OUTCOME(UNREACHABLE, -51, "There is an unreachable state in the map")
- };
- basis::outcome validate(int &examine);
- //!< checks to that all required transition conditions are satisfied.
- /*!< OKAY is returned if they are and the map is then ready to operate.
- other outcomes are returned if one or more of the conditions are not met:
- BAD_START means that the starting state is badly specified.
- OVERLAPPING_RANGES means that one state has two transitions that do not
- have mutually exclusive ranges. UNREACHABLE means that a state is not
- reachable from the starting state. for all of these cases, the "examine"
- parameter is set to a state related to the problem. */
-
- void reconfigure();
- //!< puts the transition_map back into an unvalidated state.
- /*!< this allows the characteristics of the map to be changed. validate()
- must be called again before resuming operation using this map. */
-
- // configuration functions...
-
- // NOTE: all of the functions below will fail if the transition_map has
- // already been validated previously and reconfigure() has not been called.
-
- // NOTE: a zero value for a state means that it is uninitialized. thus, zero
- // is never allowed as a value for a state or a transition endpoint. zero is
- // grudgingly allowed as a trigger value, although that interferes with the
- // state_machine object's update() method.
-
- bool add_state(int state_number);
- //!< registers a legal state in the transition_map.
- /*!< no action will be taken for any state until it is registered. the
- operation returns true for successful registration and false for errors
- (such as when a state is already registered or is invalid). */
-
- bool set_start(int starting_state);
- //!< assigns a state as the first state.
- /*!< if the "starting_state" does not already exist, it is an error and
- false is returned. */
-
- bool add_simple_transition(int current, int next);
- //!< adds a transition between "current" and "next".
- /*!< it is an error to use either an invalid "current" state or an invalid
- "next" state. these errors are detected during the validate() function.
- this type of transition is unspecialized--it does not respond to triggers
- and does not occur due to timing. to make a state_machine undergo this
- transition, make_transition() must be used. */
-
- bool add_range_transition(int current, int next, int low, int high);
- //!< adds a transition that listens to triggers in the pulse() method.
- /*!< this uses "low" and "high" as the inclusive range of triggers that
- will cause a transition to the "next" state when a state_machine is pulsed
- in the "current" state. it is erroneous for these trigger values to
- intersect with other values in the same "current" state. */
- bool add_timed_transition(int current, int next, int duration);
- //!< adds a transition that occurs after a timeout with no other activity.
- /*!< adds a timed transition to the "next" state when the "current" state
- has lasted "duration" milliseconds. this relies on the time_slice function
- being called periodically, with appropriate regularity for the specified
- "duration" (e.g., if one's time-out is 100 ms, then one might want to call
- time_slice() every 20 ms or so to ensure that the transition is at most 20
- ms late in firing. NOTE: this is not an argument for calling time_slice()
- as fast as possible; it is an argument for realizing that the "duration"
- must be carefully considered to meet one's timing deadlines). */
-
- // transition functions...
-
- // NOTE: all of these actions will fail if the transition_map has not been
- // validated yet.
-
- bool make_transition(state_machine &m, int next);
- //!< changes the state to the "next" listed for "m" given the current state.
- /*!< it is an error to make a transition that has not been specified
- through an add_X() transition function (false is returned). if the
- transition succeeds, then the current_state will be "next". */
-
- bool pulse(state_machine &m, int trigger);
- //!< applies a "trigger" to possibly cause a range transition.
- /*!< this causes the state_machine to accept the "trigger" as input and
- perform at least one traversal of the transition_map. if the trigger
- value is found in one of the range transitions for the current state, then
- the next state specified in that transition becomes the current state and
- the update() function is invoked (and true is returned). if the trigger
- is not found in any range transition, then false is returned. */
-
- bool time_slice(state_machine &m);
- // allows the transition_map to process any timed transitions that may be
- // required for the state_machine "m". the return value is true if such
- // a transition was found.
-
- bool reset(state_machine &m);
- // resets the state_machine to the starting state in the transition_map.
- // the update function is NOT invoked at the time of the reset. true is
- // returned if the reset was successful; it would fail if the transition
- // map has not been validated yet.
-
-private:
- bool _valid; //!< records whether we've been validated or not.
- int _start_state; //!< remembers the default starting state.
- state_machine_state_array *_state_list;
- //!< the list of transitions between states.
-
- bool check_overlapping(int &examine);
- //!< a validation function that looks for erroneous range transitions.
- /*!< this returns true if there are no overlapping ranges found in the
- range transitions for each state. */
-
- bool check_reachability(int &examine);
- //!< returns true if all states are reachable from the starting state.
-
- int state_index(int state_id) const;
- //!< returns the index of "state_id" in states, if it exists.
-
- int transition_index(int state_index, int next, int &start);
- //!< locates a transition into "next" for a state in our list.
- /*!< returns the index of a transition between the state at "state_index"
- and the "next" state by starting the search at index "start". if there
- is no matching transition from the "start" index on in the transition
- list, then a negative number is returned. "start" is updated to point
- to the index just after a found transition, or to some random number if
- the index is not found. */
-
- bool check_states();
- //!< returns false if the current machine is hosed up.
-
- // disallowed functions for now:
- transition_map(const transition_map &);
- transition_map &operator =(const transition_map &);
-};
-
-} //namespace.
-
-#endif
-