gv_fsm 0.0.2 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1d793e3f46c8098f7fd5a5e9cdac035fc19ab6fb744886775dbc1e7a7757398c
4
- data.tar.gz: bc16702d154fddd02a2c774b0f651ada1ece6e1e76cc9845e64dfbbb3811aba9
3
+ metadata.gz: 2896e7f9617c0ed1a9917e78cd947d1da5ceef2d8554727a4db268a7f70e6229
4
+ data.tar.gz: 63d897002c102d5291ecbfd7b4c854f8c9fc029e01155ba573d13e2043621afb
5
5
  SHA512:
6
- metadata.gz: d06e75af4f4d1c3d1ebf2d80293d60cceae9a75859ddeaef11e44b02bab4829b248071805174d15f5818767f3ee04164ac573174cb39a76db55d196cf649ba11
7
- data.tar.gz: bea055285d64501305796e2fb0d49d9a5e915f52facc45224c7eace99d801ebd5f4cc7b4a39e88587b262f779c250dac651beb7235826d2fb445aaca03cf2b3a
6
+ metadata.gz: ae5be7ee072d06983c139320227c6b234d703b2556071df085cb3f6a3365d3d1c6a8cf38f9f6daac0b866ed5b77c4726933eee69eda7aa8d9ffe17b9f2c2ca0b
7
+ data.tar.gz: bd1129ccf730d930b42a8ba53f83eb2adeb6d66368ebd9aab6466c69e2b951d6b461d0e04579254153d2c753c390f16f023806040615e6ac52d53f3b2f60c71a
data/bin/gv_fsm CHANGED
@@ -1,11 +1,60 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "gv_fsm"
4
+ require 'optparse'
4
5
 
5
- SM = GV_FSM::FSM.new(ARGV[0])
6
- SM.project_name = "test project"
7
- SM.description = "FSM designed to test the generator"
6
+ sm = GV_FSM::FSM.new
7
+
8
+ options = {header: true, source: true}
9
+ OptionParser.new do |parser|
10
+ parser.banner =<<~EOB
11
+ Graphviz to Finite State Machine generator
12
+ Version: #{GV_FSM::VERSION}
13
+ See also https://github.com/pbosetti/gv_fsm
14
+
15
+ Usage: gv_fsm [options] scheme.dot
16
+ EOB
17
+
18
+ parser.on("-p", "--project PROJECT_NAME",
19
+ "Set the project name to PROJECT_NAME") do |pn|
20
+ sm.project_name = pn
21
+ end
22
+
23
+ parser.on("-d", "--description DESCRIPTION", "Use DESCRITION string in header") do |desc|
24
+ sm.description = desc
25
+ end
26
+
27
+ parser.on("-o", "--output_file NAME", "Use NAME for generated .c and .h files") do |f|
28
+ sm.cname = f
29
+ end
30
+
31
+ parser.on("-h", "--header-only", "Only generate header file") do
32
+ options[:source] = false
33
+ end
34
+
35
+ parser.on("-s", "--source-only", "Only generate source file") do
36
+ options[:header] = false
37
+ end
38
+
39
+ parser.on("-x", "--prefix PREFIX", "Prepend PREFIX to names of generated functions and objects") do |p|
40
+ sm.prefix = p
41
+ end
42
+
43
+ end.parse!
44
+
45
+ raise ArgumentError, "I need the path to a Graphviz file!" unless ARGV[0]
46
+ raise ArgumentError, "#{ARGV[0]} does not look like a Graphviz file!" unless File.extname(ARGV[0]) == ".dot"
47
+
48
+ sm.parse(ARGV[0])
49
+
50
+ puts "Parsed #{sm.dotfile}.\nGenerating C stub for states: #{sm.states_list.join(", ")}."
51
+ if options[:header] then
52
+ sm.generate_h
53
+ puts "Generated header #{sm.cname}.h"
54
+ end
55
+ if options[:source] then
56
+ sm.generate_c
57
+ puts "Generated source #{sm.cname}.c"
58
+ end
8
59
 
