Example of a simple Petrinet :
<?xml version="1.0" ?>
<petrinet
id="net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://raw.github.com/florianv/petrinet/master/src/Petrinet/Loader/meta/schema.xsd">
<!-- A place with two tokens -->
<place id="p1" tokens="2"/>
<!-- An empty place -->
<place id="p2"/>
<!-- Two transitions -->
<transition id="t1"/>
<transition id="t2"/>
<!-- An arc with a specified id, from the place p1 to the transition t1 -->
<arc id="a1" from="p1" to="t1"/>
<arc from="p1" to="t2"/>
<!-- An arc from the transition t1 to the place p2 -->
<arc from="t1" to="p2"/>
<arc from="t2" to="p2"/>
</petrinet>
Now you can load the Petrinet using the XmlFileLoader
:
<?php
use Petrinet\Loader\XmlFileLoader;
// Creating a loader instance
$loader = new XmlFileLoader();
// Loading the Petrinet
$petrinet = $loader->load('/path/to/petrinet.xml');
<?php
use Petrinet\PetrinetBuilder;
// Instanciating the builder to create a Petrinet called "Network"
$builder = new PetrinetBuilder('Network');
// Adding an empty place identified by 'p1'
$builder->addPlace('p1');
// Adding a place with one token
$builder->addPlace('p1', 1);
// Adding a transition identified by 't1'
$builder->addTransition('t1');
// Connecting the place p1 to the transition t1
$builder->connectPT('p1', 't1');
// Connecting the transition t1 to the place p2
$builder->connectTP('t1', 'p2');
// Obtaining the resulting Petrinet
$petrinet = $builder->getPetrinet();
Firstly download and install Graphviz.
<?php
use Petrinet\Dumper\GraphvizDumper;
$dumper = New GraphvizDumper();
// Creating a file Network.dot ready to be opened by the Graphviz software
file_put_contents('Network.dot', $dumper->dump($petrinet));
<?php
use Petrinet\Engine\Engine;
// Instanciating the engine to execute the Petrinet
$engine = new Engine($petrinet);
// Starting the execution
$engine->start();
// Stopping the Execution
$engine->stop();
There are two execution modes :
Stepped
: the engine will fire the currently enabled transitions, and stop itself.Continuous
: the engine will fire the transitions until no more are enabled, and then stop itself.
By default the engine is in Continuous
mode.
<?php
// Changing the mode to "Stepped"
$engine->setMode(Engine::MODE_STEPPED);
The Petrinet framework is using Symfony EventDispatcher to dispatch its events.
Different events are available to interact with the engine or Petrinet elements during the execution process.
They are documented in the following file.
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Symfony\Component\EventDispatcher\EventDispatcher;
use Petrinet\PetrinetEvents;
use Petrinet\Event\EngineEvent;
use Petrinet\PetrinetBuilder;
use Petrinet\Engine\Engine;
// Creating a simple Petrinet
$builder = new PetrinetBuilder('Network');
$petrinet = $builder
->addPlace('p1', 1)
->addTransition('t1')
->addPlace('p2')
->connectPT('p1', 't1')
->connectTP('t1', 'p2')
->getPetrinet();
$engine = new Engine($petrinet);
$dispatcher = new EventDispatcher();
// Listening to an EngineEvent
$listener = function (EngineEvent $e) {
$petrinet = $e->getEngine()->getPetrinet();
echo 'The execution of the Petrinet ' . $petrinet->getId() . ' just stopped';
};
// Adding the listener to the dispatcher
$dispatcher->addListener(PetrinetEvents::AFTER_ENGINE_STOP, $listener);
// Injecting the dispatcher into the engine
$engine->setDispatcher($dispatcher);
// Starting the execution
$engine->start();
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Symfony\Component\EventDispatcher\EventDispatcher;
use Petrinet\PetrinetEvents;
use Petrinet\Event\TokenAndPlaceEvent;
use Petrinet\PetrinetBuilder;
use Petrinet\Engine\Engine;
$builder = new PetrinetBuilder('Network');
$petrinet = $builder
->addPlace('p1', 1)
->addTransition('t1')
->addPlace('p2')
->connectPT('p1', 't1')
->connectTP('t1', 'p2')
->getPetrinet();
$engine = new Engine($petrinet);
$dispatcher = new EventDispatcher();
$listenConsumption = function (TokenAndPlaceEvent $e) {
$placeId = $e->getPlace()->getId();
echo sprintf('The place %s just dropped a token', $placeId);
};
$listenInsertion = function (TokenAndPlaceEvent $e) {
$placeId = $e->getPlace()->getId();
echo sprintf('The place %s just received a new token', $placeId);
};
$dispatcher->addListener(PetrinetEvents::AFTER_TOKEN_CONSUME, $listenConsumption);
$dispatcher->addListener(PetrinetEvents::AFTER_TOKEN_INSERT, $listenInsertion);
$engine->setDispatcher($dispatcher);
$engine->start();
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Symfony\Component\EventDispatcher\EventDispatcher;
use Petrinet\Event\TransitionEvent;
use Petrinet\PetrinetBuilder;
use Petrinet\PetrinetEvents;
use Petrinet\Engine\Engine;
/**
* Petrinet while function
*
* @param callable $while The while condition
* @param callable $do A callable to execute after each loop
*/
function petrinet_while($while, $do)
{
$builder = new PetrinetBuilder('Loop');
$petrinet = $builder
->addPlace('p1', 1)
->addTransition('t1')
->addTransition('t2')
->addPlace('p2')
->connectPT('p1', 't1')
->connectTP('t1', 'p2')
->connectPT('p2', 't2')
->connectTP('t2', 'p1')
->getPetrinet();
$engine = new Engine($petrinet);
$dispatcher = new EventDispatcher();
$listener = function (TransitionEvent $e) use ($engine, $while, $do) {
if (!$while()) {
$engine->stop();
}
call_user_func($do);
};
$dispatcher->addListener(PetrinetEvents::AFTER_TRANSITION_FIRE, $listener);
$engine->setDispatcher($dispatcher);
$engine->start();
}
// While condition
$while = function () {
static $i = 0;
if (5 === $i) {
return false;
}
$i++;
return true;
};
// Called after each loop
$do = function () {
static $i = 0;
echo $i;
$i++;
};
petrinet_while($while, $do);