Class: OpenC3::Telemetry

Inherits:
Object show all
Defined in:
lib/openc3/packets/telemetry.rb,
ext/openc3/ext/telemetry/telemetry.c

Overview

Telemetry uses PacketConfig to parse the command and telemetry configuration files. It contains all the knowledge of which telemetry packets exist in the system and how to access them. This class is the API layer which other classes use to access telemetry.

This should not be confused with the Api module which implements the JSON API that is used by tools when accessing the Server. The Api module always provides Ruby primitives where the Telemetry class can return actual Packet or PacketItem objects. While there are some overlapping methods between the two, these are separate interfaces into the system.

Constant Summary collapse

LATEST_PACKET_NAME =
'LATEST'.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Telemetry

Returns a new instance of Telemetry.

Parameters:

  • config (PacketConfig)

    Packet configuration to use to access the telemetry



44
45
46
# File 'lib/openc3/packets/telemetry.rb', line 44

def initialize(config)
  @config = config
end

Instance Attribute Details

#configObject

Returns the value of attribute config.



38
39
40
# File 'lib/openc3/packets/telemetry.rb', line 38

def config
  @config
end

Instance Method Details

#allHash{String=>Hash{String=>Packet}}

Returns Hash of all the telemetry packets keyed by the target name. The value is another hash keyed by the packet name returning the packet.

Returns:

  • (Hash{String=>Hash{String=>Packet}})

    Hash of all the telemetry packets keyed by the target name. The value is another hash keyed by the packet name returning the packet.



421
422
423
# File 'lib/openc3/packets/telemetry.rb', line 421

def all
  @config.telemetry
end

#all_item_strings(include_hidden = false, splash = nil) ⇒ Object

Returns an array with a “TARGET_NAME PACKET_NAME ITEM_NAME” string for every item in the system



385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/openc3/packets/telemetry.rb', line 385

def all_item_strings(include_hidden = false, splash = nil)
  strings = []
  tnames = target_names()
  total = tnames.length.to_f
  tnames.each_with_index do |target_name, index|
    if splash
      splash.message = "Processing #{target_name} telemetry"
      splash.progress = index / total
    end

    # Note: System only has declared target structures but telemetry may have more
    system_target = System.targets[target_name]
    if system_target
      ignored_items = system_target.ignored_items
    else
      ignored_items = []
    end

    packets(target_name).each do |packet_name, packet|
      # We don't audit against hidden or disabled packets
      next if !include_hidden and (packet.hidden || packet.disabled)

      packet.items.each_key do |item_name|
        # Skip ignored items
        next if !include_hidden and ignored_items.include? item_name

        strings << "#{target_name} #{packet_name} #{item_name}"
      end
    end
  end
  strings
end

#dynamic_add_packet(packet, affect_ids: false) ⇒ Object



425
426
427
# File 'lib/openc3/packets/telemetry.rb', line 425

def dynamic_add_packet(packet, affect_ids: false)
  @config.dynamic_add_packet(packet, :TELEMETRY, affect_ids: affect_ids)
end

#identify(packet_data, target_names = nil) ⇒ Packet

Finds a packet from the Current Value Table that matches the given data and returns it. Does not fill the packets buffer. Use identify! to update the CVT.

Parameters:

  • packet_data (String)

    The binary packet data buffer

  • target_names (Array<String>) (defaults to: nil)

    List of target names to limit the search. The default value of nil means to search all known targets.

Returns:

  • (Packet)

    The identified packet, Returns nil if no packet could be identified.



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

def identify(packet_data, target_names = nil)
  target_names = target_names() unless target_names

  target_names.each do |target_name|
    target_name = target_name.to_s.upcase

    target_packets = nil
    begin
      target_packets = packets(target_name)
      # puts target_packets.length
    rescue RuntimeError
      # No telemetry for this target
      next
    end

    target = System.targets[target_name]
    if target and target.tlm_unique_id_mode
      # Iterate through the packets and see if any represent the buffer
      target_packets.each do |_packet_name, packet|
        return packet if packet.identify?(packet_data)
      end
    else
      # Do a hash lookup to quickly identify the packet
      if target_packets.length > 0
        packet = target_packets.first[1]
        key = packet.read_id_values(packet_data)
        hash = @config.tlm_id_value_hash[target_name]
        identified_packet = hash[key]
        identified_packet = hash['CATCHALL'.freeze] unless identified_packet
        return identified_packet if identified_packet
      end
    end
  end

  return nil
