Class: Cosmos::TableConfig

Inherits:
Object show all
Defined in:
lib/cosmos/tools/table_manager/table_config.rb

Overview

TableConfig provides capabilities to read an ascii file that defines the table parameters in a system and create a set of Tables for each.

Instance Method Summary collapse

Constructor Details

#initializeTableConfig

Constructor for a TableConfig



21
22
23
24
25
26
27
28
29
30
31
# File 'lib/cosmos/tools/table_manager/table_config.rb', line 21

def initialize
  @ordered_tables = nil
  @tables = nil
  @current_name = nil
  @current_table = nil
  @current_parameter = nil
  @cur_bit_offset = 0
  @default_count = 0
  @item_definitions = nil
  @table_names = nil
end

Instance Method Details

#commit_default_values(table) ⇒ Object

Update all default values in the definition file on disk with current values



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/cosmos/tools/table_manager/table_config.rb', line 294

def commit_default_values(table)
  new_file_data = ""
  table_found = false
  default_count = 0
  table_parameters = []
  parser = ConfigParser.new
  parser.parse_file(table.filename) do |keyword, parameters|
    line = parser.line

    case keyword
    when 'TABLE'
      name = parameters[0].remove_quotes
      if name == table.name
        table_found = true
      else
        table_found = false
      end

    when 'PARAMETER'
      if table_found
        name = parameters[0].remove_quotes
        if table.type == :ONE_DIMENSIONAL
          item = table.get_item(name)

          # update the default value (in case the user tries to reset default values)
          item.default = table.read(name, :RAW)

          # determine what the new default value will look like as printed in the DEF file
          new_default = item_to_def_string(table, item)

          # scan to the beginning of the default value
          line_index = line.index(keyword) # skip to start of keyword
          line_index = line.index(parameters[0], line_index+keyword.length) # skip to start of first param
          (1..7).each do |index|
            line_index = line.index(parameters[index], line_index+parameters[index-1].length) # skip to start of next param
          end

          # rebuild the line in 3 parts:
          # everything up to the old default value
          # the new default value
          # everything after the old default value
          line = line[0...line_index] + new_default + line[(line_index+parameters[7].length)..-1]
        else
          table_parameters << name
        end
      end

    when 'DEFAULT'
      if table_found
        line_index = line.index(keyword)
        previous_word = keyword
        table_parameters.each_with_index do |param_name, index|
          item_name = "#{param_name}#{default_count}"
          item = table.get_item(item_name)

          # update the default value (in case the user tries to reset default values)
          item.default = table.read(item_name, :RAW)

          # determine what the new default value will look like as printed in the DEF file
          new_default = item_to_def_string(table, item)
          if new_default.index(" ")
            new_default = '"' + new_default + '"'
          end

          # scan to the beginning of the default value
          line_index = line.index(parameters[index], line_index+previous_word.length)

          # rebuild the line in 3 parts:
          # everything up to the old default value
          # the new default value
          # everything after the old default value
          line = line[0...line_index] + new_default + line[(line_index+parameters[index].length)..-1]

          # update previous_word so that we can find the next value
          previous_word = new_default
        end

        # count the row so that we know what the item_name is next time we see DEFAULT
        default_count += 1
      end
    end

    new_file_data << (line + "\n")

  end # end loop

  # ok, now replace the old def file with the new one
  File.open(table.filename, "w") do |file|
    file.puts new_file_data
  end
end

#format_hex(table, item_def) ⇒ Object

# Return the entire table_definition file for the given table name as a string

