Class: OpenC3::PacketConfig

Inherits:
Object show all
Defined in:
lib/openc3/packets/packet_config.rb

Overview

Reads a command or telemetry configuration file and builds a hash of packets.

Direct Known Subclasses

TableConfig

Constant Summary collapse

COMMAND =
"Command"
TELEMETRY =
"Telemetry"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePacketConfig

Returns a new instance of PacketConfig.



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/openc3/packets/packet_config.rb', line 92

def initialize
  @name = nil
  @telemetry = {}
  @commands = {}
  @limits_groups = {}
  @limits_sets = [:DEFAULT]
  # Hash of Hashes. First index by target name and then item name.
  # Returns an array of packets with that target and item.
  @latest_data = {}
  @warnings = []
  @cmd_id_value_hash = {}
  @tlm_id_value_hash = {}

  # Create unknown packets
  @commands['UNKNOWN'] = {}
  @commands['UNKNOWN']['UNKNOWN'] = Packet.new('UNKNOWN', 'UNKNOWN', :BIG_ENDIAN)
  @telemetry['UNKNOWN'] = {}
  @telemetry['UNKNOWN']['UNKNOWN'] = Packet.new('UNKNOWN', 'UNKNOWN', :BIG_ENDIAN)

  reset_processing_variables()
end

Instance Attribute Details

#cmd_id_value_hashHash<String>=>Hash<Array>=>Packet (readonly)

that returns a hash keyed by an array of id values. The id values resolve to the packet defined by that identification. Command version

Returns:



79
80
81
# File 'lib/openc3/packets/packet_config.rb', line 79

def cmd_id_value_hash
  @cmd_id_value_hash
end

#commandsHash<String=>Packet> (readonly)

Returns Hash of all the command packets keyed by the packet name.

Returns:

  • (Hash<String=>Packet>)

    Hash of all the command packets keyed by the packet name.



54
55
56
# File 'lib/openc3/packets/packet_config.rb', line 54

def commands
  @commands
end

#languageString (readonly)

Returns Language of current target (ruby or python).

Returns:

  • (String)

    Language of current target (ruby or python)



87
88
89
# File 'lib/openc3/packets/packet_config.rb', line 87

def language
  @language
end

#latest_dataHash<String=>Hash<String=>Array(Packet)>> (readonly)

Returns Hash of hashes keyed first by the target name and then by the item name. This results in an array of packets containing that target and item. This structure is used to perform lookups when the packet and item are known but the packet is not.

Returns:

  • (Hash<String=>Hash<String=>Array(Packet)>>)

    Hash of hashes keyed first by the target name and then by the item name. This results in an array of packets containing that target and item. This structure is used to perform lookups when the packet and item are known but the packet is not.



74
75
76
# File 'lib/openc3/packets/packet_config.rb', line 74

def latest_data
  @latest_data
end

#limits_groupsHash<String=>Array(String, String, String)> (readonly)

Returns Hash of all the limits groups keyed by the group name. The value is a three element array consisting of the target_name, packet_name, and item_name.

Returns:

  • (Hash<String=>Array(String, String, String)>)

    Hash of all the limits groups keyed by the group name. The value is a three element array consisting of the target_name, packet_name, and item_name.



59
60
61
# File 'lib/openc3/packets/packet_config.rb', line 59

def limits_groups
  @limits_groups
end

#limits_setsArray<Symbol> (readonly)

Returns The defined limits sets for all items in the packet. This will always include :DEFAULT.

Returns:

  • (Array<Symbol>)

    The defined limits sets for all items in the packet. This will always include :DEFAULT.



63
64
65
# File 'lib/openc3/packets/packet_config.rb', line 63

def limits_sets
  @limits_sets
end

#nameString

Returns The name of this configuration. To be used by higher level classes to store information about the current PacketConfig.

Returns:

  • (String)

    The name of this configuration. To be used by higher level classes to store information about the current PacketConfig.



46
47
48
# File 'lib/openc3/packets/packet_config.rb', line 46

def name
  @name
end

#telemetryHash<String=>Packet> (readonly)

