Class: Cosmos::Telemetry

Inherits:
Object show all
Defined in:
lib/cosmos/packets/telemetry.rb,
ext/cosmos/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 primatives 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



41
42
43
# File 'lib/cosmos/packets/telemetry.rb', line 41

def initialize(config)
  @config = config
end

Instance Attribute Details

#configObject

Returns the value of attribute config.



35
36
37
# File 'lib/cosmos/packets/telemetry.rb', line 35

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.



483
484
485
# File 'lib/cosmos/packets/telemetry.rb', line 483

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



447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
# File 'lib/cosmos/packets/telemetry.rb', line 447

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

#check_staleArray(Packet)

Iterates through all the telemetry packets and marks them stale if they haven’t been received for over the System.staleness_seconds value.

Returns:



376
377
378
379
380
381
382
383
384
385
386
387
388
# File 'lib/cosmos/packets/telemetry.rb', line 376

def check_stale
  stale = []
  time = Time.now.sys
  @config.telemetry.each do |target_name, target_packets|
    target_packets.each do |packet_name, packet|
      if packet.received_time and (!packet.stale) and (time - packet.received_time > System.staleness_seconds)
        packet.set_stale
        stale << packet
      end
    end
  end
  stale
end

#clear_countersObject

Clears the received_count value on every packet in every target



417
418
419
420
421
422
423
# File 'lib/cosmos/packets/telemetry.rb', line 417

def clear_counters
  @config.telemetry.each do |target_name, target_packets|
    target_packets.each do |packet_name, packet|
      packet.received_count = 0
    end
  end
end

#first_non_hiddenObject

Returns the first non-hidden packet



435
436
437
438
439
440
441
442
443
444
# File 'lib/cosmos/packets/telemetry.rb', line 435

def first_non_hidden
  @config.telemetry.each do |target_name, target_packets|
    next if target_name == 'UNKNOWN'

    target_packets.each do |packet_name, packet|
      return packet unless packet.hidden
    end
  end
  nil
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.



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
307
308
309
310
311
312
# File 'lib/cosmos/packets/telemetry.rb', line 277

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.



264
265
266
267
268
# File 'lib/cosmos/packets/telemetry.rb', line 264

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



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

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



177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/cosmos/packets/telemetry.rb', line 177

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



170
171
172
# File 'lib/cosmos/packets/telemetry.rb', line 170

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.



208
209
210
211
212
213
214
215
216
217
218
# File 'lib/cosmos/packets/telemetry.rb', line 208

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


364
365
366
367
368
369
370
# File 'lib/cosmos/packets/telemetry.rb', line 364

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.



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

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



73
74
75
76
77
78
79
80
81
82
# File 'lib/cosmos/packets/telemetry.rb', line 73

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:



91
92
93
94
95
96
97
98
99
100
# File 'lib/cosmos/packets/telemetry.rb', line 91

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



61
62
63
64
65
66
67
# File 'lib/cosmos/packets/telemetry.rb', line 61

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



426
427
428
429
430
431
432
# File 'lib/cosmos/packets/telemetry.rb', line 426

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



199
200
201
202
# File 'lib/cosmos/packets/telemetry.rb', line 199

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

#stale(with_limits_only = false, target = nil) ⇒ Array(Packet)

Returns Array of the stale packets.

Parameters:

  • with_limits_only (Boolean) (defaults to: false)

    Return only the stale packets that have limits items and thus affect the overall limits state of the system

  • target (String) (defaults to: nil)

    Target name or nil for all targets

Returns:



395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
# File 'lib/cosmos/packets/telemetry.rb', line 395

def stale(with_limits_only = false, target = nil)
  if target && !target_names.include?(target)
    raise "Telemetry target '#{target.upcase}' does not exist"
  end

  stale = []
  @config.telemetry.each do |target_name, target_packets|
    next if target && target != target_name
    next if target_name == 'UNKNOWN'

    target_packets.each do |packet_name, packet|
      if packet.stale
        next if with_limits_only && packet.limits_items.empty?

        stale << packet
      end
    end
  end
  stale
end

#target_namesArray<String>

Returns The telemetry target names (excluding UNKNOWN).

Returns:

  • (Array<String>)

    The telemetry target names (excluding UNKNOWN)



51
52
53
54
55
# File 'lib/cosmos/packets/telemetry.rb', line 51

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.



355
356
357
358
359
# File 'lib/cosmos/packets/telemetry.rb', line 355

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



112
113
114
115
# File 'lib/cosmos/packets/telemetry.rb', line 112

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.



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

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.



46
47
48
# File 'lib/cosmos/packets/telemetry.rb', line 46

def warnings
  return @config.warnings
end