end

#identify!(packet_data, target_names = nil) ⇒ Packet

Identifies an unknown buffer of data as a defined packet and sets the packet’s data to the given buffer. Identifying a packet uses the fields marked as ID_ITEM to identify if the buffer passed represents the packet defined. Incorrectly sized buffers are still processed but an error is logged.

Note: This affects all subsequent requests for the packet (for example using packet) which is why the method is marked with a bang!

Parameters:

  • packet_data (String)

    The binary packet data buffer

  • target_names (Array<String>) (defaults to: nil)

    List of target names to limit the search. The default value of nil means to search all known targets.

Returns:

  • (Packet)

    The identified packet with its data set to the given packet_data buffer. Returns nil if no packet could be identified.



267
268
269
270
271
# File 'lib/openc3/packets/telemetry.rb', line 267

def identify!(packet_data, target_names = nil)
  identified_packet = identify(packet_data, target_names)
  identified_packet.buffer = packet_data if identified_packet
  return identified_packet
end

#identify_and_define_packet(packet, target_names = nil) ⇒ Object



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

def identify_and_define_packet(packet, target_names = nil)
  if !packet.identified?
    identified_packet = identify(packet.buffer(false), target_names)
    return nil unless identified_packet

    identified_packet = identified_packet.clone
    identified_packet.buffer = packet.buffer
    identified_packet.received_time = packet.received_time
    identified_packet.stored = packet.stored
    identified_packet.extra = packet.extra
    return identified_packet
  end

  if !packet.defined?
    begin
      identified_packet = self.packet(packet.target_name, packet.packet_name)
    rescue RuntimeError
      return nil
    end
    identified_packet = identified_packet.clone
    identified_packet.buffer = packet.buffer
    identified_packet.received_time = packet.received_time
    identified_packet.stored = packet.stored
    identified_packet.extra = packet.extra
    return identified_packet
  end

  return packet
end

#item_names(target_name, packet_name) ⇒ Array<PacketItem>

Returns The telemetry item names for the given target and packet name.

