gv_fsm 0.3.7 → 0.4.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.
- checksums.yaml +4 -4
- data/bin/gv_fsm +13 -4
- data/lib/gv_fsm.rb +9 -6
- data/lib/templates.rb +623 -267
- 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: f58c122355c77248d9b7ef5fa2ea612c7d9c0e38057a84660155ec4a686547dd
|
4
|
+
data.tar.gz: dfbf56baf72ae57f70f8016d27c16770e779c093b1f8be164e50d9ac5fa914d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c93b183ab778b42782f2b635a7ce07e96188f798b4e8d04608f604a697b806879f8555f49858bf0a2212821ec7493a45c4ce71492466549e09d98487a58a50f
|
7
|
+
data.tar.gz: dd27080c97ae8da158b75fa03949e060bd21e04d11f9ca20f8d6384ae436c089eddd7153e78e31c44ab0edcf5207867819fd4babf37531e7c78bc3f93dbe7b59
|
data/bin/gv_fsm
CHANGED
@@ -16,14 +16,18 @@ op = OptionParser.new do |parser|
|
|
16
16
|
EOB
|
17
17
|
|
18
18
|
parser.on("-p", "--project PROJECT_NAME",
|
19
|
-
"Set the project name to PROJECT_NAME") do |pn|
|
20
|
-
sm.project_name = pn
|
19
|
+
"Set the project name to PROJECT_NAME (in C++ also namespace)") do |pn|
|
20
|
+
sm.project_name = pn.gsub(" ", "_")
|
21
21
|
end
|
22
22
|
|
23
23
|
parser.on("-d", "--description DESCRIPTION", "Use DESCRITION string in header") do |desc|
|
24
24
|
sm.description = desc
|
25
25
|
end
|
26
26
|
|
27
|
+
parser.on("--cpp", "Generate C++17 sources") do
|
28
|
+
sm.plain_c = false
|
29
|
+
end
|
30
|
+
|
27
31
|
parser.on("-o", "--output_file NAME", "Use NAME for generated .c and .h files") do |f|
|
28
32
|
sm.cname = f
|
29
33
|
end
|
@@ -61,6 +65,11 @@ end
|
|
61
65
|
|
62
66
|
op.parse!
|
63
67
|
|
68
|
+
if sm.ino then
|
69
|
+
sm.plain_c = true
|
70
|
+
sm.syslog = false
|
71
|
+
end
|
72
|
+
|
64
73
|
unless ARGV[0]
|
65
74
|
STDERR.puts "ERROR: I need the path to a Graphviz file!\n\n"
|
66
75
|
STDERR.puts op
|
@@ -93,7 +102,7 @@ if !(top[:sources].count == 1 and top[:sinks].count <= 1) then
|
|
93
102
|
exit 4
|
94
103
|
end
|
95
104
|
|
96
|
-
puts "Generating C functions for states: #{sm.states_list.join(", ")}."
|
105
|
+
puts "Generating #{sm.plain_c ? "C" : "C++17"} functions for states: #{sm.states_list.join(", ")}."
|
97
106
|
puts " for transition: #{sm.transition_functions_list.join(", ")}."
|
98
107
|
|
99
108
|
if (sm.sigint) then
|
@@ -104,7 +113,7 @@ if (sm.sigint) then
|
|
104
113
|
end
|
105
114
|
|
106
115
|
if options[:header] then
|
107
|
-
name = sm.generate_h
|
116
|
+
name = sm.generate_h
|
108
117
|
puts "Generated header #{name}"
|
109
118
|
end
|
110
119
|
|
data/lib/gv_fsm.rb
CHANGED
@@ -11,6 +11,7 @@ module GV_FSM
|
|
11
11
|
class FSM
|
12
12
|
attr_reader :states, :transitions, :dotfile, :prefix, :error
|
13
13
|
attr_accessor :project_name, :description, :cname, :syslog, :ino, :sigint
|
14
|
+
attr_accessor :plain_c
|
14
15
|
include GV_FSM::Templates
|
15
16
|
|
16
17
|
def initialize(filename = nil)
|
@@ -21,6 +22,7 @@ module GV_FSM
|
|
21
22
|
@matrix = nil
|
22
23
|
@nodemap = {}
|
23
24
|
@sigint = nil
|
25
|
+
@plain_c = true
|
24
26
|
parse(filename) if filename
|
25
27
|
end
|
26
28
|
|
@@ -141,20 +143,21 @@ module GV_FSM
|
|
141
143
|
end
|
142
144
|
|
143
145
|
def generate_c(filename = @cname)
|
144
|
-
ext = @ino ? "cpp" : "c"
|
146
|
+
ext = (@ino || !@plain_c) ? "cpp" : "c"
|
145
147
|
fname = "#{filename}.#{ext}"
|
146
148
|
File.open(fname, "w") do |f|
|
147
|
-
f.puts ERB.new(HEADER, trim_mode: "
|
148
|
-
f.puts ERB.new(CC, trim_mode: "
|
149
|
+
f.puts ERB.new(HEADER, trim_mode: "<>-").result(binding)
|
150
|
+
f.puts ERB.new(@plain_c ? CC : CPP, trim_mode: "<>-").result(binding)
|
149
151
|
end
|
150
152
|
return fname
|
151
153
|
end
|
152
154
|
|
153
155
|
def generate_h(filename = @cname)
|
154
|
-
|
156
|
+
ext = @plain_c ? "h" : "hpp"
|
157
|
+
fname = "#{filename}.#{ext}"
|
155
158
|
File.open(fname, "w") do |f|
|
156
|
-
f.puts ERB.new(HEADER, trim_mode: "
|
157
|
-
f.puts ERB.new(HH, trim_mode: "
|
159
|
+
f.puts ERB.new(HEADER, trim_mode: "<>-").result(binding)
|
160
|
+
f.puts ERB.new(@plain_c ? HH : HPP, trim_mode: "<>-").result(binding)
|
158
161
|
end
|
159
162
|
return fname
|
160
163
|
end
|
data/lib/templates.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
|
2
2
|
module GV_FSM
|
3
3
|
module Templates
|
4
|
+
|
5
|
+
# _ _ _____ _ ____ _____ ____
|
6
|
+
# | | | | ____| / \ | _ \| ____| _ \
|
7
|
+
# | |_| | _| / _ \ | | | | _| | |_) |
|
8
|
+
# | _ | |___ / ___ \| |_| | |___| _ <
|
9
|
+
# |_| |_|_____/_/ \_\____/|_____|_| \_\
|
10
|
+
|
4
11
|
HEADER =<<~EOHEADER
|
5
12
|
/******************************************************************************
|
6
13
|
Finite State Machine
|
@@ -21,294 +28,643 @@ module GV_FSM
|
|
21
28
|
|
22
29
|
EOHEADER
|
23
30
|
|
31
|
+
# ____ _ _ _____ _ ____ _____ ____
|
32
|
+
# / ___| | | | | ____| / \ | _ \| ____| _ \
|
33
|
+
# | | | |_| | _| / _ \ | | | | _| | |_) |
|
34
|
+
# | |___ | _ | |___ / ___ \| |_| | |___| _ <
|
35
|
+
# \____| |_| |_|_____/_/ \_\____/|_____|_| \_\
|
36
|
+
|
37
|
+
|
38
|
+
|
24
39
|
HH =<<~EOH
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
40
|
+
<% if !@ino then %>
|
41
|
+
#ifndef <%= File::basename(@cname).upcase %>_H
|
42
|
+
#define <%= File::basename(@cname).upcase %>_H
|
43
|
+
#ifdef __cplusplus
|
44
|
+
extern "C" {
|
45
|
+
#endif
|
46
|
+
#include <stdlib.h>
|
47
|
+
<% else -%>
|
48
|
+
#include <arduino.h>
|
49
|
+
<% end -%>
|
35
50
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
// NOTHING SHALL BE CHANGED AFTER THIS LINE!
|
43
|
-
<% end %>
|
51
|
+
// State data object
|
52
|
+
// By default set to void; override this typedef or load the proper
|
53
|
+
// header if you need
|
54
|
+
typedef void <%= @prefix %>state_data_t;
|
55
|
+
<% if !@ino then -%>
|
44
56
|
|
45
|
-
|
46
|
-
|
47
|
-
<% @states.each_with_index do |s, i| %>
|
48
|
-
<%= @prefix.upcase %>STATE_<%= s[:id].upcase %><%= i == 0 ? " = 0" : "" %>,
|
49
|
-
<% end %>
|
50
|
-
<%= @prefix.upcase %>NUM_STATES,
|
51
|
-
<%= @prefix.upcase %>NO_CHANGE
|
52
|
-
} <%= @prefix %>state_t;
|
57
|
+
// NOTHING SHALL BE CHANGED AFTER THIS LINE!
|
58
|
+
<% end -%>
|
53
59
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
// State function prototype
|
63
|
-
typedef <%= @prefix %>state_t state_func_t(<%= @prefix %>state_data_t *data);
|
64
|
-
<%end %>
|
65
|
-
|
66
|
-
// State functions
|
67
|
-
<% dest = destinations.dup %>
|
68
|
-
<% @states.each do |s| %>
|
69
|
-
<% stable = true if dest[s[:id]].include? s[:id] %>
|
70
|
-
<% dest[s[:id]].map! {|n| (@prefix+"STATE_"+n).upcase} %>
|
71
|
-
<% if dest[s[:id]].empty? or stable then
|
72
|
-
dest[s[:id]].unshift @prefix.upcase+"NO_CHANGE"
|
73
|
-
end %>
|
74
|
-
// Function to be executed in state <%= s[:id] %>
|
75
|
-
// valid return states: <%= dest[s[:id]].join(", ") %>
|
76
|
-
<%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data);
|
77
|
-
<% end %>
|
60
|
+
// List of states
|
61
|
+
typedef enum {
|
62
|
+
<% @states.each_with_index do |s, i| -%>
|
63
|
+
<%= @prefix.upcase %>STATE_<%= s[:id].upcase %><%= i == 0 ? " = 0" : "" %>,
|
64
|
+
<% end -%>
|
65
|
+
<%= @prefix.upcase %>NUM_STATES,
|
66
|
+
<%= @prefix.upcase %>NO_CHANGE
|
67
|
+
} <%= @prefix %>state_t;
|
78
68
|
|
69
|
+
// State human-readable names
|
70
|
+
extern const char *<%= @prefix %>state_names[];
|
79
71
|
|
80
|
-
|
81
|
-
|
82
|
-
|
72
|
+
<% if transition_functions_list.count > 0 then -%>
|
73
|
+
// State function and state transition prototypes
|
74
|
+
typedef <%= @prefix %>state_t state_func_t(<%= @prefix %>state_data_t *data);
|
75
|
+
typedef void transition_func_t(<%= @prefix %>state_data_t *data);
|
76
|
+
<% else -%>
|
77
|
+
// State function prototype
|
78
|
+
typedef <%= @prefix %>state_t state_func_t(<%= @prefix %>state_data_t *data);
|
79
|
+
<%end -%>
|
83
80
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
81
|
+
// State functions
|
82
|
+
<% dest = destinations.dup -%>
|
83
|
+
<% @states.each do |s| -%>
|
84
|
+
<% stable = true if dest[s[:id]].include? s[:id] -%>
|
85
|
+
<% dest[s[:id]].map! {|n| (@prefix+"STATE_"+n).upcase} -%>
|
86
|
+
<% if dest[s[:id]].empty? or stable then
|
87
|
+
dest[s[:id]].unshift @prefix.upcase+"NO_CHANGE"
|
88
|
+
end -%>
|
89
|
+
// Function to be executed in state <%= s[:id] %>
|
90
|
+
// valid return states: <%= dest[s[:id]].join(", ") %>
|
91
|
+
<%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data);
|
92
|
+
<% end -%>
|
90
93
|
|
91
|
-
// Table of transition functions
|
92
|
-
extern transition_func_t *const <%= @prefix %>transition_table[<%= @prefix.upcase %>NUM_STATES][<%= @prefix.upcase %>NUM_STATES];
|
93
|
-
<% else %>
|
94
|
-
// No transition functions
|
95
|
-
<% end %>
|
96
94
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
95
|
+
// List of state functions
|
96
|
+
extern state_func_t *const <%= @prefix %>state_table[<%= @prefix.upcase %>NUM_STATES];
|
97
|
+
|
98
|
+
|
99
|
+
<% if transition_functions_list.count > 0 then -%>
|
100
|
+
// Transition functions
|
101
|
+
<% transition_functions_list.each do |t| -%>
|
102
|
+
<% next if t == "NULL" -%>
|
103
|
+
void <%= t %>(<%= @prefix %>state_data_t *data);
|
104
|
+
<% end -%>
|
105
|
+
|
106
|
+
// Table of transition functions
|
107
|
+
extern transition_func_t *const <%= @prefix %>transition_table[<%= @prefix.upcase %>NUM_STATES][<%= @prefix.upcase %>NUM_STATES];
|
108
|
+
<% else -%>
|
109
|
+
// No transition functions
|
110
|
+
<% end -%>
|
111
|
+
|
112
|
+
// state manager
|
113
|
+
<%= @prefix %>state_t <%= @prefix %>run_state(<%= @prefix %>state_t cur_state, <%= @prefix %>state_data_t *data);
|
114
|
+
|
115
|
+
<% if !@ino then -%>
|
116
|
+
#ifdef __cplusplus
|
117
|
+
}
|
118
|
+
#endif
|
119
|
+
#endif // <%= File::basename(@cname).upcase %>_H
|
120
|
+
<% end -%>
|
106
121
|
EOH
|
107
122
|
|
123
|
+
|
124
|
+
# ____ ____ ___ _ _ ____ ____ _____
|
125
|
+
# / ___| / ___| / _ \| | | | _ \ / ___| ____|
|
126
|
+
# | | \___ \| | | | | | | |_) | | | _|
|
127
|
+
# | |___ ___) | |_| | |_| | _ <| |___| |___
|
128
|
+
# \____| |____/ \___/ \___/|_| \_\\____|_____|
|
129
|
+
|
130
|
+
|
108
131
|
CC =<<~EOC
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
<% if sigint then %>// Install signal handler:
|
120
|
-
// SIGINT requests a transition to state <%= self.sigint %>
|
121
|
-
#include <signal.h>
|
122
|
-
static int _exit_request = 0;
|
123
|
-
static void signal_handler(int signal) {
|
124
|
-
if (signal == SIGINT) {
|
125
|
-
_exit_request = 1;<% if log == :syslog then %>
|
126
|
-
syslog(LOG_WARNING, "[FSM] SIGINT transition to <%= sigint %>");<% elsif log == :ino then %>
|
127
|
-
Serial.println("[FSM] SIGINT transition to <%= sigint %>");<% end %>
|
128
|
-
}
|
129
|
-
}
|
132
|
+
<% if !@ino then -%>
|
133
|
+
<% if @syslog then -%>
|
134
|
+
<% log = :syslog %>
|
135
|
+
#include <syslog.h>
|
136
|
+
<% end -%>
|
137
|
+
<% else -%>
|
138
|
+
<% if @syslog then log = :ino end -%>
|
139
|
+
<% end -%>
|
140
|
+
#include "<%= File::basename(@cname) %>.h"
|
130
141
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
<% @states.each do |s| %>
|
143
|
-
<%= (s[:function] + ',').ljust(fw+1) %> // in state <%= s[:id] %>
|
144
|
-
<% end %>
|
145
|
-
};
|
146
|
-
<% if transition_functions_list.count > 0 then %>
|
147
|
-
|
148
|
-
// Table of transition functions
|
149
|
-
transition_func_t *const <%= @prefix %>transition_table[<%= @prefix.upcase %>NUM_STATES][<%= @prefix.upcase %>NUM_STATES] = {
|
150
|
-
<% sl = states_list %>
|
151
|
-
<% fw = transition_functions_list.max {|a, b| a.length <=> b.length}.length %>
|
152
|
-
<% sw = [states_list, "states:"].flatten.max {|a, b| a.length <=> b.length}.length %>
|
153
|
-
/* <%= "states:".ljust(sw) %> <%= sl.map {|e| e.ljust(fw) }.join(", ") %> */
|
154
|
-
<% transitions_map.each_with_index do |l, i| %>
|
155
|
-
/* <%= sl[i].ljust(sw) %> */ {<%= l.map {|e| e.ljust(fw)}.join(", ") %>},
|
156
|
-
<% end %>
|
157
|
-
};
|
158
|
-
<% else %>
|
159
|
-
// No transition functions
|
160
|
-
<% end %>
|
142
|
+
<% if sigint then %>// Install signal handler:
|
143
|
+
// SIGINT requests a transition to state <%= self.sigint %>
|
144
|
+
#include <signal.h>
|
145
|
+
static int _exit_request = 0;
|
146
|
+
static void signal_handler(int signal) {
|
147
|
+
if (signal == SIGINT) {
|
148
|
+
_exit_request = 1;<% if log == :syslog then %>
|
149
|
+
syslog(LOG_WARNING, "[FSM] SIGINT transition to <%= sigint %>");<% elsif log == :ino then %>
|
150
|
+
Serial.println("[FSM] SIGINT transition to <%= sigint %>");<% end %>
|
151
|
+
}
|
152
|
+
}
|
161
153
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
* ___) | || (_| | || __/
|
166
|
-
* |____/ \\__\\__,_|\\__\\___|
|
167
|
-
*
|
168
|
-
* __ _ _
|
169
|
-
* / _|_ _ _ __ ___| |_(_) ___ _ __ ___
|
170
|
-
* | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
|
171
|
-
* | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
|
172
|
-
* |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
|
173
|
-
*/
|
174
|
-
<% dest = destinations.dup %>
|
175
|
-
<% topo = self.topology %>
|
176
|
-
<% @states.each do |s| %>
|
177
|
-
<% stable = true if dest[s[:id]].include? s[:id] %>
|
178
|
-
<% dest[s[:id]].map! {|n| (@prefix+"STATE_"+n).upcase} %>
|
179
|
-
<% if dest[s[:id]].empty? or stable then
|
180
|
-
dest[s[:id]].unshift @prefix.upcase+"NO_CHANGE"
|
181
|
-
end %>
|
182
|
-
// Function to be executed in state <%= s[:id] %>
|
183
|
-
// valid return states: <%= dest[s[:id]].join(", ") %>
|
184
|
-
<% if sigint && stable && topo[:sources][0] != s[:id] then %>
|
185
|
-
// SIGINT triggers an emergency transition to <%= self.sigint %>
|
186
|
-
<% end %>
|
187
|
-
<%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data) {
|
188
|
-
<%= @prefix %>state_t next_state = <%= dest[s[:id]].first %>;
|
189
|
-
<% if sigint && topo[:sources][0] == s[:id] then %>signal(SIGINT, signal_handler);
|
190
|
-
<% end %>
|
191
|
-
<% if log == :syslog then %>
|
192
|
-
syslog(LOG_INFO, "[FSM] In state <%= s[:id] %>");
|
193
|
-
<% elsif log == :ino then %>
|
194
|
-
Serial.println("[FSM] In state <%= s[:id] %>");
|
195
|
-
<% end %>
|
196
|
-
/* <%= placeholder %> */
|
197
|
-
|
198
|
-
switch (next_state) {
|
199
|
-
<% dest[s[:id]].each do |str| %>
|
200
|
-
case <%= str %>:
|
201
|
-
<% end %>
|
202
|
-
break;
|
203
|
-
default:
|
204
|
-
<% if log == :syslog then %>
|
205
|
-
syslog(LOG_WARNING, "[FSM] Cannot pass from <%= s[:id] %> to %s, remaining in this state", <%= @prefix %>state_names[next_state]);
|
206
|
-
<% elsif log == :ino then %>
|
207
|
-
Serial.print("[FSM] Cannot pass from <%= s[:id] %> to ");
|
208
|
-
Serial.print(<%= @prefix %>state_names[next_state]);
|
209
|
-
Serial.println(", remaining in this state");
|
210
|
-
<% end %>
|
211
|
-
next_state = <%= @prefix.upcase %>NO_CHANGE;
|
212
|
-
}
|
213
|
-
<% if sigint && stable && topo[:sources][0] != s[:id] then %>
|
214
|
-
// SIGINT transition override
|
215
|
-
if (_exit_request) next_state = <%= (@prefix+"STATE_"+self.sigint ).upcase %>;
|
216
|
-
<% end %>
|
217
|
-
return next_state;
|
218
|
-
}
|
154
|
+
<% end -%>
|
155
|
+
<% placeholder = "Your Code Here" -%>
|
156
|
+
// SEARCH FOR <%= placeholder %> FOR CODE INSERTION POINTS!
|
219
157
|
|
220
|
-
|
158
|
+
// GLOBALS
|
159
|
+
// State human-readable names
|
160
|
+
const char *<%= @prefix %>state_names[] = {<%= states_list.map {|sn| '"'+sn+'"'}.join(", ") %>};
|
221
161
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
* / _|_ _ _ __ ___| |_(_) ___ _ __ ___
|
231
|
-
* | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
|
232
|
-
* | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
|
233
|
-
* |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
|
234
|
-
*/
|
235
|
-
|
236
|
-
<% transition_functions_list.each do |t| %>
|
237
|
-
<% next if t == "NULL" %>
|
238
|
-
<% tpaths = transitions_paths[t] %>
|
239
|
-
// This function is called in <%= tpaths.count %> transition<%= tpaths.count == 1 ? '' : 's' %>:
|
240
|
-
<% tpaths.each_with_index do |e, i| %>
|
241
|
-
// <%= i+1 %>. from <%= e[:from] %> to <%= e[:to] %>
|
242
|
-
<% end %>
|
243
|
-
void <%= t %>(<%= @prefix %>state_data_t *data) {
|
244
|
-
<% if log == :syslog then %>
|
245
|
-
syslog(LOG_INFO, "[FSM] State transition <%= t %>");
|
246
|
-
<% elsif log == :ino then %>
|
247
|
-
Serial.println("[FSM] State transition <%= t %>");
|
248
|
-
<% end %>
|
249
|
-
/* <%= placeholder %> */
|
250
|
-
}
|
162
|
+
// List of state functions
|
163
|
+
<% fw = state_functions_list.max {|a, b| a.length <=> b.length}.length -%>
|
164
|
+
state_func_t *const <%= @prefix %>state_table[<%= @prefix.upcase %>NUM_STATES] = {
|
165
|
+
<% @states.each do |s| -%>
|
166
|
+
<%= (s[:function] + ',').ljust(fw+1) %> // in state <%= s[:id] %>
|
167
|
+
<% end -%>
|
168
|
+
};
|
169
|
+
<% if transition_functions_list.count > 0 then -%>
|
251
170
|
|
252
|
-
|
253
|
-
|
171
|
+
// Table of transition functions
|
172
|
+
transition_func_t *const <%= @prefix %>transition_table[<%= @prefix.upcase %>NUM_STATES][<%= @prefix.upcase %>NUM_STATES] = {
|
173
|
+
<% sl = states_list -%>
|
174
|
+
<% fw = transition_functions_list.max {|a, b| a.length <=> b.length}.length -%>
|
175
|
+
<% sw = [states_list, "states:"].flatten.max {|a, b| a.length <=> b.length}.length -%>
|
176
|
+
/* <%= "states:".ljust(sw) %> <%= sl.map {|e| e.ljust(fw) }.join(", ") %> */
|
177
|
+
<% transitions_map.each_with_index do |l, i| -%>
|
178
|
+
/* <%= sl[i].ljust(sw) %> */ {<%= l.map {|e| e.ljust(fw)}.join(", ") %>},
|
179
|
+
<% end -%>
|
180
|
+
};
|
181
|
+
<% else -%>
|
182
|
+
// No transition functions
|
183
|
+
<% end -%>
|
184
|
+
|
185
|
+
/* ____ _ _
|
186
|
+
* / ___|| |_ __ _| |_ ___
|
187
|
+
* \\___ \\| __/ _` | __/ _ \\
|
188
|
+
* ___) | || (_| | || __/
|
189
|
+
* |____/ \\__\\__,_|\\__\\___|
|
190
|
+
*
|
191
|
+
* __ _ _
|
192
|
+
* / _|_ _ _ __ ___| |_(_) ___ _ __ ___
|
193
|
+
* | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
|
194
|
+
* | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
|
195
|
+
* |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
|
196
|
+
*/
|
197
|
+
<% dest = destinations.dup -%>
|
198
|
+
<% topo = self.topology -%>
|
199
|
+
<% @states.each do |s| -%>
|
200
|
+
<% stable = true if dest[s[:id]].include? s[:id] -%>
|
201
|
+
<% dest[s[:id]].map! {|n| (@prefix+"STATE_"+n).upcase} -%>
|
202
|
+
<% if dest[s[:id]].empty? or stable then
|
203
|
+
dest[s[:id]].unshift @prefix.upcase+"NO_CHANGE"
|
204
|
+
end %>
|
205
|
+
// Function to be executed in state <%= s[:id] %>
|
206
|
+
// valid return states: <%= dest[s[:id]].join(", ") %>
|
207
|
+
<% if sigint && stable && topo[:sources][0] != s[:id] then -%>
|
208
|
+
// SIGINT triggers an emergency transition to <%= self.sigint %>
|
209
|
+
<% end -%>
|
210
|
+
<%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data) {
|
211
|
+
<%= @prefix %>state_t next_state = <%= dest[s[:id]].first -%>;
|
212
|
+
<% if sigint && topo[:sources][0] == s[:id] then -%>
|
213
|
+
signal(SIGINT, signal_handler);
|
214
|
+
<% end -%>
|
215
|
+
<% if log == :syslog then -%>
|
216
|
+
syslog(LOG_INFO, "[FSM] In state <%= s[:id] %>");
|
217
|
+
<% elsif log == :ino then -%>
|
218
|
+
Serial.println("[FSM] In state <%= s[:id] %>");
|
219
|
+
<% end -%>
|
220
|
+
/* <%= placeholder %> */
|
221
|
+
|
222
|
+
switch (next_state) {
|
223
|
+
<% dest[s[:id]].each do |str| -%>
|
224
|
+
case <%= str %>:
|
225
|
+
<% end -%>
|
226
|
+
break;
|
227
|
+
default:
|
228
|
+
<% if log == :syslog then -%>
|
229
|
+
syslog(LOG_WARNING, "[FSM] Cannot pass from <%= s[:id] %> to %s, remaining in this state", <%= @prefix %>state_names[next_state]);
|
230
|
+
<% elsif log == :ino then -%>
|
231
|
+
Serial.print("[FSM] Cannot pass from <%= s[:id] %> to ");
|
232
|
+
Serial.print(<%= @prefix %>state_names[next_state]);
|
233
|
+
Serial.println(", remaining in this state");
|
234
|
+
<% end -%>
|
235
|
+
next_state = <%= @prefix.upcase %>NO_CHANGE;
|
236
|
+
}
|
237
|
+
<% if sigint && stable && topo[:sources][0] != s[:id] then -%>
|
238
|
+
// SIGINT transition override
|
239
|
+
if (_exit_request)
|
240
|
+
next_state = <%= (@prefix+"STATE_"+self.sigint ).upcase %>;
|
241
|
+
<% end %>
|
242
|
+
return next_state;
|
243
|
+
}
|
244
|
+
|
245
|
+
<% end %>
|
246
|
+
|
247
|
+
<% if transition_functions_list.count > 0 then -%>
|
248
|
+
/* _____ _ _ _
|
249
|
+
* |_ _| __ __ _ _ __ ___(_) |_(_) ___ _ __
|
250
|
+
* | || '__/ _` | '_ \\/ __| | __| |/ _ \\| '_ \\
|
251
|
+
* | || | | (_| | | | \\__ \\ | |_| | (_) | | | |
|
252
|
+
* |_||_| \\__,_|_| |_|___/_|\\__|_|\\___/|_| |_|
|
253
|
+
*
|
254
|
+
* __ _ _
|
255
|
+
* / _|_ _ _ __ ___| |_(_) ___ _ __ ___
|
256
|
+
* | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
|
257
|
+
* | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
|
258
|
+
* |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
|
259
|
+
*/
|
260
|
+
|
261
|
+
<% transition_functions_list.each do |t| -%>
|
262
|
+
<% next if t == "NULL" -%>
|
263
|
+
<% tpaths = transitions_paths[t] -%>
|
264
|
+
// This function is called in <%= tpaths.count %> transition<%= tpaths.count == 1 ? '' : 's' %>:
|
265
|
+
<% tpaths.each_with_index do |e, i| -%>
|
266
|
+
// <%= i+1 %>. from <%= e[:from] %> to <%= e[:to] %>
|
267
|
+
<% end -%>
|
268
|
+
void <%= t %>(<%= @prefix %>state_data_t *data) {
|
269
|
+
<% if log == :syslog then -%>
|
270
|
+
syslog(LOG_INFO, "[FSM] State transition <%= t %>");
|
271
|
+
<% elsif log == :ino then -%>
|
272
|
+
Serial.println("[FSM] State transition <%= t %>");
|
273
|
+
<% end -%>
|
274
|
+
/* <%= placeholder %> */
|
275
|
+
}
|
276
|
+
|
277
|
+
<% end -%>
|
278
|
+
<% end -%>
|
279
|
+
|
280
|
+
/* ____ _ _
|
281
|
+
* / ___|| |_ __ _| |_ ___
|
282
|
+
* \\___ \\| __/ _` | __/ _ \\
|
283
|
+
* ___) | || (_| | || __/
|
284
|
+
* |____/ \\__\\__,_|\\__\\___|
|
285
|
+
*
|
286
|
+
*
|
287
|
+
* _ __ ___ __ _ _ __ __ _ __ _ ___ _ __
|
288
|
+
* | '_ ` _ \\ / _` | '_ \\ / _` |/ _` |/ _ \\ '__|
|
289
|
+
* | | | | | | (_| | | | | (_| | (_| | __/ |
|
290
|
+
* |_| |_| |_|\\__,_|_| |_|\\__,_|\\__, |\\___|_|
|
291
|
+
* |___/
|
292
|
+
*/
|
293
|
+
|
294
|
+
<%= @prefix %>state_t <%= @prefix %>run_state(<%= @prefix %>state_t cur_state, <%= @prefix %>state_data_t *data) {
|
295
|
+
<%= @prefix %>state_t new_state = <%= @prefix %>state_table[cur_state](data);
|
296
|
+
if (new_state == <%= @prefix.upcase %>NO_CHANGE) new_state = cur_state;
|
297
|
+
<% if transition_functions_list.count > 0 then %>
|
298
|
+
transition_func_t *transition = <%= @prefix %>transition_table[cur_state][new_state];
|
299
|
+
if (transition)
|
300
|
+
transition(data);
|
301
|
+
<% end %>
|
302
|
+
return new_state;
|
303
|
+
};
|
304
|
+
|
305
|
+
<% if @ino then %>
|
306
|
+
/* Example usage:
|
307
|
+
<%= @prefix %>state_data_t data = {count: 1};
|
308
|
+
|
309
|
+
void loop() {
|
310
|
+
static <%= @prefix %>state_t cur_state = <%= @prefix.upcase %>STATE_INIT;
|
311
|
+
cur_state = <%= @prefix %>run_state(cur_state, &data);
|
312
|
+
}
|
313
|
+
*/
|
314
|
+
<% else %>
|
315
|
+
<% nsinks = topology[:sinks].count %>
|
316
|
+
#ifdef TEST_MAIN
|
317
|
+
#include <unistd.h>
|
318
|
+
int main() {
|
319
|
+
<%= @prefix %>state_t cur_state = <%= @prefix.upcase %>STATE_<%= @states.first[:id].upcase %>;
|
320
|
+
<% if @syslog then %>
|
321
|
+
openlog("SM", LOG_PID | LOG_PERROR, LOG_USER);
|
322
|
+
syslog(LOG_INFO, "Starting SM");
|
323
|
+
<% end %>
|
324
|
+
do {
|
325
|
+
cur_state = <%= @prefix %>run_state(cur_state, NULL);
|
326
|
+
sleep(1);
|
327
|
+
<% if nsinks == 1 %>
|
328
|
+
} while (cur_state != <%= @prefix.upcase %>STATE_<%= topology[:sinks][0].upcase %>);
|
329
|
+
<%= @prefix %>run_state(cur_state, NULL);
|
330
|
+
<% else %>
|
331
|
+
} while (1);
|
332
|
+
<% end %>
|
333
|
+
return 0;
|
334
|
+
}
|
335
|
+
#endif
|
336
|
+
<% end %>
|
337
|
+
EOC
|
254
338
|
|
255
|
-
/* ____ _ _
|
256
|
-
* / ___|| |_ __ _| |_ ___
|
257
|
-
* \\___ \\| __/ _` | __/ _ \\
|
258
|
-
* ___) | || (_| | || __/
|
259
|
-
* |____/ \\__\\__,_|\\__\\___|
|
260
|
-
*
|
261
|
-
*
|
262
|
-
* _ __ ___ __ _ _ __ __ _ __ _ ___ _ __
|
263
|
-
* | '_ ` _ \\ / _` | '_ \\ / _` |/ _` |/ _ \\ '__|
|
264
|
-
* | | | | | | (_| | | | | (_| | (_| | __/ |
|
265
|
-
* |_| |_| |_|\\__,_|_| |_|\\__,_|\\__, |\\___|_|
|
266
|
-
* |___/
|
267
|
-
*/
|
268
|
-
|
269
|
-
<%= @prefix %>state_t <%= @prefix %>run_state(<%= @prefix %>state_t cur_state, <%= @prefix %>state_data_t *data) {
|
270
|
-
<%= @prefix %>state_t new_state = <%= @prefix %>state_table[cur_state](data);
|
271
|
-
if (new_state == <%= @prefix.upcase %>NO_CHANGE) new_state = cur_state;
|
272
|
-
<% if transition_functions_list.count > 0 then %>
|
273
|
-
transition_func_t *transition = <%= @prefix %>transition_table[cur_state][new_state];
|
274
|
-
if (transition)
|
275
|
-
transition(data);
|
276
|
-
<% end %>
|
277
|
-
return new_state;
|
278
|
-
};
|
279
339
|
|
280
|
-
|
281
|
-
|
282
|
-
|
340
|
+
# ____ ____ ____ _ _ _____ _ ____ _____ ____
|
341
|
+
# / ___| _ \| _ \ | | | | ____| / \ | _ \| ____| _ \
|
342
|
+
# | | | |_) | |_) | | |_| | _| / _ \ | | | | _| | |_) |
|
343
|
+
# | |___| __/| __/ | _ | |___ / ___ \| |_| | |___| _ <
|
344
|
+
# \____|_| |_| |_| |_|_____/_/ \_\____/|_____|_| \_\
|
345
|
+
|
283
346
|
|
284
|
-
|
285
|
-
|
286
|
-
|
347
|
+
HPP =<<~EOHPP
|
348
|
+
#ifndef <%= File::basename(@cname).upcase %>_HPP
|
349
|
+
#define <%= File::basename(@cname).upcase %>_HPP
|
350
|
+
#include <functional>
|
351
|
+
#include <iostream>
|
352
|
+
#include <map>
|
353
|
+
#include <string>
|
354
|
+
#include <tuple>
|
355
|
+
<% if @syslog then -%>
|
356
|
+
<% log = :syslog -%>
|
357
|
+
#include <syslog.h>
|
358
|
+
<% end -%>
|
359
|
+
<% if sigint then -%>
|
360
|
+
// Install signal handler:
|
361
|
+
// SIGINT requests a transition to state <%= self.sigint %>
|
362
|
+
#include <csignal>
|
363
|
+
<% end %>
|
364
|
+
|
365
|
+
using namespace std::string_literals;
|
366
|
+
<% ns = @project_name || "FSM" -%>
|
367
|
+
namespace <%= ns %> {
|
368
|
+
static bool <%= self.sigint %>_requested = false;
|
369
|
+
|
370
|
+
// List of states
|
371
|
+
typedef enum {
|
372
|
+
<% @states.each do |s| -%>
|
373
|
+
<%= @prefix.upcase %>STATE_<%= s[:id].upcase %>,
|
374
|
+
<% end -%>
|
375
|
+
<%= @prefix.upcase %>NUM_STATES,
|
376
|
+
<%= @prefix.upcase %>NO_CHANGE,
|
377
|
+
<%= @prefix.upcase %>UNIMPLEMENTED
|
378
|
+
} <%= @prefix %>state_t;
|
379
|
+
|
380
|
+
// State human-readable names
|
381
|
+
std::map<<%= @prefix %>state_t, char const *> state_names = {
|
382
|
+
<% @states.each do |s| -%>
|
383
|
+
{<%= @prefix.upcase %>STATE_<%= s[:id].upcase %>, "<%= s[:id].upcase %>"},
|
384
|
+
<% end -%>
|
385
|
+
{<%= @prefix.upcase %>NUM_STATES, "NUM_STATES"},
|
386
|
+
{<%= @prefix.upcase %>NO_CHANGE, "NO_CHANGE"},
|
387
|
+
{<%= @prefix.upcase %>UNIMPLEMENTED, "UNIMPLEMENTED"}
|
388
|
+
};
|
389
|
+
|
390
|
+
// Custom state functions:
|
391
|
+
<% @states.each do |s| -%>
|
392
|
+
template<class T>
|
393
|
+
<%= @prefix %>state_t <%= s[:function] %>(T &data);
|
394
|
+
<% end -%>
|
395
|
+
|
396
|
+
<% if transition_functions_list.count > 0 then -%>
|
397
|
+
// Custom transition functions:
|
398
|
+
<% transition_functions_list.each do |t| -%>
|
399
|
+
template<class T>
|
400
|
+
void <%= t %>(T &data);
|
401
|
+
<% end -%>
|
402
|
+
<% end -%>
|
403
|
+
|
404
|
+
// Finite State Machine class
|
405
|
+
template <typename DATA_T>
|
406
|
+
class FiniteStateMachine {
|
407
|
+
|
408
|
+
// Function templates
|
409
|
+
using state_fun = std::function<<%= @prefix %>state_t(DATA_T &data)>;
|
410
|
+
using transition_fun = std::function<void(DATA_T &data)>;
|
411
|
+
using operation_fun = std::function<void(DATA_T &data)>;
|
412
|
+
|
413
|
+
private:
|
414
|
+
std::pair<<%= @prefix %>state_t, <%= @prefix %>state_t> _state{<%= @prefix.upcase %>STATE_<%= states[0][:id].upcase %>, <%= @prefix.upcase %>STATE_<%= states[0][:id].upcase %>};
|
415
|
+
std::map<<%= @prefix %>state_t, state_fun> _states;
|
416
|
+
std::map<<%= @prefix %>state_t, std::map<<%= @prefix %>state_t, transition_fun>> _transitions;
|
417
|
+
std::function<void()> _timing_func;
|
418
|
+
DATA_T *_data;
|
419
|
+
|
420
|
+
public:
|
421
|
+
|
422
|
+
FiniteStateMachine(DATA_T *data) : _data(data) {
|
423
|
+
install_functions();
|
424
|
+
}
|
425
|
+
~FiniteStateMachine(){};
|
426
|
+
|
427
|
+
void set_timing_function(std::function<void()> timing_func) {
|
428
|
+
_timing_func = timing_func;
|
429
|
+
}
|
430
|
+
|
431
|
+
void add_state(<%= @prefix %>state_t name, state_fun func) { _states[name] = func; }
|
432
|
+
|
433
|
+
void add_transition(<%= @prefix %>state_t from, <%= @prefix %>state_t to, transition_fun func) {
|
434
|
+
_transitions[from][to] = func;
|
435
|
+
}
|
436
|
+
|
437
|
+
inline <%= @prefix %>state_t state() { return _state.second; }
|
438
|
+
inline std::string state_name() { return std::string(state_names[_state.second]); }
|
439
|
+
inline <%= @prefix %>state_t prev_state() { return _state.first; }
|
440
|
+
|
441
|
+
<%= @prefix %>state_t operator()(<%= @prefix %>state_t state) {
|
442
|
+
if (_states.find(state) == _states.end()) {
|
443
|
+
throw std::runtime_error("State not found: "s + state_names[state]);
|
444
|
+
}
|
445
|
+
return _states[state](*_data);
|
446
|
+
}
|
447
|
+
|
448
|
+
void operator()(<%= @prefix %>state_t from, <%= @prefix %>state_t to) {
|
449
|
+
if (_transitions.find(from) != _transitions.end()) {
|
450
|
+
if (_transitions[from].find(to) != _transitions[from].end()) {
|
451
|
+
_transitions[from][to](*_data);
|
287
452
|
}
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
453
|
+
}
|
454
|
+
}
|
455
|
+
|
456
|
+
// Run the FSM from a given state
|
457
|
+
void run(<%= @prefix %>state_t state, operation_fun operation = nullptr) {
|
458
|
+
<%= ns %>::<%= self.sigint %>_requested = false;
|
459
|
+
<%= @prefix %>state_t prev_state = state;
|
460
|
+
_state.first = state;
|
461
|
+
_state.second = state;
|
462
|
+
<% if sigint then -%>
|
463
|
+
std::signal(SIGINT, [](int signum) {
|
464
|
+
<% if log == :syslog then -%>
|
465
|
+
syslog(LOG_WARNING, "[FSM] SIGINT transition to <%= sigint %>");
|
466
|
+
<% end -%>
|
467
|
+
<%= ns %>::<%= self.sigint %>_requested = true;
|
468
|
+
});
|
469
|
+
<% end -%>
|
470
|
+
do {
|
471
|
+
(*this)(_state.first, _state.second);
|
472
|
+
if (operation) {
|
473
|
+
operation(*_data);
|
309
474
|
}
|
310
|
-
|
311
|
-
|
312
|
-
|
475
|
+
_state.first = _state.second;
|
476
|
+
_state.second = (*this)(_state.second);
|
477
|
+
if (_timing_func) {
|
478
|
+
_timing_func();
|
479
|
+
}
|
480
|
+
} while (_state.second != <%= @prefix.upcase %>STATE_<%= topology[:sinks][0].upcase %>);
|
481
|
+
// Call the exit state once more:
|
482
|
+
(*this)(<%= @prefix.upcase %>STATE_<%= topology[:sinks][0].upcase %>);
|
483
|
+
<% if sigint then -%>
|
484
|
+
std::signal(SIGINT, SIG_DFL);
|
485
|
+
<% end -%>
|
486
|
+
}
|
487
|
+
|
488
|
+
// Run the FSM from the initial state
|
489
|
+
void run(operation_fun operation = nullptr) { run(<%= @prefix.upcase %>STATE_<%= states[0][:id].upcase %>, operation); }
|
490
|
+
|
491
|
+
// install state and transition functions
|
492
|
+
void install_functions() {
|
493
|
+
|
494
|
+
// State functions
|
495
|
+
<% dest = destinations.dup -%>
|
496
|
+
<% @states.each do |s| -%>
|
497
|
+
<% stable = true if dest[s[:id]].include? s[:id] -%>
|
498
|
+
<% dest[s[:id]].map! {|n| (@prefix+"STATE_"+n).upcase} -%>
|
499
|
+
<% if dest[s[:id]].empty? or stable then
|
500
|
+
dest[s[:id]].unshift @prefix.upcase+"NO_CHANGE"
|
501
|
+
end -%>
|
502
|
+
add_state(<%= ns %>::<%= @prefix.upcase %>STATE_<%= s[:id].upcase %>, [](DATA_T &data) -> <%= ns %>::<%= @prefix %>state_t {
|
503
|
+
<% if log == :syslog then -%>
|
504
|
+
syslog(LOG_INFO, "[FSM] In state <%= s[:id].upcase %>");
|
505
|
+
<% end -%>
|
506
|
+
<%= ns %>::<%= @prefix %>state_t next_state = <%= @prefix%>do_<%= s[:id] %>(data);
|
507
|
+
|
508
|
+
switch (next_state) {
|
509
|
+
case <%= ns %>::<%= @prefix.upcase %>UNIMPLEMENTED:
|
510
|
+
throw std::runtime_error("State function not fully implemented: "s + "<%= s[:id].upcase %>");
|
511
|
+
break;
|
512
|
+
<% dest[s[:id]].each do |str| -%>
|
513
|
+
case <%= ns %>::<%= str %>:
|
514
|
+
<% end -%>
|
515
|
+
break;
|
516
|
+
default:
|
517
|
+
<% if log == :syslog then -%>
|
518
|
+
syslog(LOG_WARNING, "[FSM] Cannot pass from <%= s[:id] %> to %s, remaining in this state", state_names[next_state]);
|
519
|
+
<% end -%>
|
520
|
+
next_state = <%= ns %>::<%= @prefix.upcase %>NO_CHANGE;
|
521
|
+
}
|
522
|
+
<% if sigint && stable && self.topology[:sources][0] != s[:id] then -%>
|
523
|
+
// SIGINT transition override
|
524
|
+
if (<%= self.sigint %>_requested) next_state = <%= (@prefix+"STATE_"+self.sigint ).upcase %>;
|
525
|
+
<% end -%>
|
526
|
+
return next_state;
|
527
|
+
});
|
528
|
+
|
529
|
+
<% end -%>
|
530
|
+
|
531
|
+
<% if transition_functions_list.count > 0 then -%>
|
532
|
+
// Transition functions
|
533
|
+
<% transition_functions_list.each do |t| -%>
|
534
|
+
add_transition(<%= @prefix.upcase %>STATE_<%= transitions_paths[t][0][:from].upcase %>, <%= @prefix.upcase %>STATE_<%= transitions_paths[t][0][:to].upcase %>, [](DATA_T &data) {
|
535
|
+
<% if log == :syslog then -%>
|
536
|
+
syslog(LOG_INFO, "[FSM] State transition <%= t %>");
|
537
|
+
<% end -%>
|
538
|
+
<%= t %>(data);
|
539
|
+
});
|
540
|
+
|
541
|
+
<% end -%>
|
542
|
+
<% end -%>
|
543
|
+
}
|
544
|
+
|
545
|
+
}; // class FiniteStateMachine
|
546
|
+
|
547
|
+
}; // namespace <%= @project_name || "FSM" %>
|
548
|
+
|
549
|
+
#endif // <%= File::basename(@cname).upcase %>_HPP
|
550
|
+
|
551
|
+
EOHPP
|
552
|
+
|
553
|
+
|
554
|
+
# ____ ____ ____ ____ ___ _ _ ____ ____ _____
|
555
|
+
# / ___| _ \| _ \ / ___| / _ \| | | | _ \ / ___| ____|
|
556
|
+
# | | | |_) | |_) | \___ \| | | | | | | |_) | | | _|
|
557
|
+
# | |___| __/| __/ ___) | |_| | |_| | _ <| |___| |___
|
558
|
+
# \____|_| |_| |____/ \___/ \___/|_| \_\\____|_____|
|
559
|
+
|
560
|
+
|
561
|
+
|
562
|
+
CPP =<<~EOCPP
|
563
|
+
<% if @syslog then -%>
|
564
|
+
<% log = :syslog -%>
|
565
|
+
#include <syslog.h>
|
566
|
+
<% end -%>
|
567
|
+
#include "<%= File::basename(@cname) %>.hpp"
|
568
|
+
|
569
|
+
using namespace std;
|
570
|
+
<% ns = @project_name || "FSM" -%>
|
571
|
+
|
572
|
+
<% placeholder = "Your Code Here" %>
|
573
|
+
// SEARCH FOR <%= placeholder %> FOR CODE INSERTION POINTS!
|
574
|
+
|
575
|
+
|
576
|
+
namespace <%= @project_name || "FSM" %> {
|
577
|
+
|
578
|
+
/* ____ _ _
|
579
|
+
* / ___|| |_ __ _| |_ ___
|
580
|
+
* \\___ \\| __/ _` | __/ _ \\
|
581
|
+
* ___) | || (_| | || __/
|
582
|
+
* |____/ \\__\\__,_|\\__\\___|
|
583
|
+
*
|
584
|
+
* __ _ _
|
585
|
+
* / _|_ _ _ __ ___| |_(_) ___ _ __ ___
|
586
|
+
* | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
|
587
|
+
* | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
|
588
|
+
* |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
|
589
|
+
*/
|
590
|
+
<% dest = destinations.dup -%>
|
591
|
+
<% topo = self.topology -%>
|
592
|
+
<% @states.each do |s| -%>
|
593
|
+
<% stable = true if dest[s[:id]].include? s[:id] -%>
|
594
|
+
<% dest[s[:id]].map! {|n| (@prefix+"STATE_"+n).upcase} -%>
|
595
|
+
<% if dest[s[:id]].empty? or stable then
|
596
|
+
dest[s[:id]].unshift @prefix.upcase+"NO_CHANGE"
|
597
|
+
end %>
|
598
|
+
// Function to be executed in state STATE_<%= s[:id].upcase %>
|
599
|
+
// valid return states: <%= dest[s[:id]].join(", ") %>
|
600
|
+
<% if sigint && stable && topo[:sources][0] != s[:id] then -%>
|
601
|
+
// SIGINT triggers an emergency transition to STATE_<%= self.sigint.upcase %>
|
602
|
+
<% end -%>
|
603
|
+
template<class T>
|
604
|
+
<%= @prefix %>state_t <%= s[:function] %>(T &data) {
|
605
|
+
<%= @prefix %>state_t next_state = <%= ns %>::<%= @prefix.upcase %>UNIMPLEMENTED;
|
606
|
+
/* <%= placeholder %> */
|
607
|
+
|
608
|
+
return next_state;
|
609
|
+
}
|
610
|
+
<% end -%>
|
611
|
+
|
612
|
+
|
613
|
+
<% if transition_functions_list.count > 0 then -%>
|
614
|
+
/* _____ _ _ _
|
615
|
+
* |_ _| __ __ _ _ __ ___(_) |_(_) ___ _ __
|
616
|
+
* | || '__/ _` | '_ \\/ __| | __| |/ _ \\| '_ \\
|
617
|
+
* | || | | (_| | | | \\__ \\ | |_| | (_) | | | |
|
618
|
+
* |_||_| \\__,_|_| |_|___/_|\\__|_|\\___/|_| |_|
|
619
|
+
*
|
620
|
+
* __ _ _
|
621
|
+
* / _|_ _ _ __ ___| |_(_) ___ _ __ ___
|
622
|
+
* | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
|
623
|
+
* | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
|
624
|
+
* |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
|
625
|
+
*/
|
626
|
+
|
627
|
+
<% transition_functions_list.each do |t| -%>
|
628
|
+
<% next if t == "NULL" -%>
|
629
|
+
<% tpaths = transitions_paths[t] -%>
|
630
|
+
// This function is called in <%= tpaths.count %> transition<%= tpaths.count == 1 ? '' : 's' %>:
|
631
|
+
<% tpaths.each_with_index do |e, i| -%>
|
632
|
+
// <%= i+1 %>. from <%= e[:from] %> to <%= e[:to] %>
|
633
|
+
<% end -%>
|
634
|
+
template<class T>
|
635
|
+
void <%= t %>(T &data) {
|
636
|
+
/* <%= placeholder %> */
|
637
|
+
}
|
638
|
+
|
639
|
+
<% end -%>
|
640
|
+
<% end -%>
|
641
|
+
|
642
|
+
}; // namespace <%= @project_name || "FSM" %>
|
643
|
+
|
644
|
+
|
645
|
+
<% nsinks = topology[:sinks].count -%>
|
646
|
+
// Example usage:
|
647
|
+
#ifdef TEST_MAIN
|
648
|
+
#include <unistd.h>
|
649
|
+
#include <thread>
|
650
|
+
|
651
|
+
struct Data {
|
652
|
+
int count;
|
653
|
+
};
|
654
|
+
|
655
|
+
int main() {
|
656
|
+
Data data = {1};
|
657
|
+
auto fsm = <%= ns %>::FiniteStateMachine(&data);
|
658
|
+
fsm.set_timing_function([]() {
|
659
|
+
std::this_thread::sleep_for(std::chrono::seconds(1));
|
660
|
+
});
|
661
|
+
fsm.run([&](Data &s) {
|
662
|
+
std::cout << "State: " << fsm.state() << " data: " << s.count << std::endl;
|
663
|
+
});
|
664
|
+
return 0;
|
665
|
+
}
|
666
|
+
#endif // TEST_MAIN
|
667
|
+
EOCPP
|
668
|
+
|
313
669
|
end
|
314
670
|
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.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paolo Bosetti
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-graphviz
|