9
- SM.generate_h
10
- SM.generate_c
11
60
 
@@ -8,17 +8,22 @@ require File.expand_path("../version.rb", __FILE__)
8
8
 
9
9
  module GV_FSM
10
10
  class FSM
11
- attr_reader :states, :transitions, :dotfile
11
+ attr_reader :states, :transitions, :dotfile, :prefix
12
12
  attr_accessor :project_name, :description, :cname
13
13
  include GV_FSM::Templates
14
14
 
15
- def initialize(filename)
16
- raise ArgumentError, "File must be in .dot format" unless File.extname(filename) == ".dot"
17
- @cname = File.basename(filename, ".dot")
18
- parse(filename)
15
+ def initialize(filename = nil)
16
+ @prefix = ""
17
+ parse(filename) if filename
18
+ end
19
+
20
+ def prefix=(v)
21
+ @prefix = v + '_'
19
22
  end
20
23
 
21
24
  def parse(filename)
25
+ raise ArgumentError, "File must be in .dot format" unless File.extname(filename) == ".dot"
26
+ @cname = File.basename(filename, ".dot") unless (@cname and ! @cname.empty?)
22
27
  @dotfile = filename
23
28
  @states = []
24
29
  @transitions = []
@@ -26,11 +31,12 @@ module GV_FSM
26
31
  g.each_node do |id|
27
32
  n = g.get_node(id)
28
33
  label = n[:label].source.empty? ? "do_#{id}" : n[:label].source
29
- @states << {id: id, function: label}
34
+ @states << {id: id, function: @prefix+label}
30
35
  end
31
36
  g.each_edge do |e|
32
37
  from = e.node_one
33
38
  to = e.node_two
39
+ next unless e[:label]
34
40
  case e[:label].source
35
41
  when ""
36
42
  label = nil
@@ -39,7 +45,7 @@ module GV_FSM
39
45
  else
40
46
  label = e[:label].source
41
47
  end
42
- @transitions << {from: from, to: to, function: label}
48
+ @transitions << {from: from, to: to, function: label ? @prefix+label : nil}
43
49
  end
44
50
  end
45
51
  end
@@ -82,6 +88,15 @@ module GV_FSM
82
88
  return dest
83
89
  end
84
90
 
91
+ def transitions_paths
92
+ path = {}
93
+ @transitions.each do |t|
94
+ path[t[:function]] = [] unless path[t[:function]]
95
+ path[t[:function]] << {from: t[:from], to: t[:to]}
96
+ end
97
+ return path
98
+ end
99
+
85
100
  def generate_c(filename = @cname)
86
101
  File.open("#{filename}.c", "w") do |f|
87
102
  f.puts ERB.new(HEADER, 0, "<>").result(binding)
@@ -2,90 +2,124 @@
2
2
  module GV_FSM
3
3
  module Templates
4
4
  HEADER =<<~EOHEADER
5
- // Finite State Machine
6
- // Project: <%= self.project_name or self.dotfile %>
7
- // Description: <%= self.description or "<none given>" %>
8
- //
9
- // Generated by gv_fsm ruby gem, see https://rubygems.org/gems/gv_fsm
10
- // gv_fsm version <%= GV_FSM::VERSION %>
11
- // Generation date: <%= Time.now %>
12
- // Generated from: <%= self.dotfile %>
13
- // The finite state machine has:
14
- // <%= self.states.count %> states
15
- // <%= self.transitions.count %> transitions
5
+ /******************************************************************************
6
+ Finite State Machine
7
+ Project: <%= @project_name or @dotfile %>
8
+ Description: <%= @description or "<none given>" %>
9
+
10
+ Generated by gv_fsm ruby gem, see https://rubygems.org/gems/gv_fsm
11
+ gv_fsm version <%= GV_FSM::VERSION %>
12
+ Generation date: <%= Time.now %>
13
+ Generated from: <%= @dotfile %>
14
+ The finite state machine has:
15
+ <%= @states.count %> states
16
+ <%= @transitions.count %> transitions
17
+ <%= transition_functions_list.select {|e| e != 'NULL'}.count %> transition functions
18
+ <% if @prefix != '' %>
19
+ Functions and types have been generated with prefix "<%= @prefix %>"
20
+ <% end %>
21
+ ******************************************************************************/
16
22
 