Returns Hash of all the telemetry packets keyed by the packet name.

Returns:

  • (Hash<String=>Packet>)

    Hash of all the telemetry packets keyed by the packet name.



50
51
52
# File 'lib/openc3/packets/packet_config.rb', line 50

def telemetry
  @telemetry
end

#tlm_id_value_hashHash<String>=>Hash<Array>=>Packet (readonly)

that returns a hash keyed by an array of id values. The id values resolve to the packet defined by that identification. Telemetry version

Returns:



84
85
86
# File 'lib/openc3/packets/packet_config.rb', line 84

def tlm_id_value_hash
  @tlm_id_value_hash
end

#warningsArray<String> (readonly)

Returns Array of strings listing all the warnings that were created while parsing the configuration file.

Returns:

  • (Array<String>)

    Array of strings listing all the warnings that were created while parsing the configuration file.



67
68
69
# File 'lib/openc3/packets/packet_config.rb', line 67

def warnings
  @warnings
end

Instance Method Details

#dynamic_add_packet(packet, cmd_or_tlm = :TELEMETRY, affect_ids: false) ⇒ Object



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
# File 'lib/openc3/packets/packet_config.rb', line 340

def dynamic_add_packet(packet, cmd_or_tlm = :TELEMETRY, affect_ids: false)
  if cmd_or_tlm == :COMMAND
    @commands[packet.target_name][packet.packet_name] = packet

    if affect_ids and not packet.virtual
      hash = @cmd_id_value_hash[packet.target_name]
      hash = {} unless hash
      @cmd_id_value_hash[packet.target_name] = hash
      update_id_value_hash(packet, hash)
    end
  else
    @telemetry[packet.target_name][packet.packet_name] = packet

    # Update latest_data lookup for telemetry
    packet.sorted_items.each do |item|
      target_latest_data = @latest_data[packet.target_name]
      target_latest_data[item.name] ||= []
      latest_data_packets = target_latest_data[item.name]
      latest_data_packets << packet unless latest_data_packets.include?(packet)
    end

    if affect_ids and not packet.virtual
      hash = @tlm_id_value_hash[packet.target_name]
      hash = {} unless hash
      @tlm_id_value_hash[packet.target_name] = hash
      update_id_value_hash(packet, hash)
    end
  end
end

#finish_packetObject

Add current packet into hash if it exists



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
# File 'lib/openc3/packets/packet_config.rb', line 313

def finish_packet
  finish_item()
  if @current_packet
    @warnings += @current_packet.check_bit_offsets
    if @current_cmd_or_tlm == COMMAND
      PacketParser.check_item_data_types(@current_packet)
      @commands[@current_packet.target_name][@current_packet.packet_name] = @current_packet
      unless @current_packet.virtual
        hash = @cmd_id_value_hash[@current_packet.target_name]
        hash = {} unless hash
        @cmd_id_value_hash[@current_packet.target_name] = hash
        update_id_value_hash(@current_packet, hash)
      end
    else
      @telemetry[@current_packet.target_name][@current_packet.packet_name] = @current_packet
      unless @current_packet.virtual
        hash = @tlm_id_value_hash[@current_packet.target_name]
        hash = {} unless hash
        @tlm_id_value_hash[@current_packet.target_name] = hash
        update_id_value_hash(@current_packet, hash)
      end
    end
    @current_packet = nil
    @current_item = nil
  end
end

#process_file(filename, process_target_name, language = 'ruby') ⇒ Object

Processes a OpenC3 configuration file and uses the keywords to build up knowledge of the commands, telemetry, and limits groups.

Parameters:

  • filename (String)

    The name of the configuration file

  • process_target_name (String)

    The target name. Pass nil when parsing an xtce file to automatically determine the target name.



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
246
247
248
249
250
# File 'lib/openc3/packets/packet_config.rb', line 124

