orfeas_petri_flow 0.6.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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +80 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.md +592 -0
  5. data/Rakefile +28 -0
  6. data/lib/petri_flow/colored/arc_expression.rb +163 -0
  7. data/lib/petri_flow/colored/color.rb +40 -0
  8. data/lib/petri_flow/colored/colored_net.rb +146 -0
  9. data/lib/petri_flow/colored/guard.rb +104 -0
  10. data/lib/petri_flow/core/arc.rb +63 -0
  11. data/lib/petri_flow/core/marking.rb +64 -0
  12. data/lib/petri_flow/core/net.rb +121 -0
  13. data/lib/petri_flow/core/place.rb +54 -0
  14. data/lib/petri_flow/core/token.rb +55 -0
  15. data/lib/petri_flow/core/transition.rb +88 -0
  16. data/lib/petri_flow/export/cpn_tools_exporter.rb +322 -0
  17. data/lib/petri_flow/export/json_exporter.rb +224 -0
  18. data/lib/petri_flow/export/pnml_exporter.rb +229 -0
  19. data/lib/petri_flow/export/yaml_exporter.rb +246 -0
  20. data/lib/petri_flow/export.rb +193 -0
  21. data/lib/petri_flow/generators/adapters/aasm_adapter.rb +69 -0
  22. data/lib/petri_flow/generators/adapters/state_machines_adapter.rb +83 -0
  23. data/lib/petri_flow/generators/state_machine_adapter.rb +47 -0
  24. data/lib/petri_flow/generators/workflow_generator.rb +176 -0
  25. data/lib/petri_flow/matrix/analyzer.rb +151 -0
  26. data/lib/petri_flow/matrix/causation.rb +126 -0
  27. data/lib/petri_flow/matrix/correlation.rb +79 -0
  28. data/lib/petri_flow/matrix/crud_event_mapping.rb +74 -0
  29. data/lib/petri_flow/matrix/lineage.rb +113 -0
  30. data/lib/petri_flow/matrix/reachability.rb +128 -0
  31. data/lib/petri_flow/railtie.rb +41 -0
  32. data/lib/petri_flow/registry.rb +85 -0
  33. data/lib/petri_flow/simulation/simulator.rb +188 -0
  34. data/lib/petri_flow/simulation/trace.rb +119 -0
  35. data/lib/petri_flow/tasks/petri_flow.rake +229 -0
  36. data/lib/petri_flow/verification/boundedness_checker.rb +127 -0
  37. data/lib/petri_flow/verification/invariant_checker.rb +144 -0
  38. data/lib/petri_flow/verification/liveness_checker.rb +153 -0
  39. data/lib/petri_flow/verification/reachability_analyzer.rb +152 -0
  40. data/lib/petri_flow/verification_runner.rb +287 -0
  41. data/lib/petri_flow/version.rb +5 -0
  42. data/lib/petri_flow/visualization/graphviz.rb +220 -0
  43. data/lib/petri_flow/visualization/mermaid.rb +191 -0
  44. data/lib/petri_flow/workflow.rb +228 -0
  45. data/lib/petri_flow.rb +164 -0
  46. metadata +174 -0
