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.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/openc3/packets/packet_config.rb', line 88

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:



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

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.



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

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



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

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.



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

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.



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

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.



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

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.



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

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:



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

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.



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

def warnings
  @warnings
end

Instance Method Details

#finish_packetObject

Add current packet into hash if it exists



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/openc3/packets/packet_config.rb', line 306

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
      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(hash)
    else
      @telemetry[@current_packet.target_name][@current_packet.packet_name] = @current_packet
      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(hash)
    end
    @current_packet = nil
    @current_item = nil
  end
end

#process_file(filename, process_target_name) ⇒ 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.



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

def process_file(filename, process_target_name)
  # Handle .xtce files
  if File.extname(filename).to_s.downcase == ".xtce"
    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

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

  process_target_name = process_target_name.upcase
  parser = ConfigParser.new("https://openc3.com/docs/v5")
  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', 'ACCESSOR', 'TEMPLATE', 'TEMPLATE_FILE'
        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'
        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



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
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/openc3/packets/packet_config.rb', line 246

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



301
302
303
# File 'lib/openc3/packets/packet_config.rb', line 301

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