diff --git a/README.md b/README.md index 7559257..54a00c3 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Here, we define the net to have the five places in the cookie vending machine. #### trsn_lst/0 -The `trsn_lst/0` function lets us define the names of all transitions in the net: +The `trsn_lst/0` function lets us define the names of all transitions in the net. trsn_lst() -> [a, b]. @@ -105,9 +105,9 @@ Here, the firing of the transition `a` produces a `coin` token on the `cash_box` ### Interface Callback Functions -In addition to the net callback functions there are another six callback functions that determine how the net instance appears as an Erlang actor to the outside world: +In addition to the structure callback functions there are another six 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 happens +- `code_change/3` determines what happens when a hot code reload appears - `handle_call/3` synchronous message exchange - `handle_cast/2` asynchronous message reception - `handle_info/2` asynchronous reception of an unformatted message @@ -116,7 +116,7 @@ In addition to the net callback functions there are another six callback functio #### code_change/3 -The `code_change/3` function determines what happens when a hot code reload happens. This callback is identical to the `code_change/3` function in the `gen_server` behavior. +The `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}. diff --git a/src/gen_pnet.erl b/src/gen_pnet.erl index 0b2cea6..57e2fad 100644 --- a/src/gen_pnet.erl +++ b/src/gen_pnet.erl @@ -2,7 +2,7 @@ %% %% A generic Petri net OTP behavior. %% -%% Copyright 2016 Jorgen Brandt. All Rights Reserved. +%% Copyright 2016-2017 Jorgen Brandt. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,7 +16,21 @@ %% See the License for the specific language governing permissions and %% limitations under the License. %% +%% ------------------------------------------------------------------- %% @author Jorgen Brandt +%% @version 0.1.1 +%% @copyright 2016-2017 Jorgen Brandt. +%% @see gen_pnet_iface +%% @see gen_pnet_struct +%% @doc Callback function definitions and API for the `gen_pnet' behavior. +%% +%% The callbacks defined for the `gen_pnet' behavior may be separated into a +%% Petri net structure part and an actor interface part. Both behaviors are +%% documented in the `gen_pnet_struct' and `gen_pnet_iface' modules +%% respectively. +%% +%% @end +%% ------------------------------------------------------------------- -module( gen_pnet ). @@ -54,7 +68,7 @@ -callback trigger( Place :: atom(), Token :: _ ) -> pass | drop. -%% Net callbacks +%% Structure callbacks -callback place_lst() -> [atom()]. diff --git a/src/gen_pnet_iface.erl b/src/gen_pnet_iface.erl index a9f22bc..d55cd68 100644 --- a/src/gen_pnet_iface.erl +++ b/src/gen_pnet_iface.erl @@ -2,7 +2,7 @@ %% %% A generic Petri net OTP behavior. %% -%% Copyright 2016 Jorgen Brandt. All Rights Reserved. +%% Copyright 2016-2017 Jorgen Brandt. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,7 +16,133 @@ %% See the License for the specific language governing permissions and %% limitations under the License. %% +%% ------------------------------------------------------------------- %% @author Jorgen Brandt +%% @version 0.1.1 +%% @copyright 2016-2017 Jorgen Brandt. +%% @doc Callback function definitions for Petri net actor interface. +%% +%% In addition to the structure callback functions there are another six +%% callback functions that determine how the net instance appears as an Erlang +%% actor to the outside world: +%% +%% +%% +%%

code_change/3

+%% +%% The `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. +%% +%% Example: +%% ``` +%% code_change( _OldVsn, NetState, _Extra ) -> {ok, NetState}. +%% ''' +%% +%%

handle_call/3

+%% +%% 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 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. +%% +%% Example: +%% ``` +%% handle_call( insert_coin, _, _ ) -> +%% {reply, ok, #{}, #{ coin_slot => [coin] }}; +%% +%% handle_call( remove_cookie_box, _, +%% #net_state{ marking = #{ compartment := C } } ) -> +%% +%% case C of +%% [] -> {reply, {error, empty_compartment}}; +%% [_|_] -> {reply, ok, #{ compartment => [cookie_box] }, #{}} +%% end; +%% +%% handle_call( _, _, _ ) -> {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. +%% +%%

handle_cast/2

+%% +%% 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. +%% +%% Example: +%% ``` +%% handle_cast( _Request, _NetState ) -> noreply. +%% ''' +%% Here, we just ignore any cast. +%% +%%

handle_info/2

