Skip to content

Commit

Permalink
Documentation updated.
Browse files Browse the repository at this point in the history
  • Loading branch information
joergen7 committed Apr 28, 2017
1 parent 81a6401 commit 1816ff8
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 71 deletions.
2 changes: 1 addition & 1 deletion doc/overview.edoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ The major advantage of modeling applications with Petri nets is that they provid

This OTP behavior allows programming with Petri nets. It implements a very general form of Petri nets using Erlang terms as tokens. This means that (i) tokens are not only markers but can be any data structure conceivable in Erlang, (ii) a place can hold any number of tokens not just one, (iii) transitions can perform any computation conceivable in Erlang.

The Petri net is specified by implementing a set of callback functions (much like the `gen_fsm' behavior) declaring the place names, the transition names, the preset for each transition, in what modes a transition is enabled, what happens, when a transition fires in a given mode, and the net's initial marking. To communicate with the outside world, callback functions handling calls, casts, and unformatted messages can be provided. Finally, the user can specify a trigger function that is called for each token that is about to emerge on a place. This trigger function can devise side effects and can either let the token be created normally or make it vanish. Both terminating and live nets can be defined using `gen_pnet' and even though a live net never finishes to make progress, the net instance is constantly responsive to outside requests. Conflicting transitions fire randomly and fairly.
The Petri net is specified by implementing a set of callback functions (much like the gen_fsm behavior) declaring the place names, the transition names, the preset for each transition, in what modes a transition is enabled, what happens, when a transition fires in a given mode, and the net's initial marking. To communicate with the outside world, callback functions handling calls, casts, and unformatted messages can be provided. Finally, the user can specify a trigger function that is called for each token that is about to emerge on a place. This trigger function can devise side effects and can either let the token be created normally or make it vanish. Both terminating and live nets can be defined using gen_pnet and even though a live net never finishes to make progress, the net instance is constantly responsive to outside requests. Conflicting transitions fire randomly and fairly.
84 changes: 44 additions & 40 deletions src/gen_pnet.erl
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,7 @@
%%====================================================================

%% @doc Starts an unregistered net instance.
%%
%% The `gen_pnet' instance can be initialized with either the callback
%% module name `Mod', implementing all callback functions or with a
%% `#net_state{}' record instance. Such a `#net_state{}' record can be
%% generated using the `new/3' function. The option list `Options' is
%% handed down to `gen_server:start_link/3' as is.
%%
%% @see new/3
%% @see start_link/4
-spec start_link( IfaceMod, Args, Options ) -> start_link_result()
when IfaceMod :: atom(),
Args :: _,
Expand All @@ -169,14 +162,16 @@ start_link( IfaceMod, Args, Options )
when is_atom( IfaceMod ), is_list( Options ) ->
gen_server:start_link( ?MODULE, {IfaceMod, Args}, Options ).

%% @doc Starts a net instance registered to `ServerName' using the callback
%% module `Mod' or a `#net_state' record instance which can be created
%% using `new/3'. Herein, the `ServerName' argument can be
%% `{local, Name} | {global, Name} | {via, Module, ViaName}'. The server
%% name `ServerName' and option list `Options' are handed down to
%% `gen_server:start_link/4' as is.
%% @doc Starts a net instance registered as `ServerName' using the callback
%% module `IfaceMod' as the interface module for this net instance.
%%
%% The `Args' 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 new/3
%% @see init/1
-spec start_link( ServerName, IfaceMod, Args, Options ) -> start_link_result()
when ServerName :: server_name(),
IfaceMod :: atom(),
Expand All @@ -187,35 +182,33 @@ start_link( ServerName, IfaceMod, Args, Options )
when is_tuple( ServerName ), is_atom( IfaceMod ), is_list( Options ) ->
gen_server:start_link( ServerName, ?MODULE, {IfaceMod, Args}, Options ).

%% @doc Requests the net instance under process id `Name' to list all
%% tokens on the place named `Place'.
%% @doc Query the list of tokens on the place named `Place' in the net instance
%% identified as `Name'.
%%
%% Herein, `Name' can also be a registered process name. The return value is
%% either `{ok, [_]}'' if the place exists or a `{error, #bad_place{}}'
%% tuple.
%% Herein, `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.
-spec ls( Name, Place ) -> {ok, [_]} | {error, #bad_place{}}
when Name :: name(),
Place :: atom().

ls( Name, Place ) when is_atom( Place ) -> gen_server:call( Name, {ls, Place} ).

%% @doc Requests the net instance under process id `Name' to return a
%% marking map, associating to each place name the list of tokens that this
%% place holds.
%% @doc Query the marking map of the net instance identified as `Name'
%% associating to each place name the list of tokens that this place holds.
%%
%% Herein, `Name' can also be a registered process name. The return value
%% is the Petri net's marking map.
%% Herein, `Name' can be a process id or a registered process name. The
%% return value is the Petri net's marking map.
-spec marking( Name :: name() ) -> #{ atom() => [_] }.

marking( Name ) -> gen_server:call( Name, marking ).


%% @doc Query the user info term from the net instance identified as `Name'.
-spec usr_info( Name :: name() ) -> _.

usr_info( Name ) -> gen_server:call( Name, usr_info ).

%% @doc Requests the net instance under process id `Name' to return the
%% throughput of the net.
%% @doc Query the statistics gathered by the net instance identified as `Name'.
%%
%% The throughput is given as a `#stats{}' record consisting of three
%% `#stat{}' record instances characterizing the current, maximum, and
Expand All @@ -224,33 +217,39 @@ usr_info( Name ) -> gen_server:call( Name, usr_info ).

stats( Name ) -> gen_server:call( Name, stats ).

%% @doc Requests the net instance under process id `Name' to clear its stats.
%% @doc Requests the net instance identified as `Name' to clear its stats.
-spec reset_stats( Name :: name() ) -> ok.

reset_stats( Name ) -> gen_server:call( Name, reset_stats ).

%% @doc Signal the net instance under process id `Name' to stop.
%% @doc Requests the net instance identified as `Name' to stop.
-spec stop( Name :: name() ) -> ok.

stop( Name ) -> gen_server:stop( Name ).

%% @doc Send the request term `Request' to the net instance under process id

%% @doc Synchronously send the term `Request' to the net instance identified as
%% `Name' and return the reply.
%%
%% The request is handled by the `handle_call/3' callback function of the
%% interface module.

%% The timeout is implicitly set to five seconds.
%% @see call/3
-spec call( Name :: name(), Request :: _ ) -> _.

call( Name, Request ) -> gen_server:call( Name, {call, Request} ).


%% @doc Synchronously send the term `Request' to the net instance identified as
%% `Name' and return the reply.
%%
%% The timeout is explicitly set to `Timeout`. The request is handled by
%% the `handle_call/3' callback function of the interface module.
-spec call( Name :: name(), Request :: _, Timeout :: non_neg_integer() ) -> _.

call( Name, Request, Timeout ) when is_integer( Timeout ), Timeout >= 0 ->
gen_server:call( Name, {call, Request}, Timeout ).

%% @doc Send the request term `Request' asynchronously to the net instance under
%% process id `Name'.
%% @doc Asynchronously send the term `Request' to the net instance identified as
%% `Name'.
%%
%% The request is handled by the `handle_cast/2' callback function of the
%% interface module. Note that the cast succeeds even if a non-existing
Expand All @@ -260,7 +259,11 @@ call( Name, Request, Timeout ) when is_integer( Timeout ), Timeout >= 0 ->
cast( Name, Request ) ->
gen_server:cast( Name, {cast, Request} ).


%% @doc 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 handle_call/3
-spec reply( Client :: {pid(), _}, Reply :: _ ) -> _.

reply( Client, Reply ) when is_tuple( Client ) ->
Expand All @@ -271,7 +274,7 @@ reply( Client, Reply ) when is_tuple( Client ) ->
%% Net state constructor and accessor functions
%%====================================================================

%% @doc Generates an initial instance of a state record.
%% @doc Constructs an initial instance of a state record.
%%
%% Such a state record can be used to initialize a `gen_pnet' instance with
%% `start_link/1' or `start_link/2'.
Expand All @@ -284,19 +287,20 @@ new( NetMod, UsrInfo ) when is_atom( NetMod ) ->
#net_state{ net_mod = NetMod, usr_info = UsrInfo }.


%% @doc Lists the tokens on a given place from a net state.
%% @doc Extracts the list of tokens on a given place from a given net state.
%%
%% Throws an error if the list does not exist.
-spec get_ls( Place :: atom(), NetState :: #net_state{} ) -> [_].

get_ls( Place, #net_state{ marking = Marking } ) -> maps:get( Place, Marking ).


%% @doc Extracts the user info field from a given net state.
-spec get_usr_info( NetState :: #net_state{} ) -> _.

get_usr_info( #net_state{ usr_info = UsrInfo } ) -> UsrInfo.


%% @doc Extracts the stats field from a given net instance.
-spec get_stats( NetState :: #net_state{} ) -> #stats{}.

get_stats( #net_state{ stats = Stats } ) -> Stats.
Expand Down
39 changes: 22 additions & 17 deletions src/gen_pnet_iface.erl
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@
%% <li>`handle_call/3' synchronous message exchange</li>
%% <li>`handle_cast/2' asynchronous message reception</li>
%% <li>`handle_info/2' asynchronous reception of an unformatted message</li>
%% <li>`init/1' initializes the gen_pnet instance</li>
%% <li>`terminate/2' determines what happens when the net instance is
%% stopped</li>
%% <li>`trigger/2' allows to add a side effects to the generation of a
%% <li>`trigger/3' allows to add a side effects to the generation of a
%% token</li>
%% </ul>
%%
Expand All @@ -55,25 +56,28 @@
%% 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 either generate a reply without
%% changing the net marking by returning a `{reply, Reply}' tuple or it can
%% generate a reply, consuming or producing tokens by returning a
%% `{reply, Reply, ConsumeMap, ProduceMap}' tuple.
%% 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}'.
%%
%% Example:
%% ```
%% handle_call( insert_coin, _, _ ) ->
%% handle_call( insert_coin, _From, _NetState ) ->
%% {reply, ok, #{}, #{ coin_slot => [coin] }};
%%
%% handle_call( remove_cookie_box, _,
%% #net_state{ marking = #{ compartment := C } } ) ->
%% handle_call( remove_cookie_box, _From, NetState ) ->
%%
%% case C of
%% case gen_pnet:get_ls( compartment, NetState ) of
%% [] -> {reply, {error, empty_compartment}};
%% [_|_] -> {reply, ok, #{ compartment => [cookie_box] }, #{}}
%% end;
%%
%% handle_call( _, _, _ ) -> {reply, {error, bad_msg}}.
%% 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
Expand Down Expand Up @@ -126,17 +130,18 @@
%% terminate( _Reason, _NetState ) -> ok.
%% '''
%%
%% <h4>trigger/2</h4>
%% <h4>trigger/3</h4>
%%
%% The `trigger/2' function determines what happens when a token is produced on
%% a given place. Its first argument is the place name and its second argument
%% is the token about to be produced. The `trigger/2' function is expected to
%% return either `pass' in which case the token is produced normally, or `drop'
%% in which case the token is forgotten.
%% 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.
%%
%% Example:
%% ```
%% trigger( _, _ ) -> pass.
%% trigger( _Place, _Token, _NetState ) -> pass.
%% '''
%% Here, we simply let any token pass.
%%
Expand Down
29 changes: 16 additions & 13 deletions src/gen_pnet_struct.erl
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@
%% <ul>
%% <li>`place_lst/0' returns the names of the places in the net</li>
%% <li>`trsn_lst/0' returns the names of the transitions in the net</li>
%% <li>`init_marking/1' returns the initial marking for a given place</li>
%% <li>`init_marking/2' returns the initial marking for a given place</li>
%% <li>`preset/1' returns the preset places of a given transition</li>
%% <li>`is_enabled/2' determines whether a given transition is enabled in a
%% given mode</li>
%% <li>`fire/2' returns which tokens are produced on what places if a given
%% <li>`fire/3' returns which tokens are produced on what places if a given
%% transition is fired in a given mode that enables this transition</li>
%% </ul>
%%
Expand Down Expand Up @@ -76,15 +76,16 @@
%% `coin_slot' while the transition `b' has the places `signal' and `storage'
%% in its preset.
%%
%% <h4>init_marking/1</h4>
%% <h4>init_marking/2</h4>
%%
%% The `init_marking/1' function lets us define the initial marking for a given
%% place in the form of a token list.
%% 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'.
%%
%% Example:
%% ```
%% init_marking( storage ) -> [cookie_box, cookie_box, cookie_box];
%% init_marking( _ ) -> [].
%% 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.
Expand All @@ -107,20 +108,22 @@
%% transition. E.g., managing to get a `button' token on the `coin_slot' place
%% will not enable any transition.
%%
%% <h4>fire/2</h4>
%% <h4>fire/3</h4>
%%
%% The `fire/2' function defines what tokens are produced when a given
%% 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/2' function is called only on modes for which
%% `is_enabled/2' returns `true'. The `fire/2' function is expected to return
%% to token lists. The `fire/3' function is called only on modes for which
%% `is_enabled/2' 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.
%%
%% Example:
%% ```
%% fire( a, _ ) -> {produce, #{ cash_box => [coin], signal => [sig] }};
%% fire( b, _ ) -> {produce, #{ compartment => [cookie_box] }}.
%% 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
Expand Down

0 comments on commit 1816ff8

Please sign in to comment.