Parameters:

  • packet_name (see #packet)

    The packet name. LATEST is supported.

  • target_name (String)

    The target name

Returns:

  • (Array<PacketItem>)

    The telemetry item names for the given target and packet name



180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/openc3/packets/telemetry.rb', line 180

def item_names(target_name, packet_name)
  if LATEST_PACKET_NAME.casecmp(packet_name).zero?
    target_upcase = target_name.to_s.upcase
    target_latest_data = @config.latest_data[target_upcase]
    raise "Telemetry Target '#{target_upcase}' does not exist" unless target_latest_data

    item_names = target_latest_data.keys
  else
    tlm_packet = packet(target_name, packet_name)
    item_names = []
    tlm_packet.sorted_items.each { |item| item_names << item.name }
  end
  item_names
end

#items(target_name, packet_name) ⇒ Array<PacketItem>

Returns The telemetry items for the given target and packet name.

Parameters:

  • target_name (String)

    The target name

  • packet_name (String)

    The packet name. Must be a defined packet name and not ‘LATEST’.

Returns:

  • (Array<PacketItem>)

    The telemetry items for the given target and packet name



173
174
175
# File 'lib/openc3/packets/telemetry.rb', line 173

def items(target_name, packet_name)
  return packet(target_name, packet_name).sorted_items
end

#latest_packets(target_name, item_name) ⇒ Array<Packet>

Returns The latest (most recently arrived) packets with the specified target and item.

Parameters:

  • target_name (String)

    The target name

  • item_name (String)

    The item name

Returns:

  • (Array<Packet>)

    The latest (most recently arrived) packets with the specified target and item.



211
212
213
214
215
216
217
218
219
220
221
# File 'lib/openc3/packets/telemetry.rb', line 211

def latest_packets(target_name, item_name)
  target_upcase = target_name.to_s.upcase
  item_upcase = item_name.to_s.upcase
  target_latest_data = @config.latest_data[target_upcase]
  raise "Telemetry target '#{target_upcase}' does not exist" unless target_latest_data

  packets = @config.latest_data[target_upcase][item_upcase]
  raise "Telemetry item '#{target_upcase} #{LATEST_PACKET_NAME} #{item_upcase}' does not exist" unless packets

  return packets
end

#limits_change_callback=(limits_change_callback) ⇒ Object

Assigns a limits change callback to all telemetry packets

Parameters:

  • limits_change_callback


367
368
369
370
371
372
373
# File 'lib/openc3/packets/telemetry.rb', line 367

def limits_change_callback=(limits_change_callback)
  @config.telemetry.each do |_target_name, packets|
    packets.each do |_packet_name, packet|
      packet.limits_change_callback = limits_change_callback
    end
  end
end

#newest_packet(target_name, item_name) ⇒ Packet

Returns The packet with the most recent timestamp that contains the specified target and item.

Parameters:

  • target_name (String)

    The target name

  • item_name (String)

    The item name

Returns:

  • (Packet)

    The packet with the most recent timestamp that contains the specified target and item.



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

def newest_packet(target_name, item_name)
  # Handle LATEST_PACKET_NAME - Lookup packets for this target/item
  packets = latest_packets(target_name, item_name)

  # Find packet with newest timestamp
  newest_packet = nil
  newest_received_time = nil
  packets.each do |packet|
    received_time = packet.received_time
    if newest_received_time
      # See if the received time from this packet is newer.
      # Having the >= makes this method return the last defined packet
      # whether the timestamps are both nil or both equal.
      if received_time and received_time >= newest_received_time
        newest_packet = packet
        newest_received_time = newest_packet.received_time
      end
    else
      # No received time yet so take this packet
      newest_packet = packet
      newest_received_time = newest_packet.received_time
    end
  end
  return newest_packet
end

#packet(target_name, packet_name) ⇒ Packet

Returns The telemetry packet for the given target and packet name.

Parameters:

  • target_name (String)

    The target name

  • packet_name (String)

    The packet name. Must be a defined packet name and not ‘LATEST’.

Returns:

  • (Packet)

    The telemetry packet for the given target and packet name



76
77
78
79
80
81
82
83
84
85
# File 'lib/openc3/packets/telemetry.rb', line 76

def packet(target_name, packet_name)
  target_packets = packets(target_name)
  upcase_packet_name = packet_name.to_s.upcase
  packet = target_packets[upcase_packet_name]
  unless packet
    upcase_target_name = target_name.to_s.upcase
    raise "Telemetry packet '#{upcase_target_name} #{upcase_packet_name}' does not exist"
  end
  packet
end

#packet_and_item(target_name, packet_name, item_name) ⇒ Packet, PacketItem

Returns The packet and the packet item.

Parameters:

  • packet_name (String)

    The packet name. ‘LATEST’ can also be given to specify the last received (or defined if no packets have been received) packet within the given target that contains the item_name.

  • item_name (String)

    The item name

  • target_name (String)

    The target name

Returns:



94
95
96
97
98
99
100
101
102
103
# File 'lib/openc3/packets/telemetry.rb', line 94

def packet_and_item(target_name, packet_name, item_name)
  upcase_packet_name = packet_name.to_s.upcase
  if upcase_packet_name == "LATEST".freeze
    return_packet = newest_packet(target_name, item_name)
  else
    return_packet = packet(target_name, packet_name)
  end
  item = return_packet.get_item(item_name)
  return [return_packet, item]
end

#packets(target_name) ⇒ Hash<packet_name=>Packet>

Returns Hash of the telemetry packets for the given target name keyed by the packet name.

Parameters:

  • target_name (String)

    The target name

Returns:

  • (Hash<packet_name=>Packet>)

    Hash of the telemetry packets for the given target name keyed by the packet name



64
65
66
67
68
69
70
# File 'lib/openc3/packets/telemetry.rb', line 64

def packets(target_name)
  upcase_target_name = target_name.to_s.upcase
  target_packets = @config.telemetry[upcase_target_name]
  raise "Telemetry target '#{upcase_target_name}' does not exist" unless target_packets

  target_packets
end

#resetObject

Resets metadata on every packet in every target



376
377
378
379
380
381
382
# File 'lib/openc3/packets/telemetry.rb', line 376

def reset
  @config.telemetry.each do |_target_name, target_packets|
    target_packets.each do |_packet_name, packet|
      packet.reset
    end
  end
end

#set_value(target_name, packet_name, item_name, value, value_type = :CONVERTED) ⇒ Object

Set a telemetry value in a packet.

Parameters:

  • value

    The value to set in the packet

  • target_name (String)

    The target name

  • packet_name (String)

    The packet name. ‘LATEST’ can also be given to specify the last received (or defined if no packets have been received) packet within the given target that contains the item_name.

  • item_name (String)

    The item name



202
203
204
205
# File 'lib/openc3/packets/telemetry.rb', line 202

def set_value(target_name, packet_name, item_name, value, value_type = :CONVERTED)
  packet, _ = packet_and_item(target_name, packet_name, item_name)
  packet.write(item_name, value, value_type)
end

#target_namesArray<String>

Returns The telemetry target names (excluding UNKNOWN).

Returns:

  • (Array<String>)

    The telemetry target names (excluding UNKNOWN)



54
55
56
57
58
# File 'lib/openc3/packets/telemetry.rb', line 54

def target_names
  result = @config.telemetry.keys.sort
  result.delete('UNKNOWN'.freeze)
  return result
end

#update!(target_name, packet_name, packet_data) ⇒ Packet

Updates the specified packet with the given packet data. Raises an error if the packet could not be found.

Note: This affects all subsequent requests for the packet which is why the method is marked with a bang!

Parameters:

  • target_name (String)

    The target name

  • packet_name (String)

    The packet name. Must be a defined packet name and not ‘LATEST’.

Returns:

  • (Packet)

    The packet with its data set to the given packet_data buffer.



358
359
360
361
362
# File 'lib/openc3/packets/telemetry.rb', line 358

def update!(target_name, packet_name, packet_data)
  identified_packet = packet(target_name, packet_name)
  identified_packet.buffer = packet_data
  return identified_packet
end

#value(*args) ⇒ Object

Return a telemetry value from a packet.

Must be one of Packet::VALUE_TYPES as Strings. :RAW values will match their data_type. :CONVERTED values can be any type.

Parameters:

  • value_type (Symbol)

    How to convert the item before returning.

  • target_name (String)

    The target name

  • packet_name (String)

    The packet name. ‘LATEST’ can also be given to specify the last received (or defined if no packets have been received) packet within the given target that contains the item_name.

  • item_name (String)

    The item name

Returns:

  • The value. :FORMATTED and :WITH_UNITS values are always returned



115
116
117
118
# File 'lib/openc3/packets/telemetry.rb', line 115

def value(target_name, packet_name, item_name, value_type = :CONVERTED)
  packet, _ = packet_and_item(target_name, packet_name, item_name) # Handles LATEST
  return packet.read(item_name, value_type)
end

#values_and_limits_states(*args) ⇒ Array

Reads the specified list of items and returns their values and limits state.

Parameters:

  • item_array (Array<Array(String String String)>)

    An array consisting of [target name, packet name, item name]

  • value_types (Symbol|Array<Symbol>)

    How to convert the items before returning. A single symbol of Packet::VALUE_TYPES can be passed which will convert all items the same way. Or an array of symbols can be passed to control how each item is converted.

Returns:

  • (Array, Array, Array)

    The first array contains the item values, the second their limits state, and the third the limits settings which includes red, yellow, and green (if given) limits values.



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

def values_and_limits_states(item_array, value_types = :CONVERTED)
  items = []

  # Verify item_array is a nested array
  raise(ArgumentError, "item_array must be a nested array consisting of [[tgt,pkt,item],[tgt,pkt,item],...]") unless Array === item_array[0]

  states = []
  settings = []
  limits_set = System.limits_set

  raise(ArgumentError, "Passed #{item_array.length} items but only #{value_types.length} value types") if (Array === value_types) and item_array.length != value_types.length

  value_type = value_types.intern unless Array === value_types
  item_array.length.times do |index|
    entry = item_array[index]
    target_name = entry[0]
    packet_name = entry[1]
    item_name = entry[2]
    value_type = value_types[index].intern if Array === value_types

    packet, item = packet_and_item(target_name, packet_name, item_name) # Handles LATEST
    items << packet.read(item_name, value_type)
    limits = item.limits
    states << limits.state
    limits_values = limits.values
    if limits_values
      limits_settings = limits_values[limits_set]
    else
      limits_settings = nil
    end
    settings << limits_settings
  end

  return [items, states, settings]
end

#warningsArray<String>

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.



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

def warnings
  return @config.warnings
end