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



33
34
35
# File 'lib/cosmos/packets/telemetry.rb', line 33

def initialize(config)
  @config = config
end

Instance Attribute Details

#configObject

Returns the value of attribute config.



27
28
29
# File 'lib/cosmos/packets/telemetry.rb', line 27

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.



344
345
346
# File 'lib/cosmos/packets/telemetry.rb', line 344

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



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/cosmos/packets/telemetry.rb', line 316

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

    ignored_items = System.targets[target_name].ignored_items

    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:



249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/cosmos/packets/telemetry.rb', line 249

def check_stale
  stale = []
  time = Time.now
  @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



287
288
289
290
291
292
293
# File 'lib/cosmos/packets/telemetry.rb', line 287

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



305
306
307
308
309
310
311
312
313
# File 'lib/cosmos/packets/telemetry.rb', line 305

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

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.



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

def identify!(packet_data, target_names = nil)
  identified_packet = nil

  target_names = target_names() unless target_names

  target_names.each do |target_name|
    target_packets = nil
    begin
      target_packets = packets(target_name)
    rescue RuntimeError
      # No telemetry for this target
      next
    end

    # Iterate through the packets and see if any represent the buffer
    target_packets.each do |packet_name, packet|
      if packet.identify?(packet_data)
        identified_packet = packet
        identified_packet.buffer = packet_data
        break
      end
    end
    break if identified_packet
  end
  return identified_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



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

def item_names(target_name, packet_name)
  if LATEST_PACKET_NAME.casecmp(packet_name) == 0
    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



63
64
65
# File 'lib/cosmos/packets/telemetry.rb', line 63

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.



121
122
123
124
125
126
127
128
129
# File 'lib/cosmos/packets/telemetry.rb', line 121

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


222
223
224
225
226
227
228
# File 'lib/cosmos/packets/telemetry.rb', line 222

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.



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

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



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'ext/cosmos/ext/telemetry/telemetry.c', line 60

static VALUE packet(VALUE self, VALUE target_name, VALUE packet_name)
{
  volatile VALUE packet = Qnil;
  volatile VALUE target_packets = Qnil;
  volatile VALUE upcase_target_name = Qnil;
  volatile VALUE upcase_packet_name = Qnil;

  target_packets = packets(self, target_name);

  upcase_packet_name = rb_funcall(packet_name, id_method_to_s, 0);
  upcase_packet_name = rb_funcall(upcase_packet_name, id_method_upcase, 0);
  packet = rb_hash_aref(target_packets, upcase_packet_name);
  if (!(RTEST(packet))) {
    upcase_target_name = rb_funcall(target_name, id_method_to_s, 0);
    upcase_target_name = rb_funcall(upcase_target_name, id_method_upcase, 0);
    rb_raise(rb_eRuntimeError, "Telemetry packet '%s %s' does not exist", RSTRING_PTR(upcase_target_name), RSTRING_PTR(upcase_packet_name));
  }

  return packet;
}

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



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'ext/cosmos/ext/telemetry/telemetry.c', line 90

static VALUE packet_and_item(VALUE self, VALUE target_name, VALUE packet_name, VALUE item_name)
{
  volatile VALUE upcase_packet_name = Qnil;
  volatile VALUE return_packet = Qnil;
  volatile VALUE item = Qnil;
  volatile VALUE return_value = Qnil;
  char * string_packet_name = NULL;

  upcase_packet_name = rb_funcall(packet_name, id_method_upcase, 0);
  string_packet_name = RSTRING_PTR(upcase_packet_name);
  if (strcmp(string_packet_name, "LATEST") == 0)
  {
    return_packet = rb_funcall(self, id_method_newest_packet, 2, target_name, item_name);
  }
  else
  {
    return_packet = packet(self, target_name, packet_name);
  }

  item = rb_funcall(return_packet, id_method_get_item, 1, item_name);

  return_value = rb_ary_new();
  rb_ary_push(return_value, return_packet);
  rb_ary_push(return_value, item);
  return return_value;
}

#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



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'ext/cosmos/ext/telemetry/telemetry.c', line 37

static VALUE packets(VALUE self, VALUE target_name) {
  volatile VALUE target_packets = Qnil;
  volatile VALUE upcase_target_name = Qnil;
  volatile VALUE telemetry = Qnil;

  upcase_target_name = rb_funcall(target_name, id_method_to_s, 0);
  upcase_target_name = rb_funcall(upcase_target_name, id_method_upcase, 0);
  telemetry = rb_funcall(rb_ivar_get(self, id_ivar_config), id_method_telemetry, 0);
  target_packets = rb_hash_aref(telemetry, upcase_target_name);

  if (!(RTEST(target_packets))) {
    rb_raise(rb_eRuntimeError, "Telemetry target '%s' does not exist", RSTRING_PTR(upcase_target_name));
  }

  return target_packets;
}

#resetObject

Resets metadata on every packet in every target



296
297
298
299
300
301
302
# File 'lib/cosmos/packets/telemetry.rb', line 296

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



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

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:



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/cosmos/packets/telemetry.rb', line 268

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)



43
44
45
46
47
# File 'lib/cosmos/packets/telemetry.rb', line 43

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.



213
214
215
216
217
# File 'lib/cosmos/packets/telemetry.rb', line 213

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



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
# File 'ext/cosmos/ext/telemetry/telemetry.c', line 129

