gv_fsm 0.0.1

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 (5) hide show
  1. checksums.yaml +7 -0
  2. data/bin/gv_fsm +19 -0
  3. data/lib/gv_fsm.rb +80 -0
  4. data/lib/templates.rb +136 -0
  5. metadata +61 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6e22ea784bd92deabd21520f674e347e1edbebcdb3f1acba0256b62994c97a0f
4
+ data.tar.gz: ab0a2f310beed50a681582ce1b64fd32edb49ab5329150e7f152dbd8d6e99daf
5
+ SHA512:
6
+ metadata.gz: 9ee624e62959b91f95cf9d5ac3aa02a7172424f8738ea615bb8193aec2631a6a4d31354c60a9958470f9198105406c65f3658c3191d60c345ec0a4a799c76abe
7
+ data.tar.gz: da45ed138e0ae9d2f54937614ad91bd903aeac8b352761eb0c996b8888ad2079a73b280c09a4185d3dadde6d60fb4045723fd8156d589abd6ce535dfe8b6a8ae
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "gv_fsm"
4
+
5
+ SM = FSM.new(ARGV[0])
6
+ SM.project_name = "test project"
7
+ SM.description = "FSM designed to test the generator"
8
+ SM.cname = "test_fsm"
9
+
10
+ File.open("#{SM.cname}.h", "w") do |f|
11
+ f.puts ERB.new(Templates::HEADER, 0, "<>").result
12
+ f.puts ERB.new(Templates::HH, 0, "<>").result
13
+ end
14
+
15
+ File.open("#{SM.cname}.c", "w") do |f|
16
+ f.puts ERB.new(Templates::HEADER, 0, "<>").result
17
+ f.puts ERB.new(Templates::CC, 0, "<>").result
18
+ end
19
+
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ruby-graphviz'
4
+ require 'erb'
5
+
6
+ require './templates.rb'
7
+
8
+ class FSM
9
+ attr_reader :states, :transitions, :dotfile
10
+ attr_accessor :project_name, :description, :cname
11
+
12
+ def initialize(filename)
13
+ parse(filename)
14
+ end
15
+
16
+ def parse(filename)
17
+ @dotfile = filename
18
+ @states = []
19
+ @transitions = []
20
+ GraphViz.parse(filename) do |g|
21
+ g.each_node do |id|
22
+ n = g.get_node(id)
23
+ label = n[:label].source.empty? ? "do_#{id}" : n[:label].source
24
+ @states << {id: id, function: label}
25
+ end
26
+ g.each_edge do |e|
27
+ from = e.node_one
28
+ to = e.node_two
29
+ case e[:label].source
30
+ when ""
31
+ label = nil
32
+ when /[#]/
33
+ label = "#{from}_to_#{to}"
34
+ else
35
+ label = e[:label].source
36
+ end
37
+ @transitions << {from: from, to: to, function: label}
38
+ end
39
+ end
40
+ end
41
+
42
+ def state_functions_list
43
+ @states.map {|s| s[:function]}
44
+ end
45
+
46
+ def states_list
47
+ @states.map {|s| s[:id]}
48
+ end
49
+
50
+ def transition_functions_list
51
+ lst = []
52
+ @transitions.each do |t|
53
+ if !lst.include? t[:function] then
54
+ lst << (t[:function] or "NULL")
55
+ end
56
+ end
57
+ return lst
58
+ end
59
+
60
+ def transitions_map
61
+ idx = {}
62
+ map = Array.new(@states.count)
63
+ map.map! {|e| e = Array.new(@states.count, "NULL")}
64
+ states_list.each_with_index {|s, i| idx[s] = i }
65
+ @transitions.each do |t|
66
+ map[idx[t[:from]]][idx[t[:to]]] = (t[:function] or "NULL")
67
+ end
68
+ map
69
+ end
70
+
71
+ def destinations
72
+ dest = Hash[states_list.map {|x| [x, []]}]
73
+ @transitions.each do |t|
74
+ dest[t[:from]] = [] unless dest[t[:from]]
75
+ dest[t[:from]] << t[:to]
76
+ end
77
+ return dest
78
+ end
79
+ end
80
+
@@ -0,0 +1,136 @@
1
+
2
+ module Templates
3
+ HEADER =<<~EOHEADER
4
+ // Finite State Machine
5
+ // Project: <%= SM.project_name or SM.dotfile %>
6
+ // Description: <%= SM.description or "<none given>" %>
7
+ //
8
+ // Generation date: <%= Time.now %>
9
+ // Generated from: <%= SM.dotfile %>
10
+ // The finite state machine has:
11
+ // <%= SM.states.count %> states
12
+ // <%= SM.transitions.count %> transitions
13
+
14
+ EOHEADER
15
+
16
+ HH =<<~EOH
17
+ #ifndef <%= SM.cname.upcase %>_H
18
+ #define <%= SM.cname.upcase %>_H
19
+ #include <stdlib.h>
20
+
21
+ // List of states
22
+ typedef enum {
23
+ <% SM.states.each_with_index do |s, i| %>
24
+ STATE_<%= s[:id].upcase %><%= i == 0 ? " = 0" : "" %>,
25
+ <% end %>
26
+ NUM_STATES,
27
+ NO_CHANGE
28
+ } state_t;
29
+
30
+ const char *state_names[] = {<%= SM.states_list.map {|sn| '"'+sn+'"'}.join(", ") %>};
31
+
32
+ // State function and state transition prototypes
33
+ typedef state_t state_func_t(void *data);
34
+ typedef void transition_func_t(void *data);
35
+
36
+ // state functions
37
+ <% SM.states.each do |s| %>
38
+ state_t <%= s[:function] %>(void *data);
39
+ <% end %>
40
+
41
+ // transition functions
42
+ <% SM.transition_functions_list.each do |t| %>
43
+ <% next if t == "NULL" %>
44
+ void <%= t %>(void *data);
45
+ <% end %>
46
+
47
+ // List of state functions
48
+ state_func_t *const state_table[NUM_STATES] = {
49
+ <%= SM.state_functions_list.join(",\n ")%>
50
+ };
51
+
52
+ // Table of transition functions
53
+ transition_func_t *const transition_table[NUM_STATES][NUM_STATES] = {
54
+ <% sl = SM.states_list %>
55
+ <% fw = SM.transition_functions_list.max {|a, b| a.length <=> b.length}.length %>
56
+ <% sw = SM.states_list.max {|a, b| a.length <=> b.length}.length %>
57
+ /* <%= "states:".ljust(sw) %> <%= sl.map {|e| e.ljust(fw) }.join(", ") %> */
58
+ <% SM.transitions_map.each_with_index do |l, i| %>
59
+ /* <%= sl[i].ljust(sw) %> */ {<%= l.map {|e| e.ljust(fw)}.join(", ") %>},
60
+ <% end %>
61
+ };
62
+
63
+ // state manager
64
+ state_t run_state(state_t cur_state, void *data);
65
+
66
+ #endif
67
+ EOH
68
+
69
+ CC =<<~EOC
70
+ #include <syslog.h>
71
+ #include "<%= SM.cname %>.h"
72
+
73
+ // State functions
74
+ <% dest = SM.destinations.dup %>
75
+ <% SM.states.each do |s| %>
76
+ <% stable = true if dest[s[:id]].include? s[:id] %>
77
+ <% dest[s[:id]].map! {|n| "STATE_"+n.upcase} %>
78
+ <% if dest[s[:id]].empty? or stable then
79
+ dest[s[:id]].unshift "NO_CHANGE"
80
+ end %>
81
+ state_t <%= s[:function] %>(void *data) {
82
+ state_t next_state = <%= dest[s[:id]].first %>;
83
+
84
+ syslog(LOG_INFO, "[FSM] In state <%= s[:id] %>");
85
+ /* Your code here */
86
+
87
+ // valid return states: <%= dest[s[:id]].join(", ") %>
88
+ switch (next_state) {
89
+ <% dest[s[:id]].each do |str| %>
90
+ case <%= str %>:
91
+ <% end %>
92
+ break;
93
+ default:
94
+ syslog(LOG_WARNING, "[FSM] Cannot pass from <%= s[:id] %> to %s, remaining in this state", state_names[next_state]);
95
+ next_state = NO_CHANGE;
96
+ }
97
+ return next_state;
98
+ }
99
+
100
+ <% end %>
101
+
102
+ // Transition functions
103
+ <% SM.transition_functions_list.each do |t| %>
104
+ <% next if t == "NULL" %>
105
+ void <%= t %>(void *data) {
106
+ syslog(LOG_INFO, "[FSM] State transition <%= t %>");
107
+ /* Your code here */
108
+ }
109
+
110
+ <% end %>
111
+
112
+ // State manager
113
+ state_t run_state(state_t cur_state, void *data) {
114
+ state_t new_state = state_table[cur_state](data);
115
+ transition_func_t *transition = transition_table[cur_state][new_state];
116
+ if (transition)
117
+ transition(data);
118
+ return new_state == NO_CHANGE ? cur_state : new_state;
119
+ };
120
+
121
+
122
+ #ifdef TEST_MAIN
123
+ #include <unistd.h>
124
+ int main() {
125
+ state_t cur_state = STATE_INIT;
126
+ openlog("SM", LOG_PID | LOG_PERROR, LOG_USER);
127
+ syslog(LOG_INFO, "Starting SM");
128
+ do {
129
+ cur_state = run_state(cur_state, NULL);
130
+ sleep(1);
131
+ } while (cur_state != STATE_STOP);
132
+ return 0;
133
+ }
134
+ #endif
135
+ EOC
136
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gv_fsm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Paolo Bosetti
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-08-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ruby-graphviz
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.2.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.2.5
27
+ description: A C/C++ code generator that creates code for a finite state machine given
28
+ a description in graphviz language.
29
+ email: paolo.bosetti@unitn.it
30
+ executables:
31
+ - gv_fsm
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - bin/gv_fsm
36
+ - lib/gv_fsm.rb
37
+ - lib/templates.rb
38
+ homepage: https://rubygems.org/gems/gv_fsm
39
+ licenses:
40
+ - MIT
41
+ metadata: {}
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubygems_version: 3.1.2
58
+ signing_key:
59
+ specification_version: 4
60
+ summary: Graphviz to Finite state machine
61
+ test_files: []