Class: RPC

Inherits:
Object
  • Object
show all
Defined in:
lib/netlinx/erb/rpc.rb

Overview

:nodoc:

Class Method Summary collapse

Class Method Details

.buildObject

:nodoc:

Raises:

  • (Errno::ENOENT)


8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/netlinx/erb/rpc.rb', line 8

def self.build
  fn_exp = /
  (?#
    Pull out comment\\description above the function, enclosed in slash\\star syntax.
    Does not have to exist.
  )
  ^(?<desc>[\t ]*\/\*(?:[^\*]|\*[^\/])*\*\/)?\s*
  
  (?# Find the function definition. )
  define_function\s+
  
  (?# Capture function's return type, if it exists.)
  (?<rtn>\w+(?<width>\[\d+\])?)??\s*
  
  (?# Capture the function name. )
  (?<name>\w+)
  
  (?#
    Capture the function parameters.
    Run this through another regex to get the type\\name pairs.
  )
  \(\s*(?<params>.*?)\s*\)\s*
  
  (?# Capture the function's source code. )
  {[\r\n]*(?<code>(?:.|\r|\n)*?)?[\r\n]*}
  /x
  
  param_exp = /\s*(?:(?<type>\w+)\s+(?<name>\w+(?<width>\[\d*\])?)),?\s*/
  
  sections = {} # Collect a set of matches for each file, separated by file.
  
  
  # Pull file list from workspace.
  workspace = NetLinx::Workspace.search
  raise Errno::ENOENT, 'Workspace not found.' unless workspace
  
  file_paths = workspace.projects.first.systems.first.files
    .map(&:path)
    .select { |path| path =~ /(\.axi|\.axs)$/ }
    .reject { |path| path =~ /rpc(?:-|_.*?)?\.axi/ } # Remove RPC files.
  
  # file_paths = Dir['**/*.axi']
  
  file_paths.each do |f|
    str = File.open(f.gsub('\\', '/'), "r:iso-8859-1").read
    matches = []
    
    while str =~ fn_exp
      matches << $~
      str = $'
    end
    
    sections[f] = matches
  end
  
  # -----------------------
  # Documentation Generator
  # -----------------------
  
  # output = ''
  # sections.each do |name, matches|
  
  #   output << "--------------------------------------------------\n"
  #   output << "FILE: '#{name}'\n"
  #   output << "--------------------------------------------------\n"
  #   output << "\n\n"
  
  #   matches.each do |m|
  #     output << m[:desc].to_s
  #     output << "\n"
  #     output << m[:name].to_s
  #     output << "\n\n\n"
  #   end
  
  # end
  
  # File.open('functions.axi', 'w+') { |f| f << output }
  
  
  # ----------------------
  # RPC Function Generator
  # ----------------------
  
  # Generate list of included and excluded files for sanity check.
  directory_files = Dir['**/*.axi'] + Dir['**/*.axs']
  
  included_files  = ''
  file_paths.each { |path| included_files << path.to_s.gsub('\\', '/') + "\n" } # TODO: As string.
  
  excluded_files  = ''
  (directory_files - file_paths.map { |path| path.gsub '\\', '/' }).each { |path| excluded_files << path.to_s.gsub('\\', '/') + "\n" }
  
  fn_symbols = [] # Symbol names to avoid duplicates.
  output = ''
  
  output << <<-EOS
(***********************************************************)
(*                         WARNING                         *)
(***********************************************************)
(*  This file is automatically generated.                  *)
(***********************************************************)

/*
Included Files:
---------------
#{included_files}

Excluded Files:
---------------
#{excluded_files}
*/


#if_not_defined RPC_FUNCTION_LIST
#define RPC_FUNCTION_LIST 1

DEFINE_EVENT

data_event[vdvRPC]
{
  string:
  {
      char f_name[255];
      f_name = rpc_function_name(data.text);
      
EOS

  sections.each do |name, matches|
    output << "        /*------------------------------------------------------------------/\n"
    output << "            FILE: '#{name}'\n"
    output << "        /------------------------------------------------------------------*/\n\n"
    
    
    matches.each do |fn|
      function_valid = true
      fn_output      = ''
      return_type    = fn[:rtn].nil? ? nil : fn[:rtn].downcase.to_sym
      # TODO: Calculate return value width.
      params         = []
      
      # Store function name as symbol and check for duplicates.
      fn_sym = fn[:name].downcase.to_sym
      
      if fn_symbols.include? fn_sym
        output << "        // Already defined.\n"
        function_valid = false
      else
        fn_symbols << fn_sym
      end
      
      # Retrieve params.
      str = fn[:params]
      while str =~ param_exp
        params << $~
        str = $'
      end
      
      # Generate function handler.
      fn_output << "        if(compare_string(f_name, '#{fn[:name].downcase}'))\n"
      fn_output << "        {\n"
      
      # Generate return value.
      if return_type
        case return_type
        when :integer
          fn_output << "            #{return_type.to_s} return_value;\n"
        end
        
        fn_output << "            \n"
      end
      
      fn_output << "            print(LOG_LEVEL_INFO, 'RPC: #{fn[:name]}()');\n"
      fn_output << "            \n"
      
      # Set return value equal to function if return value exists.
      fn_output << "            "
      fn_output << "return_value = " if return_type
      
      fn_output << "#{fn[:name]}("
      fn_output << ");\n" if params.empty?
      
      function_valid = false unless [nil, :integer].include? return_type
      
      # Generate parameters.
      param_index = 0
      params.each do |param|
        param_index += 1
        
        valid_params = [:integer]
        type = param[:type].downcase.to_sym
        
        unless valid_params.include? type
          function_valid = false
          break
        end
        
        case type
        when :integer
          fn_output << "\n                rpc_get_arg_i(#{param_index}, data.text),"
        end
      end
      
      # Remove trailing comma from last arg.
      fn_output.chop! unless params.empty?
      
      # Close function.
      fn_output << "\n            );\n" unless params.empty?
      
      # Print return value if exists.
      if return_type
        fn_output << "            \n"
        
        case return_type
        when :integer
          fn_output << "            print(LOG_LEVEL_INFO, \"'RPC RTN: ', itoa(return_value)\");\n"
        end
      end
      
      fn_output << "        }\n\n"
      
      # Store function string.
      if function_valid
        output << fn_output 
      else
        output << "        // Skipped:\n"
        output << "        // #{fn[:name]}(#{fn[:params]})\n\n"
      end
    end
    
  end
  
  output << "    }\n"
  output << "}\n"
  output << "#end_if\n\n"
  
  
  File.open('include/rpc-functions.axi', 'w+') { |f| f << output }
end