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"
CONVERTED_DATA_TYPES =

Note: DERIVED is not a valid converted type. Also TIME is currently only a converted type

[:INT, :UINT, :FLOAT, :STRING, :BLOCK, :BOOL, :OBJECT, :ARRAY, :ANY, :TIME]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePacketConfig

Returns a new instance of PacketConfig.



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

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 = {}
  @cmd_subpacket_id_value_hash = {}
  @cmd_id_signature = {}
  @cmd_subpacket_id_signature = {}
  @cmd_unique_id_mode = {}
  @cmd_subpacket_unique_id_mode = {}
  @tlm_id_value_hash = {}
  @tlm_subpacket_id_value_hash = {}
  @tlm_id_signature = {}
  @tlm_subpacket_id_signature = {}
  @tlm_unique_id_mode = {}
  @tlm_subpacket_unique_id_mode = {}

  # 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_signatureObject (readonly)

Returns the value of attribute cmd_id_signature.



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

def cmd_id_signature
  @cmd_id_signature
end

#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:



81
82
83
# File 'lib/openc3/packets/packet_config.rb', line 81

def cmd_id_value_hash
  @cmd_id_value_hash
end

#cmd_subpacket_id_signatureObject (readonly)

Returns the value of attribute cmd_subpacket_id_signature.



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

def cmd_subpacket_id_signature
  @cmd_subpacket_id_signature
end

#cmd_subpacket_id_value_hashObject (readonly)

Returns the value of attribute cmd_subpacket_id_value_hash.



82
83
84
# File 'lib/openc3/packets/packet_config.rb', line 82

def cmd_subpacket_id_value_hash
  @cmd_subpacket_id_value_hash
end

#cmd_subpacket_unique_id_modeObject (readonly)

Returns the value of attribute cmd_subpacket_unique_id_mode.



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

def cmd_subpacket_unique_id_mode
  @cmd_subpacket_unique_id_mode
end

#cmd_unique_id_modeObject (readonly)

Returns the value of attribute cmd_unique_id_mode.



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

def cmd_unique_id_mode
  @cmd_unique_id_mode
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.



56
57
58
# File 'lib/openc3/packets/packet_config.rb', line 56

def commands
  @commands
end

#languageString (readonly)

Returns Language of current target (ruby or python).

Returns:

  • (String)

    Language of current target (ruby or python)



99
100
101
# File 'lib/openc3/packets/packet_config.rb', line 99

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.



76
77
78
# File 'lib/openc3/packets/packet_config.rb', line 76

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.



61
62
63
# File 'lib/openc3/packets/packet_config.rb', line 61

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.



65
66
67
# File 'lib/openc3/packets/packet_config.rb', line 65

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.



48
49
50
# File 'lib/openc3/packets/packet_config.rb', line 48

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.



52
53
54
# File 'lib/openc3/packets/packet_config.rb', line 52

def telemetry
  @telemetry
end

#tlm_id_signatureObject (readonly)

Returns the value of attribute tlm_id_signature.



93
94
95
# File 'lib/openc3/packets/packet_config.rb', line 93

def tlm_id_signature
  @tlm_id_signature
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:



91
92
93
# File 'lib/openc3/packets/packet_config.rb', line 91

def tlm_id_value_hash
  @tlm_id_value_hash
end

#tlm_subpacket_id_signatureObject (readonly)

Returns the value of attribute tlm_subpacket_id_signature.



94
95
96
# File 'lib/openc3/packets/packet_config.rb', line 94

def tlm_subpacket_id_signature
  @tlm_subpacket_id_signature
end

#tlm_subpacket_id_value_hashObject (readonly)

Returns the value of attribute tlm_subpacket_id_value_hash.



92
93
94
# File 'lib/openc3/packets/packet_config.rb', line 92

def tlm_subpacket_id_value_hash
  @tlm_subpacket_id_value_hash
end

#tlm_subpacket_unique_id_modeObject (readonly)

Returns the value of attribute tlm_subpacket_unique_id_mode.



96
97
98
# File 'lib/openc3/packets/packet_config.rb', line 96

def tlm_subpacket_unique_id_mode
  @tlm_subpacket_unique_id_mode
end

#tlm_unique_id_modeObject (readonly)

Returns the value of attribute tlm_unique_id_mode.



95
96
97
# File 'lib/openc3/packets/packet_config.rb', line 95

def tlm_unique_id_mode
  @tlm_unique_id_mode
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.



69
70
71
# File 'lib/openc3/packets/packet_config.rb', line 69

def warnings
  @warnings
end

Class Method Details

.from_config(config, process_target_name, language = 'ruby') ⇒ Object

This method provides way to quickly test packet configs

require ‘openc3/packets/packet_config’

config = <<END

...

END

pc = PacketConfig.from_config(config, “MYTARGET”) c = pc.commands[‘SET_POINTING_CMD’] c.restore_defaults() c.write(“MYITEM”, 5) puts c.buffer.formatted



413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/openc3/packets/packet_config.rb', line 413

def self.from_config(config, process_target_name, language = 'ruby')
  pc = self.new
  tf = Tempfile.new("pc.txt")
  tf.write(config)
  tf.close
  begin
    pc.process_file(tf.path, process_target_name, language)
  ensure
    tf.unlink
  end
  return pc
end

Instance Method Details

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



368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
# File 'lib/openc3/packets/packet_config.rb', line 368

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
      if packet.subpacket
        (packet, @cmd_subpacket_id_value_hash, @cmd_subpacket_id_signature, @cmd_subpacket_unique_id_mode)
      else
        (packet, @cmd_id_value_hash, @cmd_id_signature, @cmd_unique_id_mode)
      end
    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
      if packet.subpacket
        (packet, @tlm_subpacket_id_value_hash, @tlm_subpacket_id_signature, @tlm_subpacket_unique_id_mode)
      else
        (packet, @tlm_id_value_hash, @tlm_id_signature, @tlm_unique_id_mode)
      end
    end
  end
end

#finish_packetObject

Add current packet into hash if it exists



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

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
        if @current_packet.subpacket
          (@current_packet, @cmd_subpacket_id_value_hash, @cmd_subpacket_id_signature, @cmd_subpacket_unique_id_mode)
        else
          (@current_packet, @cmd_id_value_hash, @cmd_id_signature, @cmd_unique_id_mode)
        end
      end
    else
      @telemetry[@current_packet.target_name][@current_packet.packet_name] = @current_packet
      unless @current_packet.virtual
        if @current_packet.subpacket
          (@current_packet, @tlm_subpacket_id_value_hash, @tlm_subpacket_id_signature, @tlm_subpacket_unique_id_mode)
        else
          (@current_packet, @tlm_id_value_hash, @tlm_id_signature, @tlm_unique_id_mode)
        end
      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.



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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/openc3/packets/packet_config.rb', line 148

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', 'RESTRICTED', 'ACCESSOR', 'TEMPLATE', 'TEMPLATE_FILE',\
          'RESPONSE', 'ERROR_RESPONSE', 'SCREEN', 'RELATED_ITEM', 'IGNORE_OVERLAP', 'VALIDATOR', 'SUBPACKET', 'SUBPACKETIZER',\
          'STRUCTURE', 'APPEND_STRUCTURE'
        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',\
          'OBFUSCATE'
        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



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

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



334
335
336
# File 'lib/openc3/packets/packet_config.rb', line 334

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