def print_table(name)
  tdef = @tables[name]
  tstr = ""
  if tdef.table_id.class != Array
    tstr << "TABLE \"#{tdef.name}\" \"#{tdef.description}\" #{convert_table_type(tdef.type)} #{convert_endianness(tdef.get_endianness)} #{tdef.table_id}\n"
  else
    tstr << "TABLE \"#{tdef.name}\" \"#{tdef.description}\" #{convert_table_type(tdef.type)} #{convert_endianness(tdef.get_endianness)} #{tdef.table_id.join(' ')}\n"
  end
  if tdef.type == :ONE_DIMENSIONAL
    tdef.sorted_items.each do |item|
      tstr << create_item_string(item, tdef.type)
    end
  else # TWO_DIMENSIONAL
    tdef.num_columns.times do |column|
      item_def = tdef.sorted_items[column]
      tstr << create_item_string(item_def, tdef.type)
    end

    index = 0
    tdef.sorted_items.each do |item|
      if index % tdef.num_columns == 0
        tstr << "\n  DEFAULT "
      end
      case item.display_type
        when :DEC
          default = item.default
        when :STATE
          default = "\"#{item.default}\""
        when :HEX
          default = "0x#{item.default.to_s(16)}"
      end
      tstr << "#{default} "
      index += 1
    end
  end # end else # TWO_DIMENSIONAL
  tstr
end # end print_table(name)


425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
# File 'lib/cosmos/tools/table_manager/table_config.rb', line 425

def format_hex(table, item_def)
  case item_def.bit_size
    when 8
      x = sprintf("%02X", table.read(item_def.name).to_s)
      # if the number was negative x will have .. and possibly another
      # F in the string which we remove by taking the last 4 digits
      x = /\w{2}$/.match(x)[0]
    when 16
      x = sprintf("%04X", table.read(item_def.name).to_s)
      # if the number was negative x will have .. and possibly another
      # F in the string which we remove by taking the last 4 digits
      x = /\w{4}$/.match(x)[0]
    else
      x = sprintf("%08X", table.read(item_def.name).to_s)
      # if the number was negative x will have .. and possibly another
      # F in the string which we remove by taking the last 8 digits
      x = /\w{8}$/.match(x)[0]
  end
  return "0x%X" % Integer("0x#{x}") # convert to Integer
end

#get_all_tablesObject

Returns an array of all the Tables in the definition file



263
264
265
# File 'lib/cosmos/tools/table_manager/table_config.rb', line 263

def get_all_tables
  @ordered_tables
end

#get_table(name) ⇒ Object

Returns a specific Table



258
259
260
# File 'lib/cosmos/tools/table_manager/table_config.rb', line 258

def get_table(name)
  @tables[name]
end

#get_table_namesObject

Returns an array of all the table names in the definition file



268
269
270
# File 'lib/cosmos/tools/table_manager/table_config.rb', line 268

def get_table_names
  @table_names
end

#item_to_def_string(table, item_def) ⇒ Object

Returns a default-value representation of an item to be printed in a DEF file



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/cosmos/tools/table_manager/table_config.rb', line 273

def item_to_def_string(table, item_def)
  result = ""
  case item_def.display_type
  when :STATE
    if table.type == :ONE_DIMENSIONAL
      result = table.read(item_def.name, :RAW).to_s
    else
      result = table.read(item_def.name).to_s
    end
  when :DEC, :STRING, :NONE
    result = table.read(item_def.name).to_s
  when :CHECK
    result = table.read(item_def.name, :RAW).to_s
  when :HEX
    result = format_hex(table, item_def)
  end

  return result
end

#process(filename) ⇒ Object

Processes a file and adds in the tables defined in the file



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
# File 'lib/cosmos/tools/table_manager/table_config.rb', line 34

