From 1816ff8937289685544cc681e0fa5c020a04f695 Mon Sep 17 00:00:00 2001 From: Jorgen Brandt Date: Sat, 29 Apr 2017 00:16:07 +0200 Subject: [PATCH] Documentation updated. --- doc/overview.edoc | 2 +- src/gen_pnet.erl | 84 +++++++++++++++++++++-------------------- src/gen_pnet_iface.erl | 39 ++++++++++--------- src/gen_pnet_struct.erl | 29 +++++++------- 4 files changed, 83 insertions(+), 71 deletions(-) diff --git a/doc/overview.edoc b/doc/overview.edoc index cf3474a..f676bfc 100644 --- a/doc/overview.edoc +++ b/doc/overview.edoc @@ -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. \ No newline at end of file +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. \ No newline at end of file diff --git a/src/gen_pnet.erl b/src/gen_pnet.erl index 60cc8e8..db7c1a6 100644 --- a/src/gen_pnet.erl +++ b/src/gen_pnet.erl @@ -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 :: _, @@ -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(), @@ -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 @@ -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 @@ -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 ) -> @@ -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'. @@ -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. diff --git a/src/gen_pnet_iface.erl b/src/gen_pnet_iface.erl index e4246b6..64139e4 100644 --- a/src/gen_pnet_iface.erl +++ b/src/gen_pnet_iface.erl @@ -32,9 +32,10 @@ %%
  • `handle_call/3' synchronous message exchange
  • %%
  • `handle_cast/2' asynchronous message reception
  • %%
  • `handle_info/2' asynchronous reception of an unformatted message
  • +%%
  • `init/1' initializes the gen_pnet instance
  • %%
  • `terminate/2' determines what happens when the net instance is %% stopped
  • -%%
  • `trigger/2' allows to add a side effects to the generation of a +%%
  • `trigger/3' allows to add a side effects to the generation of a %% token
  • %% %% @@ -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 @@ -126,17 +130,18 @@ %% terminate( _Reason, _NetState ) -> ok. %% ''' %% -%%

    trigger/2

    +%%

    trigger/3

    %% -%% 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. %% diff --git a/src/gen_pnet_struct.erl b/src/gen_pnet_struct.erl index b5fecb1..9007117 100644 --- a/src/gen_pnet_struct.erl +++ b/src/gen_pnet_struct.erl @@ -28,11 +28,11 @@ %% %% @@ -76,15 +76,16 @@ %% `coin_slot' while the transition `b' has the places `signal' and `storage' %% in its preset. %% -%%

    init_marking/1

    +%%

    init_marking/2

    %% -%% 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. @@ -107,20 +108,22 @@ %% transition. E.g., managing to get a `button' token on the `coin_slot' place %% will not enable any transition. %% -%%

    fire/2

    +%%

    fire/3

    %% -%% 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