static VALUE value(int argc, VALUE* argv, VALUE self)
{
  volatile VALUE target_name = Qnil;
  volatile VALUE packet_name = Qnil;
  volatile VALUE item_name = Qnil;
  volatile VALUE value_type = Qnil;
  volatile VALUE result = Qnil;
  volatile VALUE packet = Qnil;

  switch (argc)
  {
    case 3:
      target_name = argv[0];
      packet_name = argv[1];
      item_name = argv[2];
      value_type = symbol_CONVERTED;
      break;
    case 4:
      target_name = argv[0];
      packet_name = argv[1];
      item_name = argv[2];
      value_type = argv[3];
      break;
    default:
      /* Invalid number of arguments given */
      rb_raise(rb_eArgError, "wrong number of arguments (%d for 3..4)", argc);
      break;
  };

  result = packet_and_item(self, target_name, packet_name, item_name);
  packet = rb_ary_entry(result, 0);
  return rb_funcall(packet, id_method_read, 2, item_name, value_type);
}

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



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
277
# File 'ext/cosmos/ext/telemetry/telemetry.c', line 178

static VALUE values_and_limits_states(int argc, VALUE* argv, VALUE self) {
  volatile VALUE item_array = Qnil;
  volatile VALUE value_types = Qnil;
  volatile VALUE items = Qnil;
  volatile VALUE states = Qnil;
  volatile VALUE settings = Qnil;
  volatile VALUE entry = Qnil;
  volatile VALUE target_name = Qnil;
  volatile VALUE packet_name = Qnil;
  volatile VALUE item_name = Qnil;
  volatile VALUE value_type = Qnil;
  volatile VALUE result = Qnil;
  volatile VALUE return_value = Qnil;
  volatile VALUE limits = Qnil;
  volatile VALUE limits_set = Qnil;
  volatile VALUE limits_values = Qnil;
  volatile VALUE limits_settings = Qnil;
  long length = 0;
  long value_types_length = 0;
  int index = 0;

  switch (argc) {
    case 1:
      item_array = argv[0];
      value_types = symbol_CONVERTED;
      break;
    case 2:
      item_array = argv[0];
      value_types = argv[1];
      break;
    default:
      /* Invalid number of arguments given */
      rb_raise(rb_eArgError, "wrong number of arguments (%d for 1..2)", argc);
      break;
  };

  items = rb_ary_new();
  /* Verify items is a nested array */
  entry = rb_ary_entry(item_array, index);
  if (TYPE(entry) != T_ARRAY) {
    rb_raise(rb_eArgError, "item_array must be a nested array consisting of [[tgt,pkt,item],[tgt,pkt,item],...]");
  }
  states = rb_ary_new();
  settings = rb_ary_new();
  limits_set = rb_funcall(cSystem, id_method_limits_set, 0);
  length = RARRAY_LEN(item_array);
  if (TYPE(value_types) == T_ARRAY) {
    value_types_length = RARRAY_LEN(value_types);
    if (length != value_types_length) {
      rb_raise(rb_eArgError, "Passed %ld items but only %ld value types", length, value_types_length);
    }

    for (index = 0; index < length; index++) {
      entry = rb_ary_entry(item_array, index);
      target_name = rb_ary_entry(entry, 0);
      packet_name = rb_ary_entry(entry, 1);
      item_name = rb_ary_entry(entry, 2);
      value_type = rb_ary_entry(value_types, index);
      value_type = rb_funcall(value_type, id_method_intern, 0);

      result = packet_and_item(self, target_name, packet_name, item_name);
      rb_ary_push(items, rb_funcall(rb_ary_entry(result, 0), id_method_read, 2, item_name, value_type));
      limits = rb_funcall(rb_ary_entry(result, 1), id_method_limits, 0);
      rb_ary_push(states, rb_funcall(limits, id_method_state, 0));
      limits_values = rb_funcall(limits, id_method_values, 0);
      if (RTEST(limits_values)) {
        limits_settings = rb_hash_aref(limits_values, limits_set);
      } else {
        limits_settings = Qnil;
      }
      rb_ary_push(settings, limits_settings);
    }
  } else {
    value_type = rb_funcall(value_types, id_method_intern, 0);
    for (index = 0; index < length; index++) {
      entry = rb_ary_entry(item_array, index);
      target_name = rb_ary_entry(entry, 0);
      packet_name = rb_ary_entry(entry, 1);
      item_name = rb_ary_entry(entry, 2);

      result = packet_and_item(self, target_name, packet_name, item_name);
      rb_ary_push(items, rb_funcall(rb_ary_entry(result, 0), id_method_read, 2, item_name, value_type));
      limits = rb_funcall(rb_ary_entry(result, 1), id_method_limits, 0);
      rb_ary_push(states, rb_funcall(limits, id_method_state, 0));
      limits_values = rb_funcall(limits, id_method_values, 0);
      if (RTEST(limits_values)) {
        limits_settings = rb_hash_aref(limits_values, limits_set);
      } else {
        limits_settings = Qnil;
      }
      rb_ary_push(settings, limits_settings);
    }
  }

  return_value = rb_ary_new2(3);
  rb_ary_push(return_value, items);
  rb_ary_push(return_value, states);
  rb_ary_push(return_value, settings);
  return return_value;
}

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



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

def warnings
  return @config.warnings
end