17
23
  EOHEADER
18
24
 
19
25
  HH =<<~EOH
20
- #ifndef <%= self.cname.upcase %>_H
21
- #define <%= self.cname.upcase %>_H
26
+ #ifndef <%= @cname.upcase %>_H
27
+ #define <%= @cname.upcase %>_H
22
28
  #include <stdlib.h>
23
29
 
30
+ // State data object
31
+ // By default set to void; override this typedef or load the proper
32
+ // header if you need
33
+ typedef void <%= @prefix %>state_data_t;
34
+
35
+ // NOTHING SHALL BE CHANGED AFTER THIS LINE!
36
+
24
37
  // List of states
25
38
  typedef enum {
26
- <% self.states.each_with_index do |s, i| %>
27
- STATE_<%= s[:id].upcase %><%= i == 0 ? " = 0" : "" %>,
39
+ <% @states.each_with_index do |s, i| %>
40
+ <%= @prefix.upcase %>STATE_<%= s[:id].upcase %><%= i == 0 ? " = 0" : "" %>,
28
41
  <% end %>
29
- NUM_STATES,
30
- NO_CHANGE
31
- } state_t;
42
+ <%= @prefix.upcase %>NUM_STATES,
43
+ <%= @prefix.upcase %>NO_CHANGE
44
+ } <%= @prefix %>state_t;
32
45
 
33
- const char *state_names[] = {<%= self.states_list.map {|sn| '"'+sn+'"'}.join(", ") %>};
46
+ const char *state_names[] = {<%= states_list.map {|sn| '"'+sn+'"'}.join(", ") %>};
34
47
 
35
48
  // State function and state transition prototypes
36
- typedef state_t state_func_t(void *data);
37
- typedef void transition_func_t(void *data);
38
-
39
- // state functions
40
- <% self.states.each do |s| %>
41
- state_t <%= s[:function] %>(void *data);
42
- <% end %>
49
+ typedef <%= @prefix %>state_t state_func_t(<%= @prefix %>state_data_t *data);
50
+ typedef void transition_func_t(<%= @prefix %>state_data_t *data);
43
51
 
44
- // transition functions
45
- <% self.transition_functions_list.each do |t| %>
46
- <% next if t == "NULL" %>
47
- void <%= t %>(void *data);
52
+ // State functions
53
+ <% @states.each do |s| %>
54
+ <%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data);
48
55
  <% end %>
49
56
 
50
57
  // List of state functions
