gv_fsm 0.2.4 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/bin/gv_fsm +31 -19
  3. data/lib/gv_fsm.rb +46 -11
  4. data/lib/templates.rb +33 -10
  5. data/lib/version.rb +1 -1
  6. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 687c6994a931d95a7cc4f190dbe26dd1c392e960f9ef1c41f544f53408f1ad70
4
- data.tar.gz: 880237c706a106208808ae00a9c42be507dfd5dd5c43d09671f32e419d715b4d
3
+ metadata.gz: 19016ba84adc54b46d0c52e6dffcd4024f3626a32ada32830cdf3a8e094a3a35
4
+ data.tar.gz: 32f84664395d90c58da54bf5b8add1a8a06bd0f0901ffd1947b9f81e713df26e
5
5
  SHA512:
6
- metadata.gz: b690e25215ce7e994c96e5231759315fcf8804f13e6357c7b36f5a6cd6a925e1f7f6c761cbe494c76d592d967e981cb91b180de094f758919bd676e616376b74
7
- data.tar.gz: 641de18537305498dafa1afc161f2d3bcb0d05a2c30835289eb4afbb1eb8cd965950f94a19691951d50f038daa5520de766bce5dade4d6db477018341d522022
6
+ metadata.gz: 5896400ccad2e6a1091f20aa32d3739bbe45c3b87fe5ed4501a4254235e3acf6a0d09561789d53686939fafa5d298de84567475a6bcca23b8c0b5375242f4f19
7
+ data.tar.gz: fdc81c9081b5c83e4842ce864fb4b5babcdfde5cc03936c3cad0eb64f8a078af517b3da50fea7bc1f1a8027c36b2678a16d3d537118cf1280e7b994b405a24a6
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("-h", "--header-only", "Only generate header file") do
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-syslog", "Omit syslog calls in stub functions") do
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
- unless File.extname(ARGV[0]) == ".dot"
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}.\nGenerating C stub for states: #{sm.states_list.join(", ")}."
74
- if sm.ino then
75
- sm.generate_ino
76
- puts "Generated .ino file #{sm.cname}.ino"
77
- else
78
- if options[:header] then
79
- sm.generate_h
80
- puts "Generated header #{sm.cname}.h"
81
- end
82
- if options[:source] then
83
- sm.generate_c
84
- puts "Generated source #{sm.cname}.c"
85
- end
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
 
@@ -2,13 +2,14 @@
2
2
 
3
3
  require 'ruby-graphviz'
4
4
  require 'erb'
5
+ require 'matrix'
5
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,7 +33,23 @@ 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
55
  if n[:label].source.empty? or
@@ -38,9 +58,12 @@ module GV_FSM
38
58
  else
39
59
  label = n[:label].source
40
60
  end
61
+ @nodemap[id] = i
62
+ i += 1
41
63
  @states << {id: id, function: @prefix+label}
42
64
  end
43
65
  g.each_edge do |e|
66
+ @matrix[@nodemap[e.node_one], @nodemap[e.node_two]] += 1
44
67
  from = e.node_one
45
68
  to = e.node_two
46
69
  unless e[:label] then
@@ -58,6 +81,11 @@ module GV_FSM
58
81
  @transitions << {from: from, to: to, function: label ? @prefix+label : nil}
59
82
  end
60
83
  end
84
+ unless graph then
85
+ @error = "Parsing error"
86
+ return nil
87
+ end
88
+ return graph
61
89
  end
62
90
 
63
91
  def state_functions_list
@@ -108,27 +136,34 @@ module GV_FSM
108
136
  end
109
137
 
110
138
  def generate_c(filename = @cname)
111
- File.open("#{filename}.c", "w") do |f|
139
+ ext = @ino ? "cpp" : "c"
140
+ fname = "#{filename}.#{ext}"
141
+ File.open(fname, "w") do |f|
112
142
  f.puts ERB.new(HEADER, 0, "<>").result(binding)
113
143
  f.puts ERB.new(CC, 0, "<>").result(binding)
114
144
  end
145
+ return fname
115
146
  end
116
147
 
117
148
  def generate_h(filename = @cname)
118
- File.open("#{filename}.h", "w") do |f|
149
+ fname = "#{filename}.h"
150
+ File.open(fname, "w") do |f|
119
151
  f.puts ERB.new(HEADER, 0, "<>").result(binding)
120
152
  f.puts ERB.new(HH, 0, "<>").result(binding)
121
153
  end
154
+ return fname
122
155
  end
123
156
 
124
- def generate_ino(filename=@cname)
125
- @syslog = false
126
- File.open("#{filename}.ino", "w") do |f|
127
- f.puts ERB.new(HEADER, 0, "<>").result(binding)
128
- f.puts ERB.new(HH, 0, "<>").result(binding)
129
- f.puts ERB.new(CC, 0, "<>").result(binding)
130
- end
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
131
165
  end
166
+
132
167
  end
133
168
 
134
169
  end
@@ -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
- #include "<%= @cname %>.h"
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 @syslog then %>
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 @syslog then %>
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 @syslog then %>
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
- <% end %>
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
- } while (cur_state != <%= @prefix.upcase %>STATE_STOP);
253
- <% if !@ino then %>
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
@@ -1,3 +1,3 @@
1
1
  module GV_FSM
2
- VERSION = "0.2.4"
2
+ VERSION = "0.2.9"
3
3
  end
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
4
+ version: 0.2.9
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-09-09 00:00:00.000000000 Z
11
+ date: 2020-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-graphviz
@@ -48,7 +48,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
48
48
  requirements:
49
49
  - - ">="
50
50
  - !ruby/object:Gem::Version
51
- version: '0'
51
+ version: '2.7'
52
52
  required_rubygems_version: !ruby/object:Gem::Requirement
53
53
  requirements:
54
54
  - - ">="