def process_file(filename, process_target_name, language = 'ruby')
  # Handle .xtce files
  extension = File.extname(filename).to_s.downcase
  if extension == ".xtce" or extension == ".xml"
    XtceParser.process(@commands, @telemetry, @warnings, filename, process_target_name)
    return
  end

  # Partial files are included into another file and thus aren't directly processed
  return if File.basename(filename)[0] == '_' # Partials start with underscore

  @language = language
  @converted_type = nil
  @converted_bit_size = nil
  @proc_text = ''
  @building_generic_conversion = false

  process_target_name = process_target_name.upcase
  parser = ConfigParser.new("https://docs.openc3.com/docs")
  parser.instance_variable_set(:@target_name, process_target_name)
  parser.parse_file(filename) do |keyword, params|
    if @building_generic_conversion
      case keyword
      # Complete a generic conversion
      when 'GENERIC_READ_CONVERSION_END', 'GENERIC_WRITE_CONVERSION_END'
        parser.verify_num_parameters(0, 0, keyword)
        @current_item.read_conversion =
          GenericConversion.new(@proc_text,
                                @converted_type,
                                @converted_bit_size) if keyword.include? "READ"
        @current_item.write_conversion =
          GenericConversion.new(@proc_text,
                                @converted_type,
                                @converted_bit_size) if keyword.include? "WRITE"
        @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 a new packet
      when 'COMMAND'
        finish_packet()
        @current_packet = PacketParser.parse_command(parser, process_target_name, @commands, @warnings)
        @current_cmd_or_tlm = COMMAND

      when 'TELEMETRY'
        finish_packet()
        @current_packet = PacketParser.parse_telemetry(parser, process_target_name, @telemetry, @latest_data, @warnings)
        @current_cmd_or_tlm = TELEMETRY

      # Select an existing packet for editing
      when 'SELECT_COMMAND', 'SELECT_TELEMETRY'
        usage = "#{keyword} <TARGET NAME> <PACKET NAME>"
        finish_packet()
        parser.verify_num_parameters(2, 2, usage)
        target_name = process_target_name
        target_name = params[0].upcase if target_name == 'SYSTEM'
        packet_name = params[1].upcase

        @current_packet = nil
        if keyword.include?('COMMAND')
          @current_cmd_or_tlm = COMMAND
          if @commands[target_name]
            @current_packet = @commands[target_name][packet_name]
          end
        else
          @current_cmd_or_tlm = TELEMETRY
          if @telemetry[target_name]
            @current_packet = @telemetry[target_name][packet_name]
          end
        end
        raise parser.error("Packet not found", usage) unless @current_packet

      # Start the creation of a new limits group
      when 'LIMITS_GROUP'
        usage = "LIMITS_GROUP <GROUP NAME>"
        parser.verify_num_parameters(1, 1, usage)
        @current_limits_group = params[0].to_s.upcase
        @limits_groups[@current_limits_group] = [] unless @limits_groups.include?(@current_limits_group)

      # Add a telemetry item to the limits group
      when 'LIMITS_GROUP_ITEM'
        usage = "LIMITS_GROUP_ITEM <TARGET NAME> <PACKET NAME> <ITEM NAME>"
        parser.verify_num_parameters(3, 3, usage)
        @limits_groups[@current_limits_group] << [params[0].to_s.upcase, params[1].to_s.upcase, params[2].to_s.upcase] if @current_limits_group

      #######################################################################
      # All the following keywords must have a current packet defined
      #######################################################################
      when 'SELECT_ITEM', 'SELECT_PARAMETER', 'DELETE_ITEM', 'DELETE_PARAMETER', 'ITEM',\
          'PARAMETER', 'ID_ITEM', 'ID_PARAMETER', 'ARRAY_ITEM', 'ARRAY_PARAMETER', 'APPEND_ITEM',\
          'APPEND_PARAMETER', 'APPEND_ID_ITEM', 'APPEND_ID_PARAMETER', 'APPEND_ARRAY_ITEM',\
          'APPEND_ARRAY_PARAMETER', 'ALLOW_SHORT', 'HAZARDOUS', 'PROCESSOR', 'META',\
          'DISABLE_MESSAGES', 'HIDDEN', 'DISABLED', 'VIRTUAL', 'ACCESSOR', 'TEMPLATE', 'TEMPLATE_FILE',\
          'RESPONSE', 'ERROR_RESPONSE', 'SCREEN', 'RELATED_ITEM', 'IGNORE_OVERLAP', 'VALIDATOR'
        raise parser.error("No current packet for #{keyword}") unless @current_packet

        process_current_packet(parser, keyword, params)

      #######################################################################
      # All the following keywords must have a current item defined
      #######################################################################
      when 'STATE', 'READ_CONVERSION', 'WRITE_CONVERSION', 'POLY_READ_CONVERSION',\
          'POLY_WRITE_CONVERSION', 'SEG_POLY_READ_CONVERSION', 'SEG_POLY_WRITE_CONVERSION',\
          'GENERIC_READ_CONVERSION_START', 'GENERIC_WRITE_CONVERSION_START', 'REQUIRED',\
          'LIMITS', 'LIMITS_RESPONSE', 'UNITS', 'FORMAT_STRING', 'DESCRIPTION',\
          'MINIMUM_VALUE', 'MAXIMUM_VALUE', 'DEFAULT_VALUE', 'OVERFLOW', 'OVERLAP', 'KEY', 'VARIABLE_BIT_SIZE'
        raise parser.error("No current item for #{keyword}") unless @current_item

        process_current_item(parser, keyword, params)

      else
        # blank config.lines will have a nil keyword and should not raise an exception
        raise parser.error("Unknown keyword '#{keyword}'") if keyword
      end # case keyword

    end # if building_generic_conversion
  end

  # Complete the last defined packet
  finish_packet()
