petri_net_2020 1.0.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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +12 -0
  5. data/CHANGELOG +8 -0
  6. data/Gemfile +9 -0
  7. data/LICENSE +21 -0
  8. data/README.rdoc +97 -0
  9. data/Rakefile +32 -0
  10. data/lib/petri_net/arc.rb +143 -0
  11. data/lib/petri_net/base.rb +30 -0
  12. data/lib/petri_net/coverability_graph/edge.rb +14 -0
  13. data/lib/petri_net/coverability_graph/graph.rb +52 -0
  14. data/lib/petri_net/coverability_graph/node.rb +123 -0
  15. data/lib/petri_net/coverability_graph.rb +8 -0
  16. data/lib/petri_net/graph/edge.rb +64 -0
  17. data/lib/petri_net/graph/graph.rb +324 -0
  18. data/lib/petri_net/graph/node.rb +141 -0
  19. data/lib/petri_net/graph.rb +7 -0
  20. data/lib/petri_net/marking.rb +27 -0
  21. data/lib/petri_net/net.rb +457 -0
  22. data/lib/petri_net/place.rb +131 -0
  23. data/lib/petri_net/reachability_graph/edge.rb +14 -0
  24. data/lib/petri_net/reachability_graph/graph.rb +24 -0
  25. data/lib/petri_net/reachability_graph/node.rb +14 -0
  26. data/lib/petri_net/reachability_graph.rb +8 -0
  27. data/lib/petri_net/transition.rb +135 -0
  28. data/lib/petri_net/version.rb +8 -0
  29. data/lib/petri_net.rb +36 -0
  30. data/petri_net.gemspec +23 -0
  31. data/test/create.rb +64 -0
  32. data/test/reachability_graph/tc_edge.rb +0 -0
  33. data/test/reachability_graph/tc_graph.rb +201 -0
  34. data/test/reachability_graph/tc_node.rb +65 -0
  35. data/test/tc_arc.rb +0 -0
  36. data/test/tc_petri_net.rb +371 -0
  37. data/test/tc_place.rb +0 -0
  38. data/test/tc_transition.rb +7 -0
  39. data/test/ts_all.rb +4 -0
  40. data/test/ts_petri_net.rb +6 -0
  41. data/test/ts_reachability_graph.rb +5 -0
  42. metadata +137 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 84efe45538b89da15a248ef1c52cfb8ffe686c1b432979f52914e3b9db978a4f
4
+ data.tar.gz: c19c84722a8071d9ab7e0e076b05390b2a14826e36b653da149447de03eee4d8
5
+ SHA512:
6
+ metadata.gz: 73f04577f5c74c937a5e0a912e0c047dbea201e9957f3dc5d8b4965bba5d808ad21e0a14b53148b67bc0ee36258d25a2f7beff3f897e509ebd55320bd812b6fb
7
+ data.tar.gz: 14712b4c660e8435f8c38556dde62c859267edc74a02254e43249a4f6616e8b0858feba60917e051b6904f40dae0b7078b3b82b0463af0b0b38622e3ca557c9e
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ pkg/*.gem
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.6
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - jruby-19mode # JRuby in 1.9 mode
5
+ - 2.0.0
6
+
7
+ before_install:
8
+ - gem install net-sftp
9
+ - sudo apt-get install graphviz
10
+
11
+ script:
12
+ - rake test
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/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in petri.gemspec
6
+ gemspec
7
+ gem 'rake'
8
+ gem 'rgl', "~>0.5.6"
9
+ #gem 'rgl', git: 'https://github.com/monora/rgl.git'
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,97 @@
1
+ {<img src="https://travis-ci.org/cclausen/petri_net.png" />}[https://travis-ci.org/cclausen/petri_net]
2
+
3
+ {<img src="https://codeclimate.com/github/cclausen/petri_net.png" />}[https://codeclimate.com/github/cclausen/petri_net]
4
+
5
+ = PTNet: Petri-net Simulation in Ruby
6
+ ===== based on the code by Brian D Nelson <bdnelson@wildcoder.com>
7
+ ===== Christian Clausen <cclausen@tzi.de>
8
+
9
+ == Version 0.6
10
+ I implemented most of the basics you need for playing with PetriNets but there are a lot things to add.
11
+ Next big step will be a stochastic analysis of the PetriNet.
12
+
13
+ == Overview
14
+ This package provides a ruby based simulation package for Petri nets.
15
+ You can build PetriNets, fire transitions and calculate statistica or solve problems in the net.
16
+ It also provides a visualization of the net (and even the Reachability Graph of the given net)
17
+
18
+
19
+ == Petri Net Resources
20
+ * http://en.wikipedia.org/wiki/Petri_net - Wikipedia
21
+
22
+ == Usage
23
+ === Creation
24
+ At first you need to create a blank petrinet:
25
+ net = PetriNet::Net.new
26
+ Then you can add places and transitions:
27
+ net << PetriNet::Place.new
28
+ net << PetriNet::Transition.new
29
+ 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:
30
+ net << PetriNet::Place.new(name: "A")
31
+ net << PetriNet::Transition.new(name: "a")
32
+ net << PetriNet::Arc.new(source: "A", destination: "a")
33
+ 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.
34
+ You can get a place/transition/arc by
35
+ net.get_place( name )
36
+ net.get_transition( name )
37
+ net.get_arc( name )
38
+ Or if you know the id (but not the name) you can use
39
+ net.get_object(id)
40
+ Of course you can save the Objects while creating them and add them afterwards:
41
+ place_B = PetriNet::Place.new(name: "B")
42
+ net << place_B
43
+ or
44
+ net.add_object place_B
45
+ or
46
+ net.add_place place_B
47
+ if you want to use this.
48
+ PetriNet#add_object even accepts arrays of accepted objects
49
+
50
+ 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.
51
+
52
+ === Markings
53
+ !!! 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.
54
+
55
+ !!! 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.
56
+
57
+ You can create markings like other PetriNet::Base objects with
58
+ marking = PetriNet::Marking.new
59
+ And add them to a place with
60
+ place_B.add_marking(marking)
61
+
62
+ But since I see no sence in creating the specific markings you can do
63
+ place_B.add_marking(3)
64
+ to add three markings to a place. Without any argument it will add just one marking.
65
+
66
+ 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.
67
+ You can even set the markings with
68
+ net.set_markings([1,3,2,4]
69
+ 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)
70
+
71
+ === Visualization
72
+ You can just print yout PetriNet with
73
+ net.to_s
74
+ as you should know this means you can write
75
+ puts net
76
+
77
+ For a more fancy way to show off with your PetriNet you should try
78
+ net.to_gv
79
+ which generates the GraphViz-Code for the PetriNet.
80
+ It is planned to use the GraphViz-gem in the future to create png and stuff like that directly.
81
+
82
+ === Reachability Graph
83
+ 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.
84
+ You can generate the reachablility Graph with
85
+ net.generate_reachability_graph
86
+ As a PetriNet itself you can to_s and to_gv the reachablility Graph too.
87
+
88
+ === More documentation will come soon
89
+
90
+ == Contributing
91
+ Neither the library nor the documentation is finished by now, so please feel free to contribute
92
+
93
+ Just write an email and/or fork this project. Active contributors will get write-access to this repository too.
94
+
95
+
96
+ == Gem
97
+ This library is available on rubygems.org soon
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "bundler/setup"
5
+ rescue LoadError
6
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
7
+ end
8
+
9
+ require "rdoc/task"
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = "rdoc"
13
+ rdoc.title = "Wf"
14
+ rdoc.options << "--line-numbers"
15
+ rdoc.rdoc_files.include("README.md")
16
+ rdoc.rdoc_files.include("lib/**/*.rb")
17
+ end
18
+
19
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
20
+
21
+
22
+ require "bundler/gem_tasks"
23
+
24
+ require "rake/testtask"
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << "test"
28
+ t.pattern = "test/**/*_test.rb"
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PetriNet
4
+ # Arc
5
+ class Arc < PetriNet::Base
6
+ # Unique ID
7
+ attr_reader :id
8
+ # human readable name
9
+ attr_accessor :name
10
+ # Description
11
+ attr_accessor :description
12
+ # Arc weight
13
+ attr_accessor :weight
14
+ # Source-object
15
+ attr_reader :source
16
+ # Source-object
17
+ attr_reader :destination
18
+ # The net this arc belongs to
19
+ attr_writer :net
20
+
21
+ # Creates an arc.
22
+ # 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
23
+ def initialize(options = {}, &block)
24
+ @id = next_object_id
25
+ @name = (options[:name] || "Arc#{@id}")
26
+ @description = (options[:description] || "Arc #{@id}")
27
+ @weight = (options[:weight] || 1)
28
+ add_source(options[:source]) unless options[:source].nil?
29
+ add_destination(options[:destination]) unless options[:destination].nil?
30
+
31
+ yield self unless block.nil?
32
+ end
33
+
34
+ # Add a source object to this arc. Validation of the source object will be performed
35
+ # before the object is added to the arc and an exception will be raised.
36
+ def add_source(object)
37
+ if object.class.to_s == 'String'
38
+ object = (@net.get_place(object) || @net.get_transition(object))
39
+ end
40
+ if validate_source_destination(object)
41
+ @source = object
42
+ object.add_output(self)
43
+ else
44
+ raise "Invalid arc source object: #{object.class}"
45
+ end
46
+ end
47
+
48
+ # Add a destination object
49
+ def add_destination(object)
50
+ if object.class.to_s == 'String'
51
+ object = (@net.get_place(object) || @net.get_transition(object))
52
+ end
53
+ if validate_source_destination(object)
54
+ @destination = object
55
+ object.add_input(self)
56
+ else
57
+ raise "Invalid arc destination object: #{object.class}"
58
+ end
59
+ end
60
+
61
+ # A Petri Net is said to be ordinary if all of its arc weights are 1's.
62
+ # Is this arc ordinary?
63
+ def ordinary?
64
+ @weight == 1
65
+ end
66
+
67
+ # Validate this arc.
68
+ def validate(net)
69
+ return false if @id < 1
70
+ return false if @name.nil? || (@name.length <= 0)
71
+ return false if @weight < 1
72
+ return false if @source.nil? || @destination.nil?
73
+ return false if @source == @destination
74
+ return false if @source.class == @destination.class
75
+
76
+ if @source.class.to_s == 'PetriNet::Place'
77
+ return net.objects_include? @source
78
+ elsif @source.class.to_s == 'PetriNet::Transition'
79
+ return net.objects_include? @source
80
+ else
81
+ return false
82
+ end
83
+ if @destination.class.to_s == 'PetriNet::Place'
84
+ return net.objects.include? @destination
85
+ elsif @destination.class.to_s == 'PetriNet::Transition'
86
+ return net.objects.include? @destination
87
+ else
88
+ return false
89
+ end
90
+
91
+ true
92
+ end
93
+
94
+ # Stringify this arc.
95
+ def to_s
96
+ "#{@id}: #{@name} (#{@weight}) #{@source.id} -> #{@destination.id}"
97
+ end
98
+
99
+ # Gives the GraphViz-representation of this arc as string of a GV-Edge
100
+ def to_gv
101
+ "\t#{@source.gv_id} -> #{@destination.gv_id} [ label = \"#{@name}\", headlabel = \"#{@weight}\" ];\n"
102
+ end
103
+
104
+ # Checks if the information in this arc are still correct.
105
+ # The information can get wrong if you merge two nets together.
106
+ def need_update?(net)
107
+ if net.get_object(@source.id).nil? || (@source.name != net.get_object(@source.id).name)
108
+ return true
109
+ end
110
+ if net.get_object(@destination.id).nil? || (@destination.name != net.get_object(@destination.id).name)
111
+ return true
112
+ end
113
+ end
114
+
115
+ # Updates the information in this arc
116
+ # Should only be necessary if PetriNet::Arc#need_update? is true
117
+ # affects source and destination
118
+ def update(net)
119
+ @source.id = net.objects_find_index @source
120
+ @destination.id = net.objects_find_index @destination
121
+ end
122
+
123
+ def <=>(object)
124
+ return false unless object.class.to_s == 'PetriNet::Arc'
125
+ return false unless object.source == source && object.destination == destination
126
+
127
+ object.weight <=> weight
128
+ end
129
+
130
+ private
131
+
132
+ # Validate source or destination object
133
+ def validate_source_destination(object)
134
+ return false if object.nil?
135
+
136
+ return object.class.to_s == 'PetriNet::Place' || object.class.to_s == 'PetriNet::Transition'
137
+
138
+ # return if @source.nil? or @source.class.to_s == object.class.to_s
139
+ # return if @destination.nil? or @destination.class.to_s == object.class.to_s
140
+ true
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PetriNet
4
+ # Common structure
5
+ class Base
6
+ # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class.
7
+ attr_accessor :logger
8
+
9
+ # Global object count.
10
+ @@object_count = 0
11
+
12
+ # Initialize the base class.
13
+ def initialize(_options = {})
14
+ @logger = Logger.new(STDOUT)
15
+ @logger.level = Logger::INFO
16
+ end
17
+
18
+ # Get the next object ID (object count).
19
+ def next_object_id
20
+ @@object_count += 1
21
+ end
22
+
23
+ # Resets the object-count
24
+ # This should not be used without extreme care
25
+ # It's made for testing-purposes only
26
+ def reset
27
+ @@object_count = 0
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PetriNet::CoverabilityGraph::Edge < PetriNet::Base
4
+ # Creates an edge for PetriNet::CoverabilityGraph
5
+ def initialize(options = {}, &block)
6
+ super(options)
7
+ yield self unless block.nil?
8
+ end
9
+
10
+ # Validates the data holded by this edge, this will be used while adding the edge to the graph
11
+ def validate
12
+ super
13
+ end
14
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphviz'
4
+
5
+ class PetriNet::CoverabilityGraph < PetriNet::Base
6
+ def initialize(net, options = {})
7
+ @objects = []
8
+ @nodes = {}
9
+ @edges = {}
10
+ @name = net.name
11
+ @unlimited = if options['unlimited'].nil?
12
+ true
13
+ else
14
+ options['unlimited']
15
+ end
16
+ end
17
+
18
+ def add_node(node)
19
+ double = false
20
+ inf = false
21
+ @nodes.each_value do |n|
22
+ if node > @objects[n]
23
+ if @unlimited
24
+ double = n
25
+ break
26
+ # return @objects[n].id *-1
27
+ else
28
+ raise PetriNet::Graph::InfiniteReachabilityGraphError
29
+ end
30
+ end
31
+ inf = true if -Float::INFINITY == (node <=> @objects[n])
32
+ rescue ArgumentError
33
+ # just two different markings, completly ok
34
+ end
35
+ # if there was a smaller marking
36
+ return (@objects[double].id * -1) if double
37
+
38
+ node_index = @objects.index node
39
+ # if there already is a node with this marking
40
+ return @objects[node_index].id * -1 unless node_index.nil?
41
+
42
+ return -Float::INFINITY if inf
43
+
44
+ if node.validate && (!@nodes.include? node.name)
45
+ @objects[node.id] = node
46
+ @nodes[node.name] = node.id
47
+ node.graph = self
48
+ return node.id
49
+ end
50
+ false
51
+ end
52
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PetriNet::CoverabilityGraph::Node < PetriNet::Base
4
+ include Comparable
5
+
6
+ # human readable name
7
+ attr_reader :name
8
+ # unique ID
9
+ attr_reader :id
10
+ # Makking this node represents
11
+ attr_reader :markings
12
+ # The graph this node belongs to
13
+ attr_accessor :graph
14
+ # Omega-marked node (unlimited Petrinet -> coverabilitygraph)
15
+ attr_reader :omega_marked
16
+ # Incoming edges
17
+ attr_reader :inputs
18
+ # Outgoing edges
19
+ attr_reader :outputs
20
+ # Label of the node
21
+ attr_reader :label
22
+ # True if this is the start-marking
23
+ attr_reader :start
24
+
25
+ def initialize(options = {}, &block)
26
+ @id = next_object_id
27
+ @name = (options[:name] || "Node#{@id}")
28
+ @description = (options[:description] || "Node #{@id}")
29
+ @inputs = []
30
+ @outputs = []
31
+ @label = (options[:label] || @name)
32
+ @markings = options[:markings]
33
+ @start = (options[:start] || false)
34
+ raise ArgumentError, 'Every Node needs markings' if @markings.nil?
35
+
36
+ @omega_marked = if @markings.include? Float::INFINITY
37
+ true
38
+ else
39
+ false
40
+ end
41
+
42
+ yield self unless block.nil?
43
+ end
44
+
45
+ # Add an omega-marking to a specified place
46
+ def add_omega(object)
47
+ ret = []
48
+ if object.class.to_s == 'PetriNet::CoverabilityGraph::Node'
49
+ if self < object
50
+ counter = 0
51
+ object.markings.each do |marking|
52
+ if @markings[counter] < marking
53
+ @markings[counter] = Float::INFINITY
54
+ ret << counter
55
+ end
56
+ counter += 1
57
+ end
58
+ else
59
+ return false
60
+ end
61
+ elsif object.class.to_s == 'Array'
62
+ object.each do |place|
63
+ markings[place] = Float::INFINITY
64
+ ret = object
65
+ end
66
+ elsif object.class.to_s == 'Fixnum'
67
+ markings[object] = Float::INFINITY
68
+ ret = [object]
69
+ end
70
+ @omega_marked = true
71
+ ret
72
+ end
73
+
74
+ def validate
75
+ true
76
+ end
77
+
78
+ def gv_id
79
+ "N#{@id}"
80
+ end
81
+
82
+ def to_gv
83
+ "\t#{gv_id} [ label = \"#{@markings}\" ];\n"
84
+ end
85
+
86
+ # Compare-operator, other Operators are available through comparable-mixin
87
+ def <=>(object)
88
+ return nil unless object.class.to_s == 'PetriNet::CoverabilityGraph::Node'
89
+ return 0 if @markings == object.markings
90
+
91
+ counter = 0
92
+ less = true
93
+ markings.each do |marking|
94
+ if marking <= object.markings[counter] && less
95
+ less = true
96
+ else
97
+ less = false
98
+ break
99
+ end
100
+ counter += 1
101
+ end
102
+ return -1 if less
103
+
104
+ counter = 0
105
+ more = true
106
+ markings.each do |marking|
107
+ if marking >= object.markings[counter] && more
108
+ more = true
109
+ else
110
+ more = false
111
+ break
112
+ end
113
+ counter += 1
114
+ end
115
+ return 1 if more
116
+
117
+ nil
118
+ end
119
+
120
+ def to_s
121
+ "#{@id}: #{@name} (#{@markings})"
122
+ end
123
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'graph'
4
+ require_relative 'coverability_graph/graph'
5
+ require_relative 'coverability_graph/node'
6
+ require_relative 'coverability_graph/edge'
7
+ # class PetriNet::CoverabilityGraph < PetriNet::Base
8
+ # end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PetriNet::Graph::Edge < PetriNet::Base
4
+ # Human readable name
5
+ attr_reader :name
6
+ # Unique ID
7
+ attr_reader :id
8
+ # Graph this edge belongs to
9
+ attr_accessor :graph
10
+ # Probability of the relating transition
11
+ attr_accessor :probability
12
+ # Source of the edge
13
+ attr_reader :source
14
+ # Destination of the edge
15
+ attr_reader :destination
16
+ # Transition this edge is representing
17
+ attr_reader :transition
18
+
19
+ # Creates an edge for PetriNet::Graph
20
+ def initialize(graph, options = {}, &block)
21
+ @graph = graph
22
+ @id = next_object_id
23
+ @name = (options[:name] || "Edge#{@id}")
24
+ @description = (options[:description] || "Edge #{@id}")
25
+ @source = options[:source]
26
+ @destination = options[:destination]
27
+ @label = (options[:label] || @name)
28
+ @probability = options[:probability]
29
+ @transition = (options[:transition] || '')
30
+
31
+ yield self unless block.nil?
32
+ end
33
+
34
+ # Validates the data holded by this edge, this will be used while adding the edge to the graph
35
+ def validate
36
+ return false unless @graph.nodes.key?(@source.name) && @graph.nodes.key?(@destination.name)
37
+
38
+ true
39
+ end
40
+
41
+ def to_gv
42
+ "\t#{@source.gv_id} -> #{@destination.gv_id}#{probability_to_gv};\n"
43
+ end
44
+
45
+ def ==(object)
46
+ return false unless object.class.to_s == 'PetriNet::ReachabilityGraph::Edge'
47
+
48
+ (@source == object.yource && @destination == oject.destination)
49
+ end
50
+
51
+ def to_s
52
+ "#{@id}: #{@name} #{@source} -> #{@destination} )"
53
+ end
54
+
55
+ private
56
+
57
+ def probability_to_gv
58
+ if @probability
59
+ " [ label = \"#{@probability}\" ] "
60
+ else
61
+ ''
62
+ end
63
+ end
64
+ end