gen_pnet
behavior.
Copyright © 2016-2017 Jörgen Brandt
Version: 0.1.7
Behaviours: gen_server.
This module defines the gen_pnet behaviour.
Required callback functions: place_lst/0, trsn_lst/0, init_marking/2, preset/1, is_enabled/3, fire/3, code_change/3, handle_call/3, handle_cast/2, handle_info/2, init/1, terminate/2, trigger/3.
Authors: Jörgen Brandt (joergen.brandt@onlinehome.de).
Callback function definitions and API for the gen_pnet
behavior.
There are six callbacks that define the Petri net structure and its initial marking:
place_lst/0
returns the names of the places in the nettrsn_lst/0
returns the names of the transitions in the netinit_marking/2
returns the initial marking for a given placepreset/1
returns the preset places of a given transitionis_enabled/3
determines whether a given transition is enabled in a
given modefire/3
returns which tokens are produced on what places if a given
transition is fired in a given mode that enables this transitionWe have a look at each of them in turn.
The place_lst/0
function lets us define the names of all places in the net.
place_lst() -> [coin_slot, cash_box, signal, storage, compartment].
Here, we define the net to have the five places in the cookie vending machine.
The trsn_lst/0
function lets us define the names of all transitions in the
net.
trsn_lst() -> [a, b].
Here, we define the net to have the two places a
and b
in the cookie
vending machine.
The preset/1
lets us define the preset places of a given transition.
preset( a ) -> [coin_slot]; preset( b ) -> [signal, storage].
Here, we define the preset of the transition a
to be just the place
coin_slot
while the transition b
has the places signal
and storage
in its preset.
The init_marking/2
function lets us define the initial marking for a given
place in the form of a token list. The argument UsrInfo
is the user info
field that has been generated in the actor interface callback init/1
.
init_marking( storage, _UsrInfo ) -> [cookie_box, cookie_box, cookie_box]; init_marking( _Place, _UsrInfo ) -> [].
Here, we initialize the storage place with three cookie_box
tokens. All
other places are left empty.
The is_enabled/3
function is a predicate determining whether a given
transition is enabled in a given mode. The UsrInfo
argument is the user
info field that has been created with init/1
.
is_enabled( a, #{ coin_slot := [coin] }, _UsrInfo ) -> true; is_enabled( b, #{ signal := [sig], storage := [cookie_box] }, _UsrInfo ) -> true; is_enabled( _Trsn, _Mode, _UsrInfo ) -> false.
Here, we state that the transition a
is enabled if it can consume a single
coin
from the coin_slot
place. Similarly, the transition b
is enabled
if it can consume a sig
token from the signal
place and a cookie_box
token from the storage` place. No other configuration can enable a
transition. E.g., managing to get a `button
token on the coin_slot
place
will not enable any transition.
The fire/3
function defines what tokens are produced when a given
transition fires in a given mode. As arguments it takes the name of the
transition, and a firing mode in the form of a hash map mapping place names
to token lists. The fire/3
function is called only on modes for which
is_enabled/3
returns true
. The fire/3
function is expected to return
either a {produce, ProduceMap}
tuple or the term abort
. If abort
is
returned, the firing is aborted. Nothing is produced or consumed.
fire( a, _Mode, _UsrInfo ) -> {produce, #{ cash_box => [coin], signal => [sig] }}; fire( b, _Mode, _UsrInfo ) -> {produce, #{ compartment => [cookie_box] }}.
Here, the firing of the transition a
produces a coin
token on the
cash_box
place and a sig
token on the signal
place. Similarly, the
firing of the transition b
produces a cookie_box
token on the
compartment
place. We do not need to state the tokens to be consumed
because the firing mode already uniquely identifies the tokens to be
consumed.
In addition to the structure callback functions there are another seven callback functions that determine how the net instance appears as an Erlang actor to the outside world:
code_change/3
determines what happens when a hot code reload
appearshandle_call/3
synchronous message exchangehandle_cast/2
asynchronous message receptionhandle_info/2
asynchronous reception of an unformatted messageinit/1
initializes the gen_pnet instanceterminate/2
determines what happens when the net instance is
stoppedtrigger/3
allows to add a side effects to the generation of a
tokenThe code_change/3
function determines what happens when a hot code reload
appears. This callback is identical to the code_change/3
function in the
gen_server
behavior.
code_change( _OldVsn, NetState, _Extra ) -> {ok, NetState}.
The handle_call/3
function performs a synchronous exchange of messages
between the caller and the net instance. The first argument is the request
message, the second argument is a tuple identifying the caller, and the third
argument is a #net_state{}
record instance describing the current state of
the net. The handle_call/3
function can generate a reply without changing
the net marking by returning a {reply, Reply}
tuple, it can generate a
reply, consuming or producing tokens by returning a
{reply, Reply, ConsumeMap, ProduceMap}
tuple, it can defer replying without
changing the net marking by returning noreply
, it can defer replying,
consuming or producing tokens by returning a
{noreply, ConsumeMap, ProduceMap}
tuple, or it can stop the net instance by
returning {stop, Reason, Reply}
.
handle_call( insert_coin, _From, _NetState ) -> {reply, ok, #{}, #{ coin_slot => [coin] }}; handle_call( remove_cookie_box, _From, NetState ) -> case gen_pnet:get_ls( compartment, NetState ) of [] -> {reply, {error, empty_compartment}}; [_|_] -> {reply, ok, #{ compartment => [cookie_box] }, #{}} end; handle_call( _Request, _From, _NetState ) -> {reply, {error, bad_msg}}.
Here, we react to two kinds of messages: Inserting a coin in the coin slot
and removing a cookie box from the compartment. Thus, we react to an
insert_coin
message by replying with ok
, consuming nothing and producing
a coin
token on the coin_slot
place. When receiving a remove_cookie_box
message, we check whether the compartment
place is empty, replying with an
error message if it is, otherwise replying with ok
, consuming one
cookie_box
token from the compartment
place, and producing nothing. Calls
that are neither insert_coin
nor remove_cookie_box
are responded to with
an error message.
The handle_cast/2
function reacts to an asynchronous message received by
the net instance. The first argument is the request while the second argument
is a #net_state{}
record instance. The handle_cast/2
function can either
leave the net unchanged by returning noreply
or it can consume or produce
tokens by returning a {noreply, ConsumeMap, ProduceMap}
tuple.
handle_cast( _Request, _NetState ) -> noreply.
Here, we just ignore any cast.
The handle_info/2
function reacts to an asynchronous, unformatted message
received by the net instance. The first argument is the message term while
the second argument is a #net_state{}
record instance. The handle_info/2
function can either leave the net unchanged by returning noreply
or it can
consume or produce tokens by returning a {noreply, ConsumeMap, ProduceMap}
tuple.
handle_info( _Request, _NetState ) -> noreply.
Here, we just ignore any message.
The init/1
function initializes the net instance. It is given an initial
argument which is provided with gen_pnet:start_link/n
. The init/1
function is expected to return a user info field which is later handed to
other callback functions.
init( _NetArg ) -> [].
Here, we return the empty list as a dummy user info field.
The terminate/2
function determines what happens when the net instance is
stopped. The first argument is the reason for termination while the second
argument is a #net_state{}
record instance. This callback is identical to
the terminate/2
function in the gen_server
behavior.
terminate( _Reason, _NetState ) -> ok.
The trigger/3
function determines what happens when a token is produced on
a given place. Its first argument Place
is the place name, its second
argument Token
is the token about to be produced, and its third argument
NetState
is the current state of the net. The trigger/3
function is
expected to return either pass
in which case the token is produced
normally, or drop
in which case the token is forgotten.
trigger( _Place, _Token, _NetState ) -> pass.Here, we simply let any token pass.
name() = atom() | {atom(), atom()} | {global, term()} | {via, atom(), term()} | pid()
prop() = atom() | {atom(), term()}
server_name() = {local, atom()} | {global, atom()} | {via, atom(), term()}
start_link_result() = {ok, pid()} | ignore | {error, term()}
call/2 | Synchronously send the term Request to the net instance identified as
Name and return the reply. |
call/3 | Synchronously send the term Request to the net instance identified as
Name and return the reply. |
cast/2 | Asynchronously send the term Request to the net instance identified as
Name . |
get_ls/2 | Extracts the list of tokens on a given place from a given net state. |
get_stats/1 | Extracts the stats field from a given net instance. |
get_usr_info/1 | Extracts the user info field from a given net state. |
ls/2 | Query the list of tokens on the place named Place in the net instance
identified as Name . |
marking/1 | Query the marking map of the net instance identified as Name
associating to each place name the list of tokens that this place holds. |
reply/2 | Sends a reply to a calling client process. |
reset_stats/1 | Requests the net instance identified as Name to clear its stats. |
start_link/3 | Starts an unregistered net instance. |
start_link/4 | Starts a net instance registered as ServerName using the callback
module NetMod as the callback module for this net instance. |
state_property/3 | Checks if a predicate about the state of the net holds. |
stats/1 | Query the statistics gathered by the net instance identified as Name . |
stop/1 | Requests the net instance identified as Name to stop. |
usr_info/1 | Query the user info term from the net instance identified as Name . |
call(Name::name(), Request::term()) -> term()
Synchronously send the term Request
to the net instance identified as
Name
and return the reply.
See also: call/3.
call(Name, Request, Timeout) -> term()
Synchronously send the term Request
to the net instance identified as
Name
and return the reply.
Timeout
. The request is handled by
the handle_call/3
callback function of the interface module. Herein
Timeout
must be a non-negative integer or the atom infinity
.
cast(Name::name(), Request::term()) -> ok
Asynchronously send the term Request
to the net instance identified as
Name
.
handle_cast/2
callback function of the
interface module. Note that the cast succeeds even if a non-existing
process is addressed or the net instance is down.
get_ls(Place::atom(), NetState::#net_state{}) -> [term()]
Extracts the list of tokens on a given place from a given net state.
Throws an error if the list does not exist.get_stats(NetState::#net_state{}) -> #stats{}
Extracts the stats field from a given net instance.
get_usr_info(NetState::#net_state{}) -> term()
Extracts the user info field from a given net state.
ls(Name, Place) -> {ok, [term()]} | {error, #bad_place{}}
Query the list of tokens on the place named Place
in the net instance
identified as Name
.
Name
can be a process id or a registered process name. The
return value is either {ok, [_]}
if the place exists or a
{error, #bad_place{}}
tuple.
marking(Name::name()) -> #{atom() => [term()]}
Query the marking map of the net instance identified as Name
associating to each place name the list of tokens that this place holds.
Name
can be a process id or a registered process name. The
return value is the Petri net's marking map.
reply(Client::{pid(), term()}, Reply::term()) -> term()
Sends a reply to a calling client process.
This funciton is to be used when the reply to a caller has been deferred by returning{noreply, _, _}
in handle_call/3
.
See also: handle_call/3.
reset_stats(Name::name()) -> ok
Requests the net instance identified as Name
to clear its stats.
start_link(NetMod, NetArg, Options) -> start_link_result()
Starts an unregistered net instance.
See also: start_link/4.
start_link(ServerName, NetMod, InitArg, Options) -> start_link_result()
Starts a net instance registered as ServerName
using the callback
module NetMod
as the callback module for this net instance.
InitArg
argument is later handed to the init/1
callback. The
ServerName
argument can be
{local, Name} | {global, Name} | {via, Module, ViaName}
. Internally,
the server name ServerName
and option list Options
are handed down
to gen_server:start_link/4
as is.
See also: init/1.
state_property(Name, Pred, PlaceLst) -> ok | {error, Reason}
Checks if a predicate about the state of the net holds.
The function takes a Petri net instance identified asName
and asks it
to verify the predicate Pred
over its marking. Herein, Pred
is a
function that takes n token lists, where each of the token lists subsume
the tokens present on the places identified by the PlaceLst
argument.
The predicate is expected to return either ok
or {error, Reason}
where Reason can be any Erlang term.
stats(Name::name()) -> #stats{}
Query the statistics gathered by the net instance identified as Name
.
#stats{}
record consisting of three
#stat{}
record instances characterizing the current, maximum, and
minimum throughput of this net in transition firings per second.
stop(Name::name()) -> ok
Requests the net instance identified as Name
to stop.
usr_info(Name::name()) -> term()
Query the user info term from the net instance identified as Name
.
Generated by EDoc