petri_net 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/CHANGELOG +8 -0
- data/LICENSE +21 -0
- data/README.rdoc +93 -0
- data/Rakefile +30 -0
- data/lib/petri_net/arc.rb +133 -0
- data/lib/petri_net/base.rb +28 -0
- data/lib/petri_net/edge.rb +38 -0
- data/lib/petri_net/marking.rb +28 -0
- data/lib/petri_net/net.rb +314 -0
- data/lib/petri_net/node.rb +44 -0
- data/lib/petri_net/place.rb +108 -0
- data/lib/petri_net/reachability_graph.rb +93 -0
- data/lib/petri_net/transition.rb +109 -0
- data/lib/petri_net.rb +35 -0
- data/test/create.rb +64 -0
- data/test/tc_arc.rb +0 -0
- data/test/tc_edge.rb +0 -0
- data/test/tc_graph.rb +0 -0
- data/test/tc_node.rb +0 -0
- data/test/tc_petri_net.rb +320 -0
- data/test/tc_place.rb +0 -0
- data/test/tc_transition.rb +6 -0
- data/test/ts_test_all.rb +2 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3862dd137aeee506ef93dc6c0247d5f872bb5dba
|
4
|
+
data.tar.gz: 1159a8fa6abde650583c95cd80a9d54a749b57e3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ad0e40f4bfb501824223f2f36fc0dc32aa42391e8903fd2a21f5805f88ec946b91f7cf238124d6f7fb06da055d65c50301f44f3ca2b9fd9279469a683e0b2885
|
7
|
+
data.tar.gz: c43775deaab72b0f5a8c56a7040665d2762d7ac4ecbd07230bb4382cab51998fc9fd81fd4c653f32153dded901baab56d5761b8f69a1a12d3f7bf785075b01aa
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0
|
data/CHANGELOG
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
2009-08-14 Brian D. Nelson <bdnelson@wildcoder.com>
|
2
|
+
* Initial project creation.
|
3
|
+
|
4
|
+
2010-08-26 Brian D. Nelson <bdnelson@wildcoder.com>
|
5
|
+
* Restart of project
|
6
|
+
|
7
|
+
2013-13.12 Christian Clausen <cclausen@tzi.de>
|
8
|
+
* Fork project to create a petrinet library to use for my Bachelor-Thesis
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2013, Christian Clausen (cclausen@tzi.de)
|
2
|
+
|
3
|
+
Copyright (c) 2009, Brian D. Nelson (bdnelson@wildcoder.com)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
= PTNet: Petri-net Simulation in Ruby
|
2
|
+
===== based on the code by Brian D Nelson <bdnelson@wildcoder.com>
|
3
|
+
===== Christian Clausen cclausen@tzi.de
|
4
|
+
|
5
|
+
== Version 0.5
|
6
|
+
I implemented most of the basics you need for playing with PetriNets but there are a lot things to add.
|
7
|
+
Next big step will be a stochastic analysis of the PetriNet.
|
8
|
+
|
9
|
+
== Overview
|
10
|
+
This package provides a ruby based simulation package for Petri nets.
|
11
|
+
You can build PetriNets, fire transitions and calculate statistica or solve problems in the net.
|
12
|
+
It also provides a visualization of the net (and even the Reachability Graph of the given net)
|
13
|
+
|
14
|
+
|
15
|
+
== Petri Net Resources
|
16
|
+
* http://en.wikipedia.org/wiki/Petri_net - Wikipedia
|
17
|
+
|
18
|
+
== Usage
|
19
|
+
=== Creation
|
20
|
+
At first you need to create a blank petrinet:
|
21
|
+
net = PetriNet::Net.new
|
22
|
+
Then you can add places and transitions:
|
23
|
+
net << PetriNet::Place.new
|
24
|
+
net << PetriNet::Transition.new
|
25
|
+
It's a good idea to name the places and transitions, as you need to refer to them to add, so the better way will be:
|
26
|
+
net << PetriNet::Place.new(name: "A")
|
27
|
+
net << PetriNet::Transition.new(name: "a")
|
28
|
+
net << PetriNet::Arc.new(source: "A", destination: "a")
|
29
|
+
As you see, an arc needs to get a source and a destination. You can, but you don't need to name your arcs, as you don't need to refer to them later in most cases.
|
30
|
+
You can get a place/transition/arc by
|
31
|
+
net.get_place( name )
|
32
|
+
net.get_transition( name )
|
33
|
+
net.get_arc( name )
|
34
|
+
Or if you know the id (but not the name) you can use
|
35
|
+
net.get_object(id)
|
36
|
+
Of course you can save the Objects while creating them and add them afterwards:
|
37
|
+
place_B = PetriNet::Place.new(name: "B")
|
38
|
+
net << place_B
|
39
|
+
or
|
40
|
+
net.add_object place_B
|
41
|
+
or
|
42
|
+
net.add_place place_B
|
43
|
+
if you want to use this.
|
44
|
+
PetriNet#add_object even accepts arrays of accepted objects
|
45
|
+
|
46
|
+
To create an arc you do not need to specify source and destination, you can add them with "add_source" and "add_destination" later on, but an arc has to have both before being added to a net.
|
47
|
+
|
48
|
+
=== Markings
|
49
|
+
!!! Attention, I'm not sure how I will implement markings in the end, this part might be wrong because I changed something. I'm not happy with the way I handle markings/token currently. Currently are PetriNet::Base-Objects which makes no sence in my opinion.
|
50
|
+
|
51
|
+
!!! Attention, Markings are inconsistens at the moment. In the original code markings are what I call "token" or "tokken" and a marking is the "state" of a petrinet, which means how many token are in which places. It's hard to understand which markings are meant as currently everything is called "marking" I will refactor this some time.
|
52
|
+
|
53
|
+
You can create markings like other PetriNet::Base objects with
|
54
|
+
marking = PetriNet::Marking.new
|
55
|
+
And add them to a place with
|
56
|
+
place_B.add_marking(marking)
|
57
|
+
|
58
|
+
But since I see no sence in creating the specific markings you can do
|
59
|
+
place_B.add_marking(3)
|
60
|
+
to add three markings to a place. Without any argument it will add just one marking.
|
61
|
+
|
62
|
+
You can get the current marking with net.get_markings. This will give you an Array of state-markings. [1,3,2,4] would mean, the first place (in order of id's, but be careful, every PetriNet::Base-object has a unique id) will hold one marking (or token) the second place will hold 3 and so on.
|
63
|
+
You can even set the markings with
|
64
|
+
net.set_markings([1,3,2,4]
|
65
|
+
but be carefull with this one, maybe this should be private in future but it can be very usefull to save the initial state or to set the net to a specific state for some algorithms (e.g. for generating the Reachability Graph)
|
66
|
+
|
67
|
+
=== Visualization
|
68
|
+
You can just print yout PetriNet with
|
69
|
+
net.to_s
|
70
|
+
as you should know this means you can write
|
71
|
+
puts net
|
72
|
+
|
73
|
+
For a more fancy way to show off with your PetriNet you should try
|
74
|
+
net.to_gv
|
75
|
+
which generates the GraphViz-Code for the PetriNet.
|
76
|
+
It is planned to use the GraphViz-gem in the future to create png and stuff like that directly.
|
77
|
+
|
78
|
+
=== Reachability Graph
|
79
|
+
The Reachablility Graph is a Graph with nodes representing reachable markings of the net in the current state and edges reresenting the transitions you need to get from one node to another.
|
80
|
+
You can generate the reachablility Graph with
|
81
|
+
net.generate_reachability_graph
|
82
|
+
As a PetriNet itself you can to_s and to_gv the reachablility Graph too.
|
83
|
+
|
84
|
+
=== More documentation will come soon
|
85
|
+
|
86
|
+
== Contributing
|
87
|
+
Neither the library nor the documentation is finished by now, so please feel free to contribute
|
88
|
+
|
89
|
+
Just write an email and/or fork this project. Active contributors will get write-access to this repository too.
|
90
|
+
|
91
|
+
|
92
|
+
== Gem
|
93
|
+
This library is available on rubygems.org soon
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'hanna/rdoctask'
|
4
|
+
require 'net/sftp'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
desc 'Default task'
|
8
|
+
task :default => [:test, :rdoc, :push, :clean]
|
9
|
+
|
10
|
+
task(:test) { puts "==> Running main test suite" }
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.test_files = FileList['test/tc_*.rb']
|
13
|
+
t.ruby_opts = ['-rubygems'] if defined? Gem
|
14
|
+
end
|
15
|
+
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_files.include('LICENSE', 'CHANGELOG', 'README', 'lib/')
|
18
|
+
rdoc.title = "PetriNet Documentation"
|
19
|
+
rdoc.options << '--webcvs=http://svn.wildcoder.com/svn/petri/trunk/'
|
20
|
+
rdoc.rdoc_dir = 'doc' # rdoc output folder
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'Clean up unused files.'
|
24
|
+
task :clean => :clobber_rdoc do
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Run tests.'
|
28
|
+
task :test do
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module PetriNet
|
2
|
+
# Arc
|
3
|
+
class Arc < PetriNet::Base
|
4
|
+
# Unique ID
|
5
|
+
attr_reader :id
|
6
|
+
# human readable name
|
7
|
+
attr_accessor :name
|
8
|
+
# Description
|
9
|
+
attr_accessor :description
|
10
|
+
# Arc weight
|
11
|
+
attr_accessor :weight
|
12
|
+
# Source-object
|
13
|
+
attr_reader :source
|
14
|
+
# Source-object
|
15
|
+
attr_reader :destination
|
16
|
+
# The net this arc belongs to
|
17
|
+
attr_writer :net
|
18
|
+
|
19
|
+
# Creates an arc.
|
20
|
+
# An arc is an directed edge between a place and a transition (or visa versa) and can have a weight which indicates how many token it comsumes or produces from/to the place
|
21
|
+
def initialize(options = {}, &block)
|
22
|
+
@id = next_object_id
|
23
|
+
@name = (options[:name] or "Arc#{@id}")
|
24
|
+
@description = (options[:description] or "Arc #{@id}")
|
25
|
+
@weight = (options[:weight] or 1)
|
26
|
+
self.add_source(options[:source]) unless options[:source].nil?
|
27
|
+
self.add_destination(options[:destination]) unless options[:destination].nil?
|
28
|
+
|
29
|
+
yield self unless block == nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# Add a source object to this arc. Validation of the source object will be performed
|
33
|
+
# before the object is added to the arc and an exception will be raised.
|
34
|
+
def add_source(object)
|
35
|
+
if object.class.to_s == "String"
|
36
|
+
object = (net.get_place object or net.get_transition object)
|
37
|
+
end
|
38
|
+
if validate_source_destination(object)
|
39
|
+
@source = object
|
40
|
+
object.add_output(self)
|
41
|
+
else
|
42
|
+
raise "Invalid arc source object: #{object.class}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Add a destination object
|
47
|
+
def add_destination(object)
|
48
|
+
if object.class.to_s == "String"
|
49
|
+
object = (net.get_place object or net.get_transition object)
|
50
|
+
end
|
51
|
+
if validate_source_destination(object)
|
52
|
+
@destination = object
|
53
|
+
object.add_input(self)
|
54
|
+
else
|
55
|
+
raise "Invalid arc destination object: #{object.class}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# A Petri Net is said to be ordinary if all of its arc weights are 1's.
|
60
|
+
# Is this arc ordinary?
|
61
|
+
def ordinary?
|
62
|
+
@weight == 1
|
63
|
+
end
|
64
|
+
|
65
|
+
# Validate this arc.
|
66
|
+
def validate(net)
|
67
|
+
return false if @id < 1
|
68
|
+
return false if @name.nil? or @name.length <= 0
|
69
|
+
return false if @weight < 1
|
70
|
+
return false if @source.nil? or @destination.nil?
|
71
|
+
return false if @source == @destination
|
72
|
+
return false if @source.class == @destination.class
|
73
|
+
|
74
|
+
if @source.class.to_s == "PetriNet::Place"
|
75
|
+
return net.objects_include? @source
|
76
|
+
elsif @source.class.to_s == "PetriNet::Transition"
|
77
|
+
return net.objects_include? @source
|
78
|
+
else
|
79
|
+
return false
|
80
|
+
end
|
81
|
+
if @destination.class.to_s == "PetriNet::Place"
|
82
|
+
return net.objects.include? @destination
|
83
|
+
elsif @destination.class.to_s == "PetriNet::Transition"
|
84
|
+
return net.objects.include? @destination
|
85
|
+
else
|
86
|
+
return false
|
87
|
+
end
|
88
|
+
return true
|
89
|
+
end
|
90
|
+
|
91
|
+
# Stringify this arc.
|
92
|
+
def to_s
|
93
|
+
"#{@id}: #{@name} (#{@weight}) #{@source.id} -> #{@destination.id}"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Gives the GraphViz-representation of this arc as string of a GV-Edge
|
97
|
+
def to_gv
|
98
|
+
"\t#{@source.gv_id} -> #{@destination.gv_id} [ label = \"#{@name}\", headlabel = \"#{@weight}\" ];\n"
|
99
|
+
end
|
100
|
+
|
101
|
+
# Checks if the information in this arc are still correct.
|
102
|
+
# The information can get wrong if you merge two nets together.
|
103
|
+
def need_update? net
|
104
|
+
if net.get_object(@source.id).nil? || (@source.name != net.get_object(@source.id).name)
|
105
|
+
return true
|
106
|
+
end
|
107
|
+
if net.get_object(@destination.id).nil? || (@destination.name != net.get_object(@destination.id).name)
|
108
|
+
return true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Updates the information in this arc
|
113
|
+
# Should only be necessary if PetriNet::Arc#need_update? is true
|
114
|
+
# affects source and destination
|
115
|
+
def update net
|
116
|
+
@source.id = net.objects_find_index @source
|
117
|
+
@destination.id = net.objects_find_index @destination
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
# Validate source or destination object
|
123
|
+
def validate_source_destination(object)
|
124
|
+
return false if object.nil?
|
125
|
+
|
126
|
+
return object.class.to_s == "PetriNet::Place" || object.class.to_s == "PetriNet::Transition"
|
127
|
+
|
128
|
+
#return if @source.nil? or @source.class.to_s == object.class.to_s
|
129
|
+
#return if @destination.nil? or @destination.class.to_s == object.class.to_s
|
130
|
+
return true
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module PetriNet
|
2
|
+
# Common structure
|
3
|
+
class Base
|
4
|
+
# Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class.
|
5
|
+
attr_accessor :logger
|
6
|
+
|
7
|
+
# Global object count.
|
8
|
+
@@object_count = 0
|
9
|
+
|
10
|
+
# Initialize the base class.
|
11
|
+
def initialize(options = {})
|
12
|
+
@logger = Logger.new(STDOUT)
|
13
|
+
@logger.level = Logger::INFO
|
14
|
+
end
|
15
|
+
|
16
|
+
# Get the next object ID (object count).
|
17
|
+
def next_object_id
|
18
|
+
@@object_count += 1
|
19
|
+
end
|
20
|
+
|
21
|
+
# Resets the object-count
|
22
|
+
# This should not be used without extreme care
|
23
|
+
# It's made for testing-purposes only
|
24
|
+
def reset
|
25
|
+
@@object_count = 0
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class PetriNet::ReachabilityGraph::Edge < PetriNet::Base
|
2
|
+
# Human readable name
|
3
|
+
attr_reader :name
|
4
|
+
# Unique ID
|
5
|
+
attr_reader :id
|
6
|
+
# Graph this edge belongs to
|
7
|
+
attr_accessor :graph
|
8
|
+
|
9
|
+
# Creates an edge for PetriNet::ReachabilityGraph
|
10
|
+
def initialize(options = {}, &block)
|
11
|
+
@id = next_object_id
|
12
|
+
@name = (options[:name] or "Edge#{@id}")
|
13
|
+
@description = (options[:description] or "Edge #{@id}")
|
14
|
+
@source = options[:source]
|
15
|
+
@destination = options[:destination]
|
16
|
+
@label = (options[:label] or @name)
|
17
|
+
|
18
|
+
yield self unless block.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
# Validates the data holded by this edge, this will be used while adding the edge to the graph
|
22
|
+
def validate
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_gv
|
27
|
+
"\t#{@source.gv_id} -> #{@destination.gv_id};\n"
|
28
|
+
end
|
29
|
+
|
30
|
+
def ==(object)
|
31
|
+
return false unless object.class.to_s == "PetriNet::ReachabilityGraph::Edge"
|
32
|
+
(@source == object.yource && @destination == oject.destination)
|
33
|
+
end
|
34
|
+
def to_s
|
35
|
+
"#{@id}: #{@name} #{@source.id} -> #{@destination} )"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module PetriNet
|
2
|
+
# Marking
|
3
|
+
class Marking < PetriNet::Base
|
4
|
+
# depricated
|
5
|
+
attr_accessor :id # Unique ID
|
6
|
+
# depricated
|
7
|
+
attr_accessor :name # Human readable name
|
8
|
+
# depricated
|
9
|
+
attr_accessor :description # Description
|
10
|
+
# depricated
|
11
|
+
attr_accessor :timestep # Marking timestep
|
12
|
+
|
13
|
+
# Create a new marking.
|
14
|
+
# depricated
|
15
|
+
def initialize(options = {}, &block)
|
16
|
+
|
17
|
+
yield self unless block == nil
|
18
|
+
end
|
19
|
+
|
20
|
+
# Validate this marking.
|
21
|
+
def validate
|
22
|
+
end
|
23
|
+
|
24
|
+
# Stringify this marking.
|
25
|
+
def to_s
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|