123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- <?php
- require_once('misc.php');
- $erFiniteStateMachine = error_reporting(E_ALL|E_STRICT);
- class Fsm_Result
- {
-
- public $fsm_return;
-
- public $fsm_state;
-
- public $fsm_action;
-
- public function __construct($return = NULL, $state = NULL, $action = NULL)
- {
- $this->fsm_return = $return;
- $this->fsm_state = $state;
- $this->fsm_action = $action;
- }
- }
- abstract class Finite_State_Machine
- {
- const VERSION = '$Id';
- const IDLE_EVENT = 'idle';
- const INIT_STATE = 'init';
- const FINAL_STATE = 'final';
- const EVENT_NORMAL = 1;
- const EVENT_QUEUE = 2;
- const EVENT_SINK = 3;
- public $idle_work = TRUE;
- public $allow_actions = TRUE;
- protected $f_event_mode = Finite_State_Machine::EVENT_NORMAL;
- protected $f_queue = array();
-
- protected $f_state;
-
- protected $f_transitions = null;
-
- public function __construct()
- {
- $this->_check_transitions();
- reset($this->f_transitions);
- $x = each($this->f_transitions);
- $x = $x[0];
- $this->f_state = $x;
- }
-
- protected function _check_transitions()
- {
- if (!isset($this->f_transitions))
- throw new Exception('No FSM processing is allowed without a transitions table\n');
- }
-
- public function get_state()
- {
- return $this->f_state;
- }
-
- public function get_accepted_events()
- {
- $this->_check_transitions();
- try
- {
- $events = array_keys($this->f_transitions[$this->f_state]);
-
- }
- catch (Exception $e)
- {
- echo "Exception in get_accepted_events" . print_r($e);
- print_r(debug_backtrace());
- $events = array();
- }
- return $events;
- }
-
- public function get_accepted_outcomes($event_name)
- {
-
- $this->_check_transitions();
-
- $outcomes = array_keys($this->f_transitions[$this->f_state][$event_name]);
-
-
- return $outcomes;
- }
-
- public function is_event_allowed($event_name)
- {
-
- $this->_check_transitions();
- $ret = in_array($event_name, $this->get_accepted_events());
-
- return $ret;
- }
-
- public function is_outcome_allowed($event_name, $outcome)
- {
- $this->_check_transitions();
- if (!$this->is_event_allowed($event_name))
- return false;
- $ret = array_key_exists($outcome, $this->get_accepted_outcomes($event_name));
- return $ret;
- }
-
- public function apply_event($event_name)
- {
-
- do {
- $result = $this->apply_simple_event($event_name);
- if ($this->allow_actions)
- {
- $event_name = $result->fsm_action;
- }
- else
- {
- $event_name = NULL;
- }
- } while($event_name);
-
- return $result;
- }
-
- private function apply_simple_event($event_name)
- {
-
- $current_state = $this->f_state;
- if (($event_name == Finite_State_Machine::IDLE_EVENT) && !$this->idle_work)
- {
- return new Fsm_Result();
- }
- if (!$this->is_event_allowed($event_name))
- {
- die("Event $event_name not allowed in current state $current_state.\n");
-
- }
- $outcomes = $this->get_accepted_outcomes($event_name);
- $method_name = "event$event_name";
- if (!method_exists($this, $method_name))
- {
- die (func_name() . ": missing method "
- . get_class($this) . "::$method_name. Aborting.\n");
- }
- $outcome = $this->$method_name();
- if (!in_array($outcome, $outcomes))
- {
- throw new Exception(func_name()
- . ": event guard. Transition on \"$event_name\" return invalid outcome: "
- . print_r($outcome, true)
- . " for state $this->f_state\n");
- }
- $transition = &$this->f_transitions[$current_state][$event_name][$outcome];
- $result = new Fsm_Result
- (
- $outcome,
- $transition[0],
- $transition[1]
- );
- if (isset($result->fsm_state))
- {
- $this->f_state = $result->fsm_state;
- }
-
- if (!isset($outcome))
- $outcome = 'NULL';
-
- return $result;
- }
-
- public function f_idle()
- {
- return TRUE;
- }
-
- public function idle()
- {
- return $this->apply_event(Finite_State_Machine::IDLE_EVENT);
- }
-
- public function get_event_mode()
- {
- return $this->f_event_mode;
- }
-
- public function set_event_mode($mode)
- {
- switch ($mode)
- {
- case Finite_State_Machine::EVENT_NORMAL :
- if (count($this->f_queue) > 0)
- {
- while (($event = array_shift($this->f_queue)) !== NULL)
- {
- $this->apply_event($event);
- }
- }
- break;
- case Finite_State_Machine::EVENT_QUEUE :
- break;
- case Finite_State_Machine::EVENT_SINK :
- if (count($this->f_queue) > 0)
- {
- $this->f_queue = array();
- }
- break;
- default:
- throw new Exception("Trying to set unknown FSM mode $mode");
- }
- $this->f_event_mode = $mode;
- return $this->f_event_mode;
- }
-
- public function load_fsm($url = NULL)
- {
- $osd = FALSE;
- if ($osd)
- echo "Loading FSM from $url\n";
- if (!isset($url))
- {
- $url = get_class($this) . ".xml";
- }
- $fsm = simplexml_load_file($url);
- $fsm_version = (string) $fsm['fsm_version'];
- if ($fsm_version !== '1.3')
- {
- die("Revision $fsm_version of schema is not supported.\n");
- }
- $this->idle_work = ($fsm['idle_work'] == 1);
- $this->allow_actions = ($fsm['allow_actions'] == 1);
- $this->f_transitions = array();
- $t = &$this->f_transitions;
-
- foreach ($fsm->state as $state)
- {
- $id = (string) $state['id'];
- if ($osd)
- echo "State $id :\n";
- $t[$id] = array();
- switch ($id)
- {
- case Finite_State_Machine::INIT_STATE :
- if ($osd)
- echo " Initial state\n";
- break;
- case Finite_State_Machine::FINAL_STATE :
- if ($osd)
- echo " Final state\n";
- break;
- }
- foreach ($state->event as $event)
- {
- $ename = (string) $event['name'];
- if ($osd)
- echo " Event $ename";
- if (!isset($event['type']))
- $event['type'] = 'void';
- $etype = (string) $event['type'];
- if ($osd)
- echo ", type $etype\n";
- foreach ($event as $next)
- {
- if ($event['type'] == 'void')
- {
- $next['result'] = 'always';
- $eresult = null;
- }
- else
- $eresult = (string) $next['result'];
- if (!isset($next['state']))
- $next['state'] = (string) $state['id'];
- if ($osd)
- {
- echo " Next(" . $next['result'] . ') = ' . $next['state'];
- if (isset($next['action']))
- echo " + event " . $next['action'];
- echo PHP_EOL;
- }
- $t[$id][$ename][$eresult] = array(
- (string) $next['state'],
- (string) $next['action']);
- }
- }
- }
- }
- }
- error_reporting($erFiniteStateMachine);
- unset($erFiniteStateMachine);
|