The PetriNet library implements a Petri net that can be run on a processor like Arduino.
The Petri net is initialized by indicating the number of states and transitions. Both, states and transitions, are identified by an 8-bit integer (0 to 255).
To create transitions between states, the SetTransition() function is used, which receives as parameters the identifier number of the transition, the input states and their numbers, the output states and their numbers, the firing function of the transition, and optionally, the associated action to perform when firing the transition.
Additionally, these transition parameters can be modified later with the SetTransitionInputs(), SetTransitionOutputs(), SetTransitionAction(), and SetTransitionCondition() functions.
To initialize or modify the state of the Petri net, we can use the SetMarkup() function, which sets the number of marks of a state. We can also get the current number of marks of a state with the GetMarkup() function.
Finally, the Update() function updates the Petri net. It calculates sensitized inputs, firing conditions, and, if necessary, updates the state marks and triggers the appropriate actions.
The conditions of the transitions should be as lightweight as possible, as they are evaluated every time the Update() function is fired. Therefore, it is advisable to avoid performing heavy calculations in them, and instead evaluate simple conditions, and perform the calculations when convenient in the main loop.
For example, if we are waiting for communication, it is better to perform it only in the loop, and evaluate the transitions with the result. Otherwise, we would perform the communication in each transition, unnecessarily slowing down the update of the Petri net.
User Manual
The PetriNet class can be instantiated as an object through one of its constructors,
PetriNet(uint8_t numStates, uint8_t numTransitions);
PetriNet Usage
// Update the petri net
void Update();
// Configure the transitions
void SetTransition(uint8_t transition, uint8_t* inputs, uint8_t numInputs, uint8_t* outputs, uint8_t numOutputs, PetriNetCondition condition, PetriNetAction action) const;
void SetTransitionInputs(uint8_t transition, uint8_t* inputs) const;
void SetTransitionOutputs(uint8_t transition, uint8_t* outputs) const;
void SetTransitionAction(uint8_t transition, PetriNetAction action) const;
void SetTransitionCondition(uint8_t transition, PetriNetCondition condition) const;
// Set, get, or clear the state marks
void ClearMarks() const;
void SetMarkup(uint8_t state, uint8_t marks) const;
uint8_t GetMarkup(uint8_t state) const;
Examples
The PetriNet library includes the following examples to illustrate its use.
- Simple: Example showing the use of PetriNet
#include "PetriNetLib.h"
enum Input
{
ForwardA = 0,
ForwardB = 1,
Unknown = 2
};
Input input;
PetriNet petriNet(8, 7);
void setup()
{
Serial.begin(9600);
// Definition of the example's petri net
// Inputs and outputs of the states
static uint8_t inputs0[] = { 0, 4 };
static uint8_t outputs0[] = { 1, 5 };
static uint8_t inputs1[] = { 1 };
static uint8_t outputs1[] = { 2 };
static uint8_t inputs2[] = { 2, 6 };
static uint8_t outputs2[] = { 3, 7 };
static uint8_t inputs3[] = { 3 };
static uint8_t outputs3[] = { 0 };
static uint8_t inputs4[] = { 5 };
static uint8_t outputs4[] = { 6 };
static uint8_t inputs5[] = { 7 };
static uint8_t outputs5[] = { 4 };
// Transitions
petriNet.SetTransition(0, inputs0, 2, outputs0, 2,
[]() -> bool {return input == Input::ForwardA || input == Input::ForwardB; },
[]() {Serial.println("Fired T0"); printMarkup(); });
petriNet.SetTransition(1, inputs1, 1, outputs1, 1,
[]() -> bool {return input == Input::ForwardA; },
[]() {Serial.println("Fired T1"); printMarkup(); });
petriNet.SetTransition(2, inputs2, 2, outputs2, 2,
[]() -> bool {return input == Input::ForwardA || input == Input::ForwardB; },
[]() {Serial.println("Fired T2"); printMarkup(); });
petriNet.SetTransition(3, inputs3, 1, outputs3, 1,
[]() -> bool {return input == Input::ForwardA; },
[]() {Serial.println("Fired T3"); printMarkup(); });
petriNet.SetTransition(4, inputs4, 1, outputs4, 1,
[]() -> bool {return input == Input::ForwardB; },
[]() {Serial.println("Fired T4"); printMarkup(); activateTimer(); });
petriNet.SetTransition(5, inputs5, 1, outputs5, 1,
[]() -> bool {return input == Input::ForwardB; },
[]() {Serial.println("Fired T5"); printMarkup(); });
petriNet.SetTransition(6, outputs4, 1, inputs4, 1,
timerExpired,
[]() {Serial.println("Reseting T6"); printMarkup(); });
// Initial marking
petriNet.SetMarkup(0, 1);
petriNet.SetMarkup(4, 1);
printMarkup(); // Show the initial state
}
void loop()
{
input = static_cast<Input>(readInput());
petriNet.Update();
}
// Perform the reading of a character for the example
int readInput()
{
Input currentInput = Input::Unknown;
if (Serial.available())
{
char incomingChar = Serial.read();
switch (incomingChar)
{
case 'A': currentInput = Input::ForwardA; break;
case 'B': currentInput = Input::ForwardB; break;
default: break;
}
}
return currentInput;
}
// For example debugging
void printMarkup()
{
Serial.print(petriNet.GetMarkup(0));
Serial.print(petriNet.GetMarkup(1));
Serial.print(petriNet.GetMarkup(2));
Serial.println(petriNet.GetMarkup(3));
Serial.print(petriNet.GetMarkup(4));
Serial.print(petriNet.GetMarkup(5));
Serial.print(petriNet.GetMarkup(6));
Serial.println(petriNet.GetMarkup(7));
}
// Timer for transition 6
unsigned long previousMillis;
bool isTimerON = false;
void activateTimer()
{
isTimerON = true;
previousMillis = millis();
}
bool timerExpired()
{
if (isTimerON == false) return false;
if ((unsigned long)(millis() - previousMillis) >= 5000)
return true;
return false;
}
- Inheritance Example: Example showing the use of PetriNet through a derived class.
#include "PetriNetLib.h"
enum Input
{
ForwardA = 0,
ForwardB = 1,
Unknown = 2
};
Input input;
// Definition of the example's petri net
class MyPetriNet : public PetriNet
{
public:
MyPetriNet() : PetriNet(8, 7)
{
// Inputs and outputs of the states
static uint8_t inputs0[] = { 0, 4 };
static uint8_t outputs0[] = { 1, 5 };
static uint8_t inputs1[] = { 1 };
static uint8_t outputs1[] = { 2 };
static uint8_t inputs2[] = { 2, 6 };
static uint8_t outputs2[] = { 3, 7 };
static uint8_t inputs3[] = { 3 };
static uint8_t outputs3[] = { 0 };
static uint8_t inputs4[] = { 5 };
static uint8_t outputs4[] = { 6 };
static uint8_t inputs5[] = { 7 };
static uint8_t outputs5[] = { 4 };
// Transitions
SetTransition(0, inputs0, 2, outputs0, 2,
[]() -> bool {return input == Input::ForwardA || input == Input::ForwardB; },
[]() {Serial.println("Fired T0"); printMarkup(); });
SetTransition(1, inputs1, 1, outputs1, 1,
[]() -> bool {return input == Input::ForwardA; },
[]() {Serial.println("Fired T1"); printMarkup(); });
SetTransition(2, inputs2, 2, outputs2, 2,
[]() -> bool {return input == Input::ForwardA || input == Input::ForwardB; },
[]() {Serial.println("Fired T2"); printMarkup(); });
SetTransition(3, inputs3, 1, outputs3, 1,
[]() -> bool {return input == Input::ForwardA; },
[]() {Serial.println("Fired T3"); printMarkup(); });
SetTransition(4, inputs4, 1, outputs4, 1,
[]() -> bool {return input == Input::ForwardB; },
[]() {Serial.println("Fired T4"); printMarkup(); activateTimer(); });
SetTransition(5, inputs5, 1, outputs5, 1,
[]() -> bool {return input == Input::ForwardB; },
[]() {Serial.println("Fired T5"); printMarkup(); });
SetTransition(6, outputs4, 1, inputs4, 1,
timerExpired,
[]() {Serial.println("Reseting T6"); printMarkup(); });
// Initial marking
SetMarkup(0, 1);
SetMarkup(4, 1);
}
};
MyPetriNet petriNet;
void setup()
{
Serial.begin(9600);
printMarkup(); // Show the initial state
}
void loop()
{
input = static_cast<Input>(readInput());
petriNet.Update();
}
// Perform the reading of a character for the example
int readInput()
{
Input currentInput = Input::Unknown;
if (Serial.available())
{
char incomingChar = Serial.read();
switch (incomingChar)
{
case 'A': currentInput = Input::ForwardA; break;
case 'B': currentInput = Input::ForwardB; break;
default: break;
}
}
return currentInput;
}
// For example debugging
void printMarkup()
{
Serial.print(petriNet.GetMarkup(0));
Serial.print(petriNet.GetMarkup(1));
Serial.print(petriNet.GetMarkup(2));
Serial.println(petriNet.GetMarkup(3));
Serial.print(petriNet.GetMarkup(4));
Serial.print(petriNet.GetMarkup(5));
Serial.print(petriNet.GetMarkup(6));
Serial.println(petriNet.GetMarkup(7));
}
// Timer for transition 6
unsigned long previousMillis;
bool isTimerON = false;
void activateTimer()
{
isTimerON = true;
previousMillis = millis();
}
bool timerExpired()
{
if (isTimerON == false) return false;
if ((unsigned long)(millis() - previousMillis) >= 5000)
return true;
return false;
}
Installation
- Download the latest version from GitHub
- Unzip the file
- Copy it to your libraries folder (usually My Documents\Arduino\libraries)
- Restart the Arduino IDE