def process(filename)
  building_generic_conversion = false
  converted_type = nil
  converted_bit_size = nil
  proc_text = ''

  Logger.info "Processing table config in file '#{filename}'"

  unless test ?f, filename
    Logger.error "File does not exist"
    raise "ERROR! Table config file #{filename} does not exist!"
  end

  parser = ConfigParser.new
  parser.parse_file(filename) do |keyword, parameters|
    next unless keyword
    if building_generic_conversion
      case keyword
      # Complete a generic conversion
      when 'GENERIC_READ_CONVERSION_END', 'GENERIC_WRITE_CONVERSION_END', 'CONSTRAINT_END'
        parser.verify_num_parameters(0, 0, keyword)
        @current_parameter.read_conversion =
          GenericConversion.new(proc_text,
                                converted_type,
                                converted_bit_size) if keyword.include? "READ"
        @current_parameter.write_conversion =
          GenericConversion.new(proc_text,
                                converted_type,
                                converted_bit_size) if keyword.include? "WRITE"
        @current_parameter.constraint =
          GenericConversion.new(proc_text,
                                converted_type,
                                converted_bit_size) if keyword.include? "CONSTRAINT"
        building_generic_conversion = false
      # Add the current config.line to the conversion being built
      else
        proc_text << parser.line << "\n"
      end # case keyword

    else # not building generic conversion

      case keyword
      # Start the definition of a generic conversion.
      # All config.lines following this config.line are considered part
      # of the conversion until an end of conversion marker is found
      when 'GENERIC_READ_CONVERSION_START', 'GENERIC_WRITE_CONVERSION_START', 'CONSTRAINT_START'
        usage = "#{keyword} <Converted Type (optional)> <Converted Bit Size (optional)>"
        parser.verify_num_parameters(0, 2, usage)
        proc_text = ''
        building_generic_conversion = true
        converted_type = nil
        converted_bit_size = nil
        if parameters[0]
          converted_type = parameters[0].upcase.intern
          raise parser.error("Invalid converted_type: #{converted_type}.") unless [:INT, :UINT, :FLOAT, :STRING, :BLOCK].include? converted_type
        end
        converted_bit_size = Integer(parameters[1]) if parameters[1]

      when 'TABLEFILE'
        parser.verify_num_parameters(1, 1, "#{keyword} <File name>")
        process(File.join(File.dirname(filename), parameters[0]))

      when 'TABLE'
        usage = "TABLE <Table Name> <Table Description> <ONE_DIMENSIONAL or TWO_DIMENSIONAL> <BIG_ENDIAN or LITTLE_ENDIAN> <Identifier>"
        parser.verify_num_parameters(5, nil, usage)
        begin
          # locals declared for readability
          name = parameters[0]
          description = parameters[1]
          type = read_table_type(parameters[2])
          endianness = read_endianness(parameters[3])
          if parameters[5].nil?
            table_id = Integer(parameters[4])
          else
            table_id = []
            parameters[4..-1].each {|parameter| table_id << Integer(parameter)}
          end

          start_new_table(name, description, type, endianness, table_id, filename)
        rescue ArgumentError => err
          raise parser.error("#{err.message} with #{keyword}.\nUSAGE: #{usage}")
        end

      when 'PARAMETER'
        usage = "PARAMETER <Parameter Name> <Parameter Description> <Data Type> <Bit Size> <Display Type> <Minimum Value> <Maximum Value> <Default Value - Only in ONE_DIMENTIONAL>"
        parser.verify_num_parameters(7, 8, usage)
        finish_parameter()
        if @current_table
          @current_table.num_rows += 1

          begin
            if @current_table.type == :TWO_DIMENSIONAL
              parameters[0] = "#{parameters[0]}0"
            end
            # locals declared for readability
            name = parameters[0]
            if @current_table.items[name.upcase]
              raise ArgumentError, "The name \"#{name}\" was already defined"
            end
            description = parameters[1]
            type = read_item_type(parameters[2])
            bit_size = parameters[3].to_i
            display_type, editable = read_display_type(parameters[4], type)
            if type == :BLOCK
              range = nil
            elsif type == :STRING
              range = convert_to_range(parameters[5], parameters[6], :UINT, 0)
            else
              range = convert_to_range(parameters[5], parameters[6], type, bit_size)
            end
            if @current_table.type == :ONE_DIMENSIONAL
              default = convert_to_type(parameters[7], type)
            else # TWO_DIMENSIONAL defaults are set by the DEFAULT keyword
              default = 0
            end

            @current_parameter = @current_table.create_param(name, @cur_bit_offset,
              bit_size, type, description, range, default, display_type, editable)
            @cur_bit_offset += parameters[3].to_i
          rescue ArgumentError => err
            raise parser.error("#{err.message} with #{keyword}.\nUSAGE: #{usage}")
          end
        end

      when 'STATE'
        usage = "STATE <Key> <Value>"
        parser.verify_num_parameters(2, 2, usage)
        begin
          # locals declared for readability
          state_name = parameters[0]
          state_value = convert_to_type(parameters[1], @current_parameter.data_type)

          @current_parameter.states ||= {}
          @current_parameter.states[state_name.upcase] = state_value
        rescue ArgumentError => err
          raise parser.error("#{err.message} with #{keyword}.\nUSAGE: #{usage}")
        end

      when 'DEFAULT'
        usage = "DEFAULT <Value1> <Value2> ... <ValueN>"
        # if this is our first default value for a TWO_DIMENSIONAL table
        # then we redefine the default values
        if @default_count == 0
          @item_definitions = Array.new(@current_table.sorted_items)
          index = 0
          @item_definitions.each do |item_def|
            set_item_default_value(item_def, parameters[index])
            index += 1
          end

          @current_table.num_rows = 1

        # more default values have been given so copy all the parameters from
        # the first row and reset the defaults
        else
          index = 0

          @item_definitions.each do |item_def|
            new_item = @current_table.duplicate_item(item_def,
                                                            @default_count,
                                                            @cur_bit_offset)
            @cur_bit_offset += item_def.bit_size
            set_item_default_value(new_item, parameters[index])
            index += 1
          end
          @current_table.num_rows += 1
        end # end else for if @default_count == 1

        @default_count += 1

      when 'POLY_READ_CONVERSION'
        parser.verify_num_parameters(2, nil, "#{keyword} <C0> ... <CX>")
        @current_parameter.read_conversion = PolynomialConversion.new(parameters[0..-1])

      when 'POLY_WRITE_CONVERSION'
        parser.verify_num_parameters(2, nil, "#{keyword} <C0> ... <CX>")
        @current_parameter.write_conversion = PolynomialConversion.new(parameters[0..-1])

      else
        unknown_keyword(parser, keyword, parameters)
      end # end case parameters[0]
    end # end else for generic conversion
  end # end loop

  # Handle last packet
  finish_table()

