gv_fsm 0.2.3 → 0.2.8
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 +4 -4
- data/bin/gv_fsm +31 -19
- data/lib/gv_fsm.rb +53 -14
- data/lib/templates.rb +33 -10
- data/lib/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b5c409a59baa14a81f425617228e69a99e08c6914aba00156516bb6d58f4b01
|
4
|
+
data.tar.gz: 88f01660fdc03e5b7e439e8eb5c471990de71f0ab32cd5fd6d6a6e93879294ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68c7c63845e630d6b17e5d9ec7a7dac42bbd4045c9e6bd41f25bfcd8d1edc12e3b6dea3518179fe1bea72400a865e4a1fd0112980ea4625b3df3e2185cb651dd
|
7
|
+
data.tar.gz: 8c47fdce73aafb70871ad87cddf1a5a359021b2e1cd1f619212186d1fda7b552b30858f958ad818ab4b6a491d47e27501bb7ccd50777d1d5686be17d9c9062af
|
data/bin/gv_fsm
CHANGED
@@ -28,7 +28,7 @@ op = OptionParser.new do |parser|
|
|
28
28
|
sm.cname = f
|
29
29
|
end
|
30
30
|
|
31
|
-
parser.on("-
|
31
|
+
parser.on("-e", "--header-only", "Only generate header file") do
|
32
32
|
options[:source] = false
|
33
33
|
end
|
34
34
|
|
@@ -44,7 +44,7 @@ op = OptionParser.new do |parser|
|
|
44
44
|
sm.ino = true
|
45
45
|
end
|
46
46
|
|
47
|
-
parser.on("-l", "--no-
|
47
|
+
parser.on("-l", "--no-log", "Omit log calls in stub functions") do
|
48
48
|
sm.syslog = false
|
49
49
|
end
|
50
50
|
|
@@ -60,28 +60,40 @@ op.parse!
|
|
60
60
|
unless ARGV[0]
|
61
61
|
STDERR.puts "ERROR: I need the path to a Graphviz file!\n\n"
|
62
62
|
STDERR.puts op
|
63
|
-
exit
|
63
|
+
exit 1
|
64
64
|
end
|
65
|
-
|
65
|
+
if !File.extname(ARGV[0]) == ".dot" or !File.exist? ARGV[0] then
|
66
66
|
STDERR.puts "ERROR: #{ARGV[0]} does not look like a Graphviz file!\n\n"
|
67
67
|
STDERR.puts op
|
68
|
-
exit
|
68
|
+
exit 2
|
69
69
|
end
|
70
70
|
|
71
|
-
sm.parse(ARGV[0])
|
71
|
+
unless sm.parse(ARGV[0]) then
|
72
|
+
puts "Error parsing the file #{ARGV[0]}: #{sm.error}"
|
73
|
+
exit 3
|
74
|
+
end
|
72
75
|
|
73
|
-
puts "Parsed #{sm.dotfile}
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
76
|
+
puts "Parsed #{sm.dotfile}"
|
77
|
+
top = sm.topology
|
78
|
+
puts "Graph topology:"
|
79
|
+
puts " Pure source nodes: #{top[:sources].join(', ')}"
|
80
|
+
puts " Pure sink nodes: #{top[:sinks].empty? ? "<none>" : top[:sinks].join(', ')}"
|
81
|
+
|
82
|
+
if !(top[:sources].count == 1 and top[:sinks].count <= 1) then
|
83
|
+
puts "Topology error: there must be exactly one source and zero or one sink"
|
84
|
+
exit 4
|
85
|
+
end
|
86
|
+
|
87
|
+
puts "Generating C functions for states: #{sm.states_list.join(", ")}."
|
88
|
+
puts " for transition: #{sm.transition_functions_list.join(", ")}."
|
89
|
+
|
90
|
+
|
91
|
+
if options[:header] then
|
92
|
+
name = sm.generate_h
|
93
|
+
puts "Generated header #{name}"
|
94
|
+
end
|
95
|
+
if options[:source] then
|
96
|
+
name = sm.generate_c
|
97
|
+
puts "Generated source #{name}"
|
86
98
|
end
|
87
99
|
|
data/lib/gv_fsm.rb
CHANGED
@@ -2,13 +2,14 @@
|
|
2
2
|
|
3
3
|
require 'ruby-graphviz'
|
4
4
|
require 'erb'
|
5
|
-
require '
|
5
|
+
require 'matrix'
|
6
|
+
|
6
7
|
require File.expand_path('../templates.rb', __FILE__)
|
7
8
|
require File.expand_path("../version.rb", __FILE__)
|
8
9
|
|
9
10
|
module GV_FSM
|
10
11
|
class FSM
|
11
|
-
attr_reader :states, :transitions, :dotfile, :prefix
|
12
|
+
attr_reader :states, :transitions, :dotfile, :prefix, :error
|
12
13
|
attr_accessor :project_name, :description, :cname, :syslog, :ino
|
13
14
|
include GV_FSM::Templates
|
14
15
|
|
@@ -16,6 +17,9 @@ module GV_FSM
|
|
16
17
|
@prefix = ""
|
17
18
|
@syslog = true
|
18
19
|
@ino = false
|
20
|
+
@error = nil
|
21
|
+
@matrix = nil
|
22
|
+
@nodemap = {}
|
19
23
|
parse(filename) if filename
|
20
24
|
end
|
21
25
|
|
@@ -29,14 +33,37 @@ module GV_FSM
|
|
29
33
|
@dotfile = filename
|
30
34
|
@states = []
|
31
35
|
@transitions = []
|
32
|
-
GraphViz.parse(filename) do |g|
|
36
|
+
graph = GraphViz.parse(filename) do |g|
|
37
|
+
if g.graph_count > 1 then
|
38
|
+
@error = "Only one graph in the dot file is permitted"
|
39
|
+
return nil
|
40
|
+
end
|
41
|
+
unless g.type == "digraph" then
|
42
|
+
@error = "Graph is not directed"
|
43
|
+
return nil
|
44
|
+
end
|
45
|
+
n = g.node_count
|
46
|
+
if n == 0 then
|
47
|
+
@error = "Graph is empty"
|
48
|
+
return nil
|
49
|
+
end
|
50
|
+
@matrix = Matrix.zero(n, n)
|
51
|
+
@description = g.name
|
52
|
+
i = 0
|
33
53
|
g.each_node do |id|
|
34
54
|
n = g.get_node(id)
|
35
|
-
|
36
|
-
|
55
|
+
if n[:label].source.empty? or
|
56
|
+
(n[:label].source == id and !n[:label].source.match(/^do_/)) then
|
57
|
+
label = "do_#{id}"
|
58
|
+
else
|
59
|
+
label = n[:label].source
|
60
|
+
end
|
61
|
+
@nodemap[id] = i
|
62
|
+
i += 1
|
37
63
|
@states << {id: id, function: @prefix+label}
|
38
64
|
end
|
39
65
|
g.each_edge do |e|
|
66
|
+
@matrix[@nodemap[e.node_one], @nodemap[e.node_two]] += 1
|
40
67
|
from = e.node_one
|
41
68
|
to = e.node_two
|
42
69
|
unless e[:label] then
|
@@ -54,6 +81,11 @@ module GV_FSM
|
|
54
81
|
@transitions << {from: from, to: to, function: label ? @prefix+label : nil}
|
55
82
|
end
|
56
83
|
end
|
84
|
+
unless graph then
|
85
|
+
@error = "Parsing error"
|
86
|
+
return nil
|
87
|
+
end
|
88
|
+
return graph
|
57
89
|
end
|
58
90
|
|
59
91
|
def state_functions_list
|
@@ -104,27 +136,34 @@ module GV_FSM
|
|
104
136
|
end
|
105
137
|
|
106
138
|
def generate_c(filename = @cname)
|
107
|
-
|
139
|
+
ext = @ino ? "cpp" : "c"
|
140
|
+
fname = "#{filename}.#{ext}"
|
141
|
+
File.open(fname, "w") do |f|
|
108
142
|
f.puts ERB.new(HEADER, 0, "<>").result(binding)
|
109
143
|
f.puts ERB.new(CC, 0, "<>").result(binding)
|
110
144
|
end
|
145
|
+
return fname
|
111
146
|
end
|
112
147
|
|
113
148
|
def generate_h(filename = @cname)
|
114
|
-
|
149
|
+
fname = "#{filename}.h"
|
150
|
+
File.open(fname, "w") do |f|
|
115
151
|
f.puts ERB.new(HEADER, 0, "<>").result(binding)
|
116
152
|
f.puts ERB.new(HH, 0, "<>").result(binding)
|
117
153
|
end
|
154
|
+
return fname
|
118
155
|
end
|
119
156
|
|
120
|
-
def
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
157
|
+
def topology
|
158
|
+
res = {matrix: @matrix}
|
159
|
+
# rows have the number of froms, columns the number of tos
|
160
|
+
res[:froms] = @matrix.row_vectors.map {|v| v.sum }
|
161
|
+
res[:tos] = @matrix.column_vectors.map {|v| v.sum }
|
162
|
+
res[:sinks] = res[:froms].each_index.select {|i| res[:froms][i] == 0}.map {|i| @nodemap.keys[i]}
|
163
|
+
res[:sources] = res[:tos].each_index.select {|i| res[:tos][i] == 0}.map {|i| @nodemap.keys[i]}
|
164
|
+
return res
|
127
165
|
end
|
166
|
+
|
128
167
|
end
|
129
168
|
|
130
169
|
end
|
data/lib/templates.rb
CHANGED
@@ -26,6 +26,8 @@ module GV_FSM
|
|
26
26
|
#ifndef <%= @cname.upcase %>_H
|
27
27
|
#define <%= @cname.upcase %>_H
|
28
28
|
#include <stdlib.h>
|
29
|
+
<% else %>
|
30
|
+
#include <arduino.h>
|
29
31
|
<% end %>
|
30
32
|
|
31
33
|
// State data object
|
@@ -100,10 +102,13 @@ module GV_FSM
|
|
100
102
|
CC =<<~EOC
|
101
103
|
<% if !@ino then %>
|
102
104
|
<% if @syslog then %>
|
105
|
+
<% log = :syslog %>
|
103
106
|
#include <syslog.h>
|
104
107
|
<% end %>
|
105
|
-
|
108
|
+
<% else %>
|
109
|
+
<% if @syslog then log = :ino end %>
|
106
110
|
<% end %>
|
111
|
+
#include "<%= @cname %>.h"
|
107
112
|
|
108
113
|
<% placeholder = "Your Code Here" %>
|
109
114
|
// SEARCH FOR <%= placeholder %> FOR CODE INSERTION POINTS!
|
@@ -159,8 +164,10 @@ module GV_FSM
|
|
159
164
|
<%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data) {
|
160
165
|
<%= @prefix %>state_t next_state = <%= dest[s[:id]].first %>;
|
161
166
|
|
162
|
-
<% if
|
167
|
+
<% if log == :syslog then %>
|
163
168
|
syslog(LOG_INFO, "[FSM] In state <%= s[:id] %>");
|
169
|
+
<% elsif log == :ino then %>
|
170
|
+
Serial.println("[FSM] In state <%= s[:id] %>");
|
164
171
|
<% end %>
|
165
172
|
/* <%= placeholder %> */
|
166
173
|
|
@@ -170,8 +177,12 @@ module GV_FSM
|
|
170
177
|
<% end %>
|
171
178
|
break;
|
172
179
|
default:
|
173
|
-
<% if
|
180
|
+
<% if log == :syslog then %>
|
174
181
|
syslog(LOG_WARNING, "[FSM] Cannot pass from <%= s[:id] %> to %s, remaining in this state", state_names[next_state]);
|
182
|
+
<% elsif log == :ino then %>
|
183
|
+
Serial.print("[FSM] Cannot pass from <%= s[:id] %> to ");
|
184
|
+
Serial.print(state_names[next_state]);
|
185
|
+
Serial.println(", remaining in this state");
|
175
186
|
<% end %>
|
176
187
|
next_state = <%= @prefix.upcase %>NO_CHANGE;
|
177
188
|
}
|
@@ -202,8 +213,10 @@ module GV_FSM
|
|
202
213
|
// <%= i+1 %>. from <%= e[:from] %> to <%= e[:to] %>
|
203
214
|
<% end %>
|
204
215
|
void <%= t %>(<%= @prefix %>state_data_t *data) {
|
205
|
-
<% if
|
216
|
+
<% if log == :syslog then %>
|
206
217
|
syslog(LOG_INFO, "[FSM] State transition <%= t %>");
|
218
|
+
<% elsif log == :ino then %>
|
219
|
+
Serial.println("[FSM] State transition <%= t %>");
|
207
220
|
<% end %>
|
208
221
|
/* <%= placeholder %> */
|
209
222
|
}
|
@@ -226,6 +239,7 @@ module GV_FSM
|
|
226
239
|
|
227
240
|
<%= @prefix %>state_t <%= @prefix %>run_state(<%= @prefix %>state_t cur_state, <%= @prefix %>state_data_t *data) {
|
228
241
|
<%= @prefix %>state_t new_state = <%= @prefix %>state_table[cur_state](data);
|
242
|
+
if (new_state == <%= @prefix.upcase %>NO_CHANGE) new_state = cur_state;
|
229
243
|
<% if transition_functions_list.count > 0 then %>
|
230
244
|
transition_func_t *transition = <%= @prefix %>transition_table[cur_state][new_state];
|
231
245
|
if (transition)
|
@@ -236,12 +250,19 @@ module GV_FSM
|
|
236
250
|
|
237
251
|
<% if @ino then %>
|
238
252
|
/* Example usage:
|
253
|
+
<%= @prefix %>state_data_t data = {count: 1};
|
254
|
+
|
255
|
+
void loop() {
|
256
|
+
static <%= @prefix %>state_t cur_state = <%= @prefix.upcase %>STATE_INIT;
|
257
|
+
cur_state = <%= @prefix %>run_state(cur_state, &data);
|
258
|
+
}
|
259
|
+
*/
|
239
260
|
<% else %>
|
261
|
+
<% nsinks = topology[:sinks].count %>
|
240
262
|
#ifdef TEST_MAIN
|
241
263
|
#include <unistd.h>
|
242
264
|
int main() {
|
243
|
-
|
244
|
-
<%= @prefix %>state_t cur_state = <%= @prefix.upcase %>STATE_INIT;
|
265
|
+
<%= @prefix %>state_t cur_state = <%= @prefix.upcase %>STATE_<%= @states.first[:id].upcase %>;
|
245
266
|
<% if @syslog then %>
|
246
267
|
openlog("SM", LOG_PID | LOG_PERROR, LOG_USER);
|
247
268
|
syslog(LOG_INFO, "Starting SM");
|
@@ -249,13 +270,15 @@ module GV_FSM
|
|
249
270
|
do {
|
250
271
|
cur_state = <%= @prefix %>run_state(cur_state, NULL);
|
251
272
|
sleep(1);
|
252
|
-
|
253
|
-
|
273
|
+
<% if nsinks == 1 %>
|
274
|
+
} while (cur_state != <%= @prefix.upcase %>STATE_<%= topology[:sinks][0].upcase %>);
|
275
|
+
<%= @prefix %>run_state(cur_state, NULL);
|
276
|
+
<% else %>
|
277
|
+
} while (1);
|
278
|
+
<% end %>
|
254
279
|
return 0;
|
255
280
|
}
|
256
281
|
#endif
|
257
|
-
<% else %>
|
258
|
-
*/
|
259
282
|
<% end %>
|
260
283
|
EOC
|
261
284
|
end
|
data/lib/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gv_fsm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paolo Bosetti
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-graphviz
|