123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- <?php
- /**
- * Output converter for FSM from GraphViz
- *
- * This class converts a loaded FSM to a GraphViz DOT file.
- *
- * @copyright (c) 2007-2012 Ouest Systèmes Informatiques
- * @author Frederic G. MARAND
- * @license CeCILL 2.0
- * @link http://wiki.audean.com/fsm/fsm
- * @since FSM 1.6
- */
- namespace Osinet\FiniteStateMachine;
- class Grapher {
- const STATE_SHAPE = 'octagon';
- const EVENT_SHAPE = 'egg';
- /**
- * The FSM being plotted.
- *
- * @var Machine
- */
- public $m;
- /**
- * The Image_Graphviz canvas.
- *
- * @var Image_Graphviz
- */
- public $g;
- /**
- * Debug level. >= 0.
- *
- * @var int
- */
- public $debug = NULL;
- /**
- * A hash of state name to GraphViz name.
- *
- * @var array
- */
- public $states = array();
- /**
- * @param Machine $m
- */
- public function __construct(Machine $m, $values = array()) {
- $this->m = $m;
- $default_values = array(
- 'debug' => 0,
- );
- $values = array_intersect_key($values, $default_values);
- $values += $default_values;
- foreach ($values as $key => $value) {
- $this->$key = $value;
- }
- // PEAR is not currently PSR-0 compliant.
- // As a consequence, strict PSR-0 autoloading will not work for it.
- require 'Image/GraphViz.php';
- $this->g = new \Image_Graphviz();
- }
- public function render() {
- $i = 0;
- $g = &$this->g;
- // First make sure all states have labeled nodes, so that forward
- // declarations of states occurring in transitions find the proper state id.
- foreach ($this->m->fTransitions as $state => $events) {
- $label = $state;
- $state .= "_$i";
- $this->states[$label] = $state;
- $g->addNode($state, array(
- 'label' => $label,
- 'shape' => self::STATE_SHAPE,
- ));
- $i++;
- }
- foreach ($this->m->fTransitions as $state => $events) {
- $state_id = $this->states[$state];
- foreach ($events as $event => $outcomes) {
- if ($this->debug) {
- echo "State $state Event $event " . count($outcomes) . " outcomes\n";
- print_r($outcomes);
- }
- if (0 && count($outcomes) == 1) {
- $outcome = reset($outcomes);
- // Only one outcome: we do not care for the result.
- echo "State $state event $event, outcome:"; print_r($outcome); echo "\n";
- $edge = array($state_id => $this->states[$outcome[0]]);
- $edge_label = "\n:" . $outcome[1];
- $edge_attributes = empty($outcome[1])
- ? array()
- : array('label' => $edge_label);
- $g->addEdge($edge, $edge_attributes);
- }
- else {
- $event_label = $event;
- $event_id = "{$state_id}_$event";
- $g->addNode($event_id, array(
- 'label' => $event_label,
- 'shape' => self::EVENT_SHAPE,
- ));
- $g->addEdge(array($state_id => $event_id));
- foreach ($outcomes as $result => $next) {
- list($next_state, $next_action) = $next;
- $edge = array($event_id => $this->states[$next_state]) ;
- $edge_label_parts = array();
- if (!empty($result)) {
- $edge_label_parts[] = "[$result]";
- }
- if (!empty($next_action)) {
- $edge_label_parts[] = ":$next_action";
- }
- if (empty($edge_label_parts)) {
- echo "No label!\n";
- $g->addEdge($edge);
- }
- else {
- $edge_label = implode("\n", $edge_label_parts);
- $edge_attributes = array(
- 'label' => $edge_label,
- );
- $g->addEdge($edge, $edge_attributes);
- }
- }
- }
- }
- }
- return $g->parse();
- }
- }
|