51
- state_func_t *const state_table[NUM_STATES] = {
52
- <%= self.state_functions_list.join(",\n ")%>
58
+ state_func_t *const <%= @prefix %>state_table[<%= @prefix.upcase %>NUM_STATES] = {
59
+ <% @states.each do |s| %>
60
+ <%= s[:function] %>, // in state <%= s[:id] %>
61
+ <% end %>
53
62
  };
63
+
64
+ <% if transition_functions_list.count > 0 then %>
65
+ // Transition functions
66
+ <% transition_functions_list.each do |t| %>
67
+ <% next if t == "NULL" %>
68
+ void <%= t %>(<%= @prefix %>state_data_t *data);
69
+ <% end %>
54
70
 
55
71
  // Table of transition functions
56
- transition_func_t *const transition_table[NUM_STATES][NUM_STATES] = {
57
- <% sl = self.states_list %>
58
- <% fw = self.transition_functions_list.max {|a, b| a.length <=> b.length}.length %>
59
- <% sw = self.states_list.max {|a, b| a.length <=> b.length}.length %>
72
+ transition_func_t *const <%= @prefix %>transition_table[<%= @prefix.upcase %>NUM_STATES][<%= @prefix.upcase %>NUM_STATES] = {
73
+ <% sl = states_list %>
74
+ <% fw = transition_functions_list.max {|a, b| a.length <=> b.length}.length %>
75
+ <% sw = states_list.max {|a, b| a.length <=> b.length}.length %>
60
76
  /* <%= "states:".ljust(sw) %> <%= sl.map {|e| e.ljust(fw) }.join(", ") %> */
61
- <% self.transitions_map.each_with_index do |l, i| %>
77
+ <% transitions_map.each_with_index do |l, i| %>
62
78
  /* <%= sl[i].ljust(sw) %> */ {<%= l.map {|e| e.ljust(fw)}.join(", ") %>},
63
79
  <% end %>
64
80
  };
81
+ <% else %>
82
+ // No transition functions
83
+ <% end %>
65
84
 
66
85
  // state manager
67
- state_t run_state(state_t cur_state, void *data);
86
+ <%= @prefix %>state_t <%= @prefix %>run_state(<%= @prefix %>state_t cur_state, void *data);
68
87
 
69
88
  #endif
70
89
  EOH
71
90
 
72
91
  CC =<<~EOC
73
92
  #include <syslog.h>
74
- #include "<%= self.cname %>.h"
93
+ #include "<%= @cname %>.h"
75
94
 
76
- // State functions
77
- <% dest = self.destinations.dup %>
78
- <% self.states.each do |s| %>
95
+ <% placeholder = "Your Code Here" %>
96
+ // SEARCH FOR <%= placeholder %> FOR CODE INSERTION POINTS!
97
+
98
+ // ____ _ _
99
+ // / ___|| |_ __ _| |_ ___
100
+ // \\___ \\| __/ _` | __/ _ \\
101
+ // ___) | || (_| | || __/
102
+ // |____/ \\__\\__,_|\\__\\___|
103
+ //
104
+ // __ _ _
105
+ // / _|_ _ _ __ ___| |_(_) ___ _ __ ___
106
+ // | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
107
+ // | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
108
+ // |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
109
+ //
110
+ <% dest = destinations.dup %>
111
+ <% @states.each do |s| %>
79
112
  <% stable = true if dest[s[:id]].include? s[:id] %>
80
- <% dest[s[:id]].map! {|n| "STATE_"+n.upcase} %>
113
+ <% dest[s[:id]].map! {|n| (@prefix+"STATE_"+n).upcase} %>
81
114
  <% if dest[s[:id]].empty? or stable then
82
- dest[s[:id]].unshift "NO_CHANGE"
115
+ dest[s[:id]].unshift @prefix.upcase+"NO_CHANGE"
83
116
  end %>
84
- state_t <%= s[:function] %>(void *data) {
85
- state_t next_state = <%= dest[s[:id]].first %>;
117
+ // Function to be executed in state <%= s[:id] %>
118
+ <%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data) {
119
+ <%= @prefix %>state_t next_state = <%= dest[s[:id]].first %>;
86
120
 
87
121
  syslog(LOG_INFO, "[FSM] In state <%= s[:id] %>");
88
- /* Your code here */
122
+ /* <%= placeholder %> */
89
123
 
90
124
  // valid return states: <%= dest[s[:id]].join(", ") %>
91
125
  switch (next_state) {
@@ -95,43 +129,76 @@ module GV_FSM
95
129
  break;
96
130
  default:
97
131
  syslog(LOG_WARNING, "[FSM] Cannot pass from <%= s[:id] %> to %s, remaining in this state", state_names[next_state]);
98
- next_state = NO_CHANGE;
132
+ next_state = <%= @prefix.upcase %>NO_CHANGE;
99
133
  }
100
134
  return next_state;
101
135
  }
102
136
 
103
137
  <% end %>
104
138
 
105
- // Transition functions
106
- <% self.transition_functions_list.each do |t| %>
139
+ <% if transition_functions_list.count > 0 then %>
140
+ // _____ _ _ _
141
+ // |_ _| __ __ _ _ __ ___(_) |_(_) ___ _ __
142
+ // | || '__/ _` | '_ \\/ __| | __| |/ _ \\| '_ \\
143
+ // | || | | (_| | | | \\__ \\ | |_| | (_) | | | |
144
+ // |_||_| \\__,_|_| |_|___/_|\\__|_|\\___/|_| |_|
145
+ //
146
+ // __ _ _
147
+ // / _|_ _ _ __ ___| |_(_) ___ _ __ ___
148
+ // | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
149
+ // | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
150
+ // |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
151
+ //
152
+
153
+ <% transition_functions_list.each do |t| %>
107
154
  <% next if t == "NULL" %>
108
- void <%= t %>(void *data) {
155
+ <% tpaths = transitions_paths[t] %>
156
+ // This function is called in <%= tpaths.count %> transition<%= tpaths.count == 1 ? '' : 's' %>:
157
+ <% tpaths.each_with_index do |e, i| %>
158
+ // <%= i+1 %>. from <%= e[:from] %> to <%= e[:to] %>
159
+ <% end %>
160
+ void <%= t %>(<%= @prefix %>state_data_t *data) {
109
161
  syslog(LOG_INFO, "[FSM] State transition <%= t %>");
110
- /* Your code here */
162
+ /* <%= placeholder %> */
111
163
  }
112
164
 
165
+ <% end %>
113
166
  <% end %>
114
167
 
115
- // State manager
116
- state_t run_state(state_t cur_state, void *data) {
117
- state_t new_state = state_table[cur_state](data);
118
- transition_func_t *transition = transition_table[cur_state][new_state];
168
+ // ____ _ _
169
+ // / ___|| |_ __ _| |_ ___
170
+ // \\___ \\| __/ _` | __/ _ \\
171
+ // ___) | || (_| | || __/
172
+ // |____/ \\__\\__,_|\\__\\___|
173
+ //
174
+ //
175
+ // _ __ ___ __ _ _ __ __ _ __ _ ___ _ __
176
+ // | '_ ` _ \\ / _` | '_ \\ / _` |/ _` |/ _ \\ '__|
177
+ // | | | | | | (_| | | | | (_| | (_| | __/ |
178
+ // |_| |_| |_|\\__,_|_| |_|\\__,_|\\__, |\\___|_|
179
+ // |___/
180
+
181
+ <%= @prefix %>state_t <%= @prefix %>run_state(<%= @prefix %>state_t cur_state, <%= @prefix %>state_data_t *data) {
182
+ <%= @prefix %>state_t new_state = <%= @prefix %>state_table[cur_state](data);
183
+ <% if transition_functions_list.count > 0 then %>
184
+ transition_func_t *transition = <%= @prefix %>transition_table[cur_state][new_state];
119
185
  if (transition)
120
186
  transition(data);
121
- return new_state == NO_CHANGE ? cur_state : new_state;
187
+ <% end %>
188
+ return new_state == <%= @prefix.upcase %>NO_CHANGE ? cur_state : new_state;
122
189
  };
123
190
 
124
191
 
125
192
  #ifdef TEST_MAIN
126
193
  #include <unistd.h>
127
194
  int main() {
128
- state_t cur_state = STATE_INIT;
195
+ <%= @prefix %>state_t cur_state = <%= @prefix.upcase %>STATE_INIT;
129
196
  openlog("SM", LOG_PID | LOG_PERROR, LOG_USER);
130
197
  syslog(LOG_INFO, "Starting SM");
131
198
  do {
132
- cur_state = run_state(cur_state, NULL);
199
+ cur_state = <%= @prefix %>run_state(cur_state, NULL);
133
200
  sleep(1);
134
- } while (cur_state != STATE_STOP);
201
+ } while (cur_state != <%= @prefix.upcase %>STATE_STOP);
135
202
  return 0;
136
203
  }
137
204
  #endif
@@ -1,3 +1,3 @@
1
1
  module GV_FSM
2
- VERSION = "0.0.2"
2
+ VERSION = "0.2.1"
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.0.2
4
+ version: 0.2.1
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-08-28 00:00:00.000000000 Z
11
+ date: 2020-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-graphviz