+%% +%% 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. +%% +%% Example: +%% ``` +%% handle_info( _Request, _NetState ) -> noreply. +%% ''' +%% Here, we just ignore any message. +%% +%%

terminate/2

+%% +%% 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. +%% +%% Example: +%% ``` +%% terminate( _Reason, _NetState ) -> ok. +%% ''' +%% +%%

trigger/2

+%% +%% 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. +%% +%% Example: +%% ``` +%% trigger( _, _ ) -> pass. +%% ''' +%% Here, we simply let any token pass. +%% +%% @end +%% ------------------------------------------------------------------- + diff --git a/src/gen_pnet_net.erl b/src/gen_pnet_net.erl deleted file mode 100644 index fb01820..0000000 --- a/src/gen_pnet_net.erl +++ /dev/null @@ -1,34 +0,0 @@ -%% -*- erlang -*- -%% -%% A generic Petri net OTP behavior. -%% -%% Copyright 2016 Jorgen Brandt. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% @author Jorgen Brandt - --module( gen_pnet_net ). - --callback place_lst() -> [atom()]. - --callback trsn_lst() -> [atom()]. - --callback init_marking( atom() ) -> [_]. - --callback preset( Place :: atom() ) -> [atom()]. - --callback is_enabled( Trsn :: atom(), Mode :: #{ atom() => [_]} ) -> boolean(). - --callback fire( Trsn :: atom(), Mode :: #{ atom() => [_] } ) -> - abort | {produce, #{ atom() => [_] }}. diff --git a/src/gen_pnet_struct.erl b/src/gen_pnet_struct.erl new file mode 100644 index 0000000..d72899e --- /dev/null +++ b/src/gen_pnet_struct.erl @@ -0,0 +1,149 @@ +%% -*- erlang -*- +%% +%% A generic Petri net OTP behavior. +%% +%% Copyright 2016-2017 Jorgen Brandt. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% ------------------------------------------------------------------- +%% @author Jorgen Brandt +%% @version 0.1.1 +%% @copyright 2016-2017 Jorgen Brandt. +%% @doc Callback function definitions for Petri net structure definition. +%% +%% 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 net
  • +%%
  • `trsn_lst/0' returns the names of the transitions in the net
  • +%%
  • `init_marking/1' returns the initial marking for a given place
  • +%%
  • `preset/1' returns the preset places of a given transition
  • +%%
  • `is_enabled/2' determines whether a given transition is enabled in a +%% given mode
  • +%%
  • `fire/2' returns which tokens are produced on what places if a given +%% transition is fired in a given mode that enables this transition
  • +%%
+%% +%% We have a look at each of them in turn. +%% +%%

place_lst/0

+%% +%% The `place_lst/0' function lets us define the names of all places in the net. +%% +%% Example: +%% ``` +%% place_lst() -> +%% [coin_slot, cash_box, signal, storage, compartment]. +%% ''' +%% Here, we define the net to have the five places in the cookie vending +%% machine. +%% +%%

trsn_lst/0

+%% +%% The `trsn_lst/0' function lets us define the names of all transitions in the +%% net. +%% +%% Example: +%% ``` +%% trsn_lst() -> +%% [a, b]. +%% ''' +%% Here, we define the net to have the two places `a' and `b' in the cookie +%% vending machine. +%% +%%

preset/1

+%% +%% The `preset/1' lets us define the preset places of a given transition. +%% +%% Example: +%% ``` +%% 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. +%% +%%

init_marking/1

+%% +%% The `init_marking/1' function lets us define the initial marking for a given +%% place in the form of a token list. +%% +%% Example: +%% ``` +%% init_marking( storage ) -> [cookie_box, cookie_box, cookie_box]; +%% init_marking( _ ) -> []. +%% ''' +%% Here, we initialize the storage place with three `cookie_box` tokens. All +%% other places are left empty. +%% +%%

is_enabled/2

+%% +%% The `is_enabled/2' function is a predicate determining whether a given +%% transition is enabled in a given mode. +%% +%% Example: +%% ``` +%% is_enabled( a, #{ coin_slot := [coin] } ) -> true; +%% is_enabled( b, #{ signal := [sig], storage := [cookie_box] } ) -> true; +%% is_enabled( _, _ ) -> 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. +%% +%%

fire/2

+%% +%% The `fire/2' 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 +%% 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] }}. +%% ''' +%% 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. +%% +%% @end +%% ------------------------------------------------------------------- + + +-module( gen_pnet_struct ). + +-callback place_lst() -> [atom()]. + +-callback trsn_lst() -> [atom()]. + +-callback init_marking( atom() ) -> [_]. + +-callback preset( Place :: atom() ) -> [atom()]. + +-callback is_enabled( Trsn :: atom(), Mode :: #{ atom() => [_]} ) -> boolean(). + +-callback fire( Trsn :: atom(), Mode :: #{ atom() => [_] } ) -> + abort | {produce, #{ atom() => [_] }}.