Browse Source

FEAT Actually implemented idle processing
FEAT Added idle processing deactivation/activation
FEAT Added event processing modes: NORMAL, QUEUE, SINK.
QUAL Added comments

Frederic G. Marand 17 years ago
parent
commit
6f9e951268
1 changed files with 106 additions and 4 deletions
  1. 106 4
      u_fsm.php

+ 106 - 4
u_fsm.php

@@ -1,13 +1,14 @@
 <?php
 /**
  * Finite state machine skeleton
- * embodying a Moore FSM
+ *
  * (c) 2006 Ouest Systèmes Informatiques (OSI)
  * Licensed under the CeCILL 2.0 license
  *
- * $Id: u_fsm.php,v 1.2 2007-04-28 20:02:50 marand Exp $
+ * $Id: u_fsm.php,v 1.3 2007-04-29 15:40:04 marand Exp $
  */
 require_once('misc.php'); // for func_name()
+error_reporting(E_ALL|E_STRICT);
 
 /**
  * This class defines a possible outcome for a given FSM transition
@@ -42,8 +43,37 @@ class fsm_result
     }
   }
 
+/**
+ * This class must be inherited by code implementing actual FSMs.
+ *
+ * Applications must create a fsm descendant in which they will:
+ *
+ *   - define the f_transitions table, usually in their constructor
+ *   - create "f_foo" functions for each "foo" event, except "idle", which is builtin.
+ *
+ * Applications may:
+ *   - disable event processing by setting $this->event to one of the fsm::EVENT_* constants
+ *   - disable post-event actions by setting $this->allows actions to false
+ *   - disable the builtin idle event by setting $this->idle_work to false
+ *   - query the current state by using $this->get_state()
+ *   - send an idle event by using $this->idle()
+ *   - submit any event (including idle) by using $this->apply_event($event_name)
+ *
+ */
 abstract class fsm
   {
+  const IDLE_EVENT   = 'idle'; // must not change name: method f_idle depends on it
+
+  const EVENT_NORMAL = 1; // processes events
+  const EVENT_QUEUE  = 2; // queue events for later use
+  const EVENT_SINK   = 3; // throw away events
+
+  public $idle_work     = TRUE;
+  public $allow_actions = TRUE;
+
+  protected $f_event_mode = fsm::EVENT_NORMAL;
+  protected $f_queue      = array(); // event queue for EVENT_QUEUE mode
+
   /**
    * the current state of the object
    * @var string
@@ -178,7 +208,14 @@ abstract class fsm
 
     do {
       $result = $this->apply_simple_event($event_name);
-      $event_name = $result->fsm_action; // can be NULL
+      if ($this->allow_actions)
+        {
+        $event_name = $result->fsm_action; // can be NULL
+        }
+      else
+        {
+        $event_name = NULL;
+        }
       } while($event_name);
 
     return $result;
@@ -194,6 +231,10 @@ abstract class fsm
   private function apply_simple_event($event_name)
     {
     //echo func_name() . "\n";
+    if (($event_name == fsm::IDLE_EVENT) && !$this->idle_work)
+      {
+      return new fsm_result();
+      }
 
     if (! $this->is_event_allowed($event_name))
       throw new Exception(func_name()
@@ -217,4 +258,65 @@ abstract class fsm
     // echo func_name() . ", new state: $this->f_state, action: $result->fsm_action\n";
     return $result;
     }
-  }
+
+  /**
+   * Default event
+   * @return boolean
+   */
+  public function f_idle()
+    {
+    return TRUE;
+    }
+
+  /**
+   * Apply an fsm::IDLE_EVENT event. Do not confuse with f_idle !
+   *
+   * @return fsm_result
+   */
+  public function idle()
+    {
+    return $this->apply_event(fsm::IDLE_EVENT);
+    }
+
+  /**
+   * return the current operating mode of the FSM
+   * @return int
+   */
+  public function get_event_mode()
+    {
+    return $this->f_event_mode;
+    }
+
+  /**
+   * set the current operating mode for the FSM
+   *
+   * @param int $mode fsm::EVENT_* constants
+   */
+  public function set_event_mode($mode)
+    {
+    switch ($mode)
+      {
+      case fsm::EVENT_NORMAL :
+        if (count($this->f_queue) > 0)
+          {
+          while ($event = array_shift($this->f_queue))
+            {
+            $this->apply_event($event);
+            }
+          }
+        break;
+      case fsm::EVENT_QUEUE  : // nothing special to do
+        break;
+      case fsm::EVENT_SINK : // empty queue if needed
+        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;
+    }
+  }