ensure
  file.close if defined? file and file
end

#set_item_default_value(item_def, default = nil) ⇒ Object

Sets the given item definition’s default parameter to the default value given. This function does conversions based on the display_type of the item.



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/cosmos/tools/table_manager/table_config.rb', line 227

def set_item_default_value(item_def, default = nil)
  begin
    # If no default was passed use the definition default
    if default == nil
      default = item_def.default
    end
    if item_def.display_type == :STATE
      begin
        val = item_def.states.key(Integer(default))
      rescue
        val = default
      end
      @current_table.write(item_def.name, val)
      item_def.default = @current_table.read(item_def.name, :RAW)
    else
      item_def.default = convert_to_type(default, item_def.data_type)
    end
  rescue
    Logger.error "Setting #{item_def.name} to #{default} failed! Using #{item_def.range.first} for the default value."
    item_def.default = item_def.range.first
  end
end

#unknown_keyword(parser, keyword, parameters) ⇒ Object

Classes extending table_definition should override this function to add new keyword processing. Ensure super() is called after processing new keywords so the default behavior of raising an ArgumentError is maintained.



253
254
255
# File 'lib/cosmos/tools/table_manager/table_config.rb', line 253

def unknown_keyword(parser, keyword, parameters)
  raise parser.error("Unknown keyword '#{keyword}'.", nil) if keyword
end