end

#to_config(output_dir) ⇒ Object

Convert the PacketConfig back to OpenC3 configuration files for each target



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/openc3/packets/packet_config.rb', line 253

def to_config(output_dir)
  FileUtils.mkdir_p(output_dir)

  @telemetry.each do |target_name, packets|
    next if target_name == 'UNKNOWN'

    FileUtils.mkdir_p(File.join(output_dir, target_name, 'cmd_tlm'))
    filename = File.join(output_dir, target_name, 'cmd_tlm', target_name.downcase + '_tlm.txt')
    begin
      File.delete(filename)
    rescue
      # Doesn't exist
    end
    packets.each do |_packet_name, packet|
      File.open(filename, 'a') do |file|
        file.puts packet.to_config(:TELEMETRY)
        file.puts ""
      end
    end
  end

  @commands.each do |target_name, packets|
    next if target_name == 'UNKNOWN'

    FileUtils.mkdir_p(File.join(output_dir, target_name, 'cmd_tlm'))
    filename = File.join(output_dir, target_name, 'cmd_tlm', target_name.downcase + '_cmd.txt')
    begin
      File.delete(filename)
    rescue
      # Doesn't exist
    end
    packets.each do |_packet_name, packet|
      File.open(filename, 'a') do |file|
        file.puts packet.to_config(:COMMAND)
        file.puts ""
      end
    end
  end

  # Put limits groups into SYSTEM target
  if @limits_groups.length > 0
    FileUtils.mkdir_p(File.join(output_dir, 'SYSTEM', 'cmd_tlm'))
    filename = File.join(output_dir, 'SYSTEM', 'cmd_tlm', 'limits_groups.txt')
    File.open(filename, 'w') do |file|
      @limits_groups.each do |limits_group_name, limits_group_items|
        file.puts "LIMITS_GROUP #{limits_group_name.to_s.quote_if_necessary}"
        limits_group_items.each do |target_name, packet_name, item_name|
          file.puts "  LIMITS_GROUP_ITEM #{target_name.to_s.quote_if_necessary} #{packet_name.to_s.quote_if_necessary} #{item_name.to_s.quote_if_necessary}"
        end
        file.puts ""
      end
    end
  end
end

#to_xtce(output_dir) ⇒ Object

def to_config



308
309
310
# File 'lib/openc3/packets/packet_config.rb', line 308

def to_xtce(output_dir)
  XtceConverter.convert(@commands, @telemetry, output_dir)
end