@@ -0,0 +1,224 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module PetriFlow
6
+ module Export
7
+ # Exports Petri nets and Colored Petri Nets to JSON format
8
+ # Provides a clean, human-readable, and API-friendly format
9
+ class JsonExporter
10
+ attr_reader :net
11
+
12
+ def initialize(net)
13
+ @net = net
14
+ end
15
+
16
+ # Export to JSON string
17
+ # @param pretty [Boolean] Whether to format JSON with indentation
18
+ # @return [String] JSON representation of the net
19
+ def to_json(pretty: true)
20
+ hash = to_hash
21
+ if pretty
22
+ JSON.pretty_generate(hash)
23
+ else
24
+ JSON.generate(hash)
25
+ end
26
+ end
27
+
28
+ # Export to Ruby hash
29
+ # @return [Hash] Hash representation of the net
30
+ def to_hash
31
+ {
32
+ meta: meta_info,
33
+ net: net_structure,
34
+ colors: color_definitions,
35
+ places: places_data,
36
+ transitions: transitions_data,
37
+ arcs: arcs_data,
38
+ marking: current_marking,
39
+ statistics: statistics
40
+ }
41
+ end
42
+
43
+ # Save to JSON file
44
+ def save_json(filename, pretty: true)
45
+ File.write(filename, to_json(pretty: pretty))
46
+ end
47
+
48
+ private
49
+
50
+ def colored_net?
51
+ @net.is_a?(PetriFlow::Colored::ColoredNet)
52
+ end
53
+
54
+ def meta_info
55
+ {
56
+ format: 'PetriFlow JSON Export',
57
+ version: '1.0',
58
+ exported_at: Time.now.utc.iso8601,
59
+ net_type: colored_net? ? 'colored_petri_net' : 'petri_net'
60
+ }
61
+ end
62
+
63
+ def net_structure
64
+ {
65
+ name: @net.name,
66
+ type: colored_net? ? 'ColoredPetriNet' : 'PetriNet',
67
+ place_count: @net.places.size,
68
+ transition_count: @net.transitions.size,
69
+ arc_count: @net.arcs.size
70
+ }
71
+ end
72
+
73
+ def color_definitions
74
+ return [] unless colored_net?
75
+
76
+ @net.colors.map do |color_name, color|
77
+ {
78
+ name: color_name,
79
+ attributes: color.attributes,
80
+ has_validator: !color.validator.nil?
81
+ }
82
+ end
83
+ end
84
+
85
+ def places_data
86
+ @net.places.map do |place_id, place|
87
+ place_hash = {
88
+ id: place_id,
89
+ name: place.name,
90
+ tokens: place.tokens,
91
+ capacity: place.capacity == Float::INFINITY ? 'infinite' : place.capacity
92
+ }
93
+
94
+ # Add colored net place info
95
+ if colored_net? && @net.colored_places[place_id]
96
+ colored_info = @net.colored_places[place_id]
97
+ place_hash[:color] = colored_info[:color]
98
+ place_hash[:colored_tokens] = format_colored_tokens(place_id)
99
+ end
100
+
101
+ place_hash
102
+ end
103
+ end
104
+
105
+ def format_colored_tokens(place_id)
106
+ return [] unless colored_net?
107
+
108
+ tokens = @net.token_pools[place_id] || []
109
+ tokens.map do |token|
110
+ {
111
+ id: token.id,
112
+ color: token.color,
113
+ data: token.data,
114
+ timestamp: token.timestamp
115
+ }
116
+ end
117
+ end
118
+
119
+ def transitions_data
120
+ @net.transitions.map do |trans_id, transition|
121
+ trans_hash = {
122
+ id: trans_id,
123
+ name: transition.name,
124
+ input_arcs: transition.input_arcs.size,
125
+ output_arcs: transition.output_arcs.size,
126
+ enabled: transition.enabled?
127
+ }
128
+
129
+ # Add guard info
130
+ if transition.guard
131
+ trans_hash[:guard] = format_guard(transition.guard)
132
+ end
133
+
134
+ trans_hash
135
+ end
136
+ end
137
+
138
+ def format_guard(guard)
139
+ if guard.respond_to?(:name) && guard.name
140
+ {
141
+ type: 'named_guard',
142
+ name: guard.name,
143
+ description: guard.name
144
+ }
145
+ elsif guard.respond_to?(:condition)
146
+ {
147
+ type: 'guard',
148
+ description: 'Custom guard condition'
149
+ }
150
+ else
151
+ {
152
+ type: 'proc',
153
+ description: 'Lambda/Proc guard'
154
+ }
155
+ end
156
+ end
157
+
158
+ def arcs_data
159
+ @net.arcs.map.with_index do |arc, index|
160
+ arc_hash = {
161
+ id: "arc_#{index}",
162
+ source: {
163
+ id: arc.source.id,
164
+ name: arc.source.name,
165
+ type: arc.source.class.name.split('::').last
166
+ },
167
+ target: {
168
+ id: arc.target.id,
169
+ name: arc.target.name,
170
+ type: arc.target.class.name.split('::').last
171
+ },
172
+ weight: arc.weight,
173
+ direction: arc.input_arc? ? 'place_to_transition' : 'transition_to_place'
174
+ }
175
+
176
+ # Add arc expression info
177
+ if arc.expression
178
+ arc_hash[:expression] = format_expression(arc.expression)
179
+ end
180
+
181
+ arc_hash
182
+ end
183
+ end
184
+
185
+ def format_expression(expression)
186
+ if expression.respond_to?(:name) && expression.name
187
+ {
188
+ type: 'named_expression',
189
+ name: expression.name,
190
+ description: expression.name
191
+ }
192
+ elsif expression.respond_to?(:transformation)
193
+ {
194
+ type: 'arc_expression',
195
+ description: 'Custom arc expression'
196
+ }
197
+ else
198
+ {
199
+ type: 'proc',
200
+ description: 'Lambda/Proc expression'
201
+ }
202
+ end
203
+ end
204
+
205
+ def current_marking
206
+ {
207
+ tokens_by_place: @net.places.transform_values(&:tokens),
208
+ total_tokens: @net.places.values.sum(&:tokens)
209
+ }.tap do |marking|
210
+ if colored_net?
211
+ marking[:colored_marking] = @net.colored_marking
212
+ end
213
+ end
214
+ end
215
+
216
+ def statistics
217
+ @net.stats.merge({
218
+ enabled_transitions_list: @net.enabled_transitions.map(&:id),
219
+ deadlocked: @net.deadlocked?
220
+ })
221
+ end
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rexml/document'
4
+
5
+ module PetriFlow
6
+ module Export
7
+ # Exports Petri nets and Colored Petri Nets to PNML format
8
+ # PNML (Petri Net Markup Language) is the ISO/IEC 15909 standard
9
+ class PnmlExporter
10
+ attr_reader :net
11
+
12
+ def initialize(net)
13
+ @net = net
14
+ end
15
+
16
+ # Export to PNML XML string
17
+ def to_pnml
18
+ doc = REXML::Document.new
19
+ doc << REXML::XMLDecl.new('1.0', 'UTF-8')
20
+
21
+ # Root element
22
+ pnml = doc.add_element('pnml', {
23
+ 'xmlns' => 'http://www.pnml.org/version-2009/grammar/pnml'
24
+ })
25
+
26
+ # Add net element
27
+ net_element = pnml.add_element('net', {
28
+ 'id' => net_id,
29
+ 'type' => net_type_url
30
+ })
31
+
32
+ # Add name
33
+ add_name(net_element, @net.name)
34
+
35
+ # Add page (container for net elements)
36
+ page = net_element.add_element('page', { 'id' => 'page1' })
37
+
38
+ # Add places
39
+ @net.places.each do |place_id, place|
40
+ add_place(page, place)
41
+ end
42
+
43
+ # Add transitions
44
+ @net.transitions.each do |trans_id, transition|
45
+ add_transition(page, transition)
46
+ end
47
+
48
+ # Add arcs
49
+ @net.arcs.each_with_index do |arc, index|
50
+ add_arc(page, arc, index)
51
+ end
52
+
53
+ # Add colored net extensions if applicable
54
+ if colored_net?
55
+ add_color_declarations(net_element)
56
+ end
57
+
58
+ # Format XML nicely
59
+ formatter = REXML::Formatters::Pretty.new(2)
60
+ formatter.compact = true
61
+ output = String.new
62
+ formatter.write(doc, output)
63
+ output
64
+ end
65
+
66
+ # Save to file
67
+ def save_pnml(filename)
68
+ File.write(filename, to_pnml)
69
+ end
70
+
71
+ private
72
+
73
+ def net_id
74
+ @net.name.downcase.gsub(/\s+/, '_')
75
+ end
76
+
77
+ def net_type_url
78
+ if colored_net?
79
+ 'http://www.pnml.org/version-2009/grammar/highlevelnet'
80
+ else
81
+ 'http://www.pnml.org/version-2009/grammar/ptnet'
82
+ end
83
+ end
84
+
85
+ def colored_net?
86
+ @net.is_a?(PetriFlow::Colored::ColoredNet)
87
+ end
88
+
89
+ def add_name(element, name)
90
+ name_elem = element.add_element('name')
91
+ text_elem = name_elem.add_element('text')
92
+ text_elem.text = name
93
+ end
94
+
95
+ def add_place(page, place)
96
+ place_elem = page.add_element('place', { 'id' => place.id.to_s })
97
+ add_name(place_elem, place.name)
98
+
99
+ # Add initial marking
100
+ if place.tokens > 0
101
+ marking_elem = place_elem.add_element('initialMarking')
102
+ text_elem = marking_elem.add_element('text')
103
+ text_elem.text = place.tokens.to_s
104
+ end
105
+
106
+ # Add colored net place extensions
107
+ if colored_net? && @net.colored_places[place.id]
108
+ add_place_type(place_elem, @net.colored_places[place.id])
109
+ end
110
+
111
+ # Add graphics (optional - for visualization)
112
+ add_place_graphics(place_elem)
113
+ end
114
+
115
+ def add_place_type(place_elem, place_info)
116
+ return unless place_info[:color]
117
+
118
+ type_elem = place_elem.add_element('type')
119
+ text_elem = type_elem.add_element('text')
120
+ text_elem.text = place_info[:color].to_s
121
+ end
122
+
123
+ def add_place_graphics(place_elem)
124
+ graphics = place_elem.add_element('graphics')
125
+ position = graphics.add_element('position', { 'x' => '0', 'y' => '0' })
126
+ dimension = graphics.add_element('dimension', { 'x' => '40', 'y' => '40' })
127
+ end
128
+
129
+ def add_transition(page, transition)
130
+ trans_elem = page.add_element('transition', { 'id' => transition.id.to_s })
131
+ add_name(trans_elem, transition.name)
132
+
133
+ # Add guard if present
134
+ if transition.guard
135
+ add_guard(trans_elem, transition.guard)
136
+ end
137
+
138
+ # Add graphics
139
+ add_transition_graphics(trans_elem)
140
+ end
141
+
142
+ def add_guard(trans_elem, guard)
143
+ condition_elem = trans_elem.add_element('condition')
144
+ text_elem = condition_elem.add_element('text')
145
+
146
+ guard_text = if guard.respond_to?(:name) && guard.name
147
+ guard.name
148
+ else
149
+ '[guard]'
150
+ end
151
+ text_elem.text = guard_text
152
+ end
153
+
154
+ def add_transition_graphics(trans_elem)
155
+ graphics = trans_elem.add_element('graphics')
156
+ position = graphics.add_element('position', { 'x' => '0', 'y' => '0' })
157
+ dimension = graphics.add_element('dimension', { 'x' => '40', 'y' => '25' })
158
+ end
159
+
160
+ def add_arc(page, arc, index)
161
+ arc_id = "arc_#{index}"
162
+ source_id = arc.source.id.to_s
163
+ target_id = arc.target.id.to_s
164
+
165
+ arc_elem = page.add_element('arc', {
166
+ 'id' => arc_id,
167
+ 'source' => source_id,
168
+ 'target' => target_id
169
+ })
170
+
171
+ # Add inscription (arc weight)
172
+ if arc.weight != 1
173
+ inscription_elem = arc_elem.add_element('inscription')
174
+ text_elem = inscription_elem.add_element('text')
175
+ text_elem.text = arc.weight.to_s
176
+ end
177
+
178
+ # Add arc expression if present
179
+ if arc.expression
180
+ add_arc_expression(arc_elem, arc.expression)
181
+ end
182
+ end
183
+
184
+ def add_arc_expression(arc_elem, expression)
185
+ inscription_elem = arc_elem.add_element('inscription')
186
+ text_elem = inscription_elem.add_element('text')
187
+
188
+ expr_text = if expression.respond_to?(:name) && expression.name
189
+ expression.name
190
+ else
191
+ '[expression]'
192
+ end
193
+ text_elem.text = expr_text
194
+ end
195
+
196
+ def add_color_declarations(net_element)
197
+ return unless colored_net? && @net.colors.any?
198
+
199
+ declaration_elem = net_element.add_element('declaration')
200
+ structure_elem = declaration_elem.add_element('structure')
201
+
202
+ @net.colors.each do |color_name, color|
203
+ add_color_declaration(structure_elem, color_name, color)
204
+ end
205
+ end
206
+
207
+ def add_color_declaration(structure_elem, color_name, color)
208
+ declaration = structure_elem.add_element('declaration')
209
+
210
+ name_elem = declaration.add_element('name')
211
+ text_elem = name_elem.add_element('text')
212
+ text_elem.text = color_name.to_s
213
+
214
+ # Add color attributes as structure
215
+ if color.attributes.any?
216
+ sort_elem = declaration.add_element('sort')
217
+ struct_elem = sort_elem.add_element('struct')
218
+
219
+ color.attributes.each do |attr_name, attr_type|
220
+ field_elem = struct_elem.add_element('field', {
221
+ 'name' => attr_name.to_s,
222
+ 'type' => attr_type.to_s
223
+ })
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module PetriFlow
6
+ module Export
7
+ # Exports Petri nets and Colored Petri Nets to YAML format
8
+ # Provides the most human-readable format, ideal for configuration and documentation
9
+ class YamlExporter
10
+ attr_reader :net
11
+
12
+ def initialize(net)
13
+ @net = net
14
+ end
15
+
16
+ # Export to YAML string
17
+ # @return [String] YAML representation of the net
18
+ def to_yaml
19
+ to_hash.to_yaml
20
+ end
21
+
22
+ # Export to Ruby hash
23
+ # @return [Hash] Hash representation of the net
24
+ def to_hash
25
+ {
26
+ 'meta' => meta_info,
27
+ 'net' => net_structure,
28
+ 'colors' => color_definitions,
29
+ 'places' => places_data,
30
+ 'transitions' => transitions_data,
31
+ 'arcs' => arcs_data,
32
+ 'marking' => current_marking,
33
+ 'statistics' => statistics
34
+ }
35
+ end
36
+
37
+ # Save to YAML file
38
+ def save_yaml(filename)
39
+ File.write(filename, to_yaml)
40
+ end
41
+
42
+ private
43
+
44
+ def colored_net?
45
+ @net.is_a?(PetriFlow::Colored::ColoredNet)
46
+ end
47
+
48
+ def meta_info
49
+ {
50
+ 'format' => 'PetriFlow YAML Export',
51
+ 'version' => '1.0',
52
+ 'exported_at' => Time.now.utc.iso8601,
53
+ 'net_type' => colored_net? ? 'colored_petri_net' : 'petri_net'
54
+ }
55
+ end
56
+
57
+ def net_structure
58
+ {
59
+ 'name' => @net.name,
60
+ 'type' => colored_net? ? 'ColoredPetriNet' : 'PetriNet',
61
+ 'place_count' => @net.places.size,
62
+ 'transition_count' => @net.transitions.size,
63
+ 'arc_count' => @net.arcs.size
64
+ }
65
+ end
66
+
67
+ def color_definitions
68
+ return [] unless colored_net?
69
+
70
+ @net.colors.map do |color_name, color|
71
+ {
72
+ 'name' => color_name.to_s,
73
+ 'attributes' => format_attributes(color.attributes),
74
+ 'has_validator' => !color.validator.nil?
75
+ }
76
+ end
77
+ end
78
+
79
+ def format_attributes(attributes)
80
+ attributes.transform_keys(&:to_s).transform_values do |v|
81
+ v.is_a?(Symbol) ? v.to_s : v
82
+ end
83
+ end
84
+
85
+ def places_data
86
+ @net.places.map do |place_id, place|
87
+ place_hash = {
88
+ 'id' => place_id.to_s,
89
+ 'name' => place.name,
90
+ 'tokens' => place.tokens,
91
+ 'capacity' => place.capacity == Float::INFINITY ? 'infinite' : place.capacity
92
+ }
93
+
94
+ # Add colored net place info
95
+ if colored_net? && @net.colored_places[place_id]
96
+ colored_info = @net.colored_places[place_id]
97
+ place_hash['color'] = colored_info[:color].to_s if colored_info[:color]
98
+
99
+ colored_tokens = format_colored_tokens(place_id)
100
+ place_hash['colored_tokens'] = colored_tokens if colored_tokens.any?
101
+ end
102
+
103
+ place_hash
104
+ end
105
+ end
106
+
107
+ def format_colored_tokens(place_id)
108
+ return [] unless colored_net?
109
+
110
+ tokens = @net.token_pools[place_id] || []
111
+ tokens.map do |token|
112
+ token_hash = {
113
+ 'id' => token.id,
114
+ 'color' => token.color.to_s
115
+ }
116
+
117
+ token_hash['data'] = stringify_hash(token.data) if token.data.any?
118
+ token_hash['timestamp'] = token.timestamp if token.timestamp
119
+
120
+ token_hash
121
+ end
122
+ end
123
+
124
+ def stringify_hash(hash)
125
+ hash.transform_keys(&:to_s).transform_values do |v|
126
+ case v
127
+ when Symbol
128
+ v.to_s
129
+ when Hash
130
+ stringify_hash(v)
131
+ when Array
132
+ v.map { |item| item.is_a?(Hash) ? stringify_hash(item) : item }
133
+ else
134
+ v
135
+ end
136
+ end
137
+ end
138
+
139
+ def transitions_data
140
+ @net.transitions.map do |trans_id, transition|
141
+ trans_hash = {
142
+ 'id' => trans_id.to_s,
143
+ 'name' => transition.name,
144
+ 'input_arcs' => transition.input_arcs.size,
145
+ 'output_arcs' => transition.output_arcs.size,
146
+ 'enabled' => transition.enabled?
147
+ }
148
+
149
+ # Add guard info
150
+ if transition.guard
151
+ trans_hash['guard'] = format_guard(transition.guard)
152
+ end
153
+
154
+ trans_hash
155
+ end
156
+ end
157
+
158
+ def format_guard(guard)
159
+ if guard.respond_to?(:name) && guard.name
160
+ {
161
+ 'type' => 'named_guard',
162
+ 'name' => guard.name,
163
+ 'description' => guard.name
164
+ }
165
+ elsif guard.respond_to?(:condition)
166
+ {
167
+ 'type' => 'guard',
168
+ 'description' => 'Custom guard condition'
169
+ }
170
+ else
171
+ {
172
+ 'type' => 'proc',
173
+ 'description' => 'Lambda/Proc guard'
174
+ }
175
+ end
176
+ end
177
+
178
+ def arcs_data
179
+ @net.arcs.map.with_index do |arc, index|
180
+ arc_hash = {
181
+ 'id' => "arc_#{index}",
182
+ 'source' => {
183
+ 'id' => arc.source.id.to_s,
184
+ 'name' => arc.source.name,
185
+ 'type' => arc.source.class.name.split('::').last
186
+ },
187
+ 'target' => {
188
+ 'id' => arc.target.id.to_s,
189
+ 'name' => arc.target.name,
190
+ 'type' => arc.target.class.name.split('::').last
191
+ },
192
+ 'weight' => arc.weight,
193
+ 'direction' => arc.input_arc? ? 'place_to_transition' : 'transition_to_place'
194
+ }
195
+
196
+ # Add arc expression info
197
+ if arc.expression
198
+ arc_hash['expression'] = format_expression(arc.expression)
199
+ end
200
+
201
+ arc_hash
202
+ end
203
+ end
204
+
205
+ def format_expression(expression)
206
+ if expression.respond_to?(:name) && expression.name
207
+ {
208
+ 'type' => 'named_expression',
209
+ 'name' => expression.name,
210
+ 'description' => expression.name
211
+ }
212
+ elsif expression.respond_to?(:transformation)
213
+ {
214
+ 'type' => 'arc_expression',
215
+ 'description' => 'Custom arc expression'
216
+ }
217
+ else
218
+ {
219
+ 'type' => 'proc',
220
+ 'description' => 'Lambda/Proc expression'
221
+ }
222
+ end
223
+ end
224
+
225
+ def current_marking
226
+ marking_hash = {
227
+ 'tokens_by_place' => stringify_hash(@net.places.transform_values(&:tokens)),
228
+ 'total_tokens' => @net.places.values.sum(&:tokens)
229
+ }
230
+
231
+ if colored_net?
232
+ marking_hash['colored_marking'] = stringify_hash(@net.colored_marking)
233
+ end
234
+
235
+ marking_hash
236
+ end
237
+
238
+ def statistics
239
+ stringify_hash(@net.stats.merge({
240
+ enabled_transitions_list: @net.enabled_transitions.map { |t| t.id.to_s },
241
+ deadlocked: @net.deadlocked?
242
+ }))
243
+ end
244
+ end
245
+ end
246
+ end