Class: Cosmos::PacketItem

Inherits:
StructureItem
  • Object
show all
Defined in:
lib/cosmos/packets/packet_item.rb,
ext/cosmos/ext/packet/packet.c

Overview

Maintains knowledge of an item in a Packet

Direct Known Subclasses

TableItem

Constant Summary collapse

STATE_COLORS =

The allowable state colors

[:GREEN, :YELLOW, :RED]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, bit_offset, bit_size, data_type, endianness, array_size = nil, overflow = :ERROR) ⇒ Object

Create a StructureItem by setting all the attributes. It calls all the setter routines to do the attribute verification and then verifies the overall integrity.

It also initializes the attributes of the PacketItem.

Parameters:

  • name (String)

    The item name

  • bit_offset (Integer)

    Offset to the item starting at 0

  • bit_size (Integer)

    Size of the items in bits

  • data_type (Symbol)

    DATA_TYPES

  • endianness (Symbol)
  • array_size (Integer, nil) (defaults to: nil)

    Size of the array item in bits. For example, if the bit_size is 8, an array_size of 16 holds two values.

  • overflow (Symbol) (defaults to: :ERROR)


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

def initialize(name, bit_offset, bit_size, data_type, endianness, array_size = nil, overflow = :ERROR)
  super(name, bit_offset, bit_size, data_type, endianness, array_size, overflow)
  @format_string = nil
  @read_conversion = nil
  @write_conversion = nil
  @id_value = nil
  @states = nil
  @description = nil
  @units_full = nil
  @units = nil
  @default = nil
  @range = nil
  @required = false
  @hazardous = nil
  @state_colors = nil
  @limits = PacketItemLimits.new
  @persistence_setting = 1
  @persistence_count = 0
  @meta = nil
end

Instance Attribute Details

#defaultObject

The default value type depends on the data_type of the PacketItem

Returns:

  • Default value for this item



62
63
64
# File 'lib/cosmos/packets/packet_item.rb', line 62

def default
  @default
end

#descriptionString

Returns Description of the item.

Returns:

  • (String)

    Description of the item



48
49
50
# File 'lib/cosmos/packets/packet_item.rb', line 48

def description
  @description
end

#format_stringString

Returns Printf-style string used to format the item.

Returns:

  • (String)

    Printf-style string used to format the item



29
30
31
# File 'lib/cosmos/packets/packet_item.rb', line 29

def format_string
  @format_string
end

#hazardousHash

States that are hazardous for this item as well as their descriptions

Returns:

  • (Hash)

    Hazardous states given as STATE_NAME => DESCRIPTION. If no description was given the value will be nil.



76
77
78
# File 'lib/cosmos/packets/packet_item.rb', line 76

def hazardous
  @hazardous
end

#id_valueObject

The id_value type depends on the data_type of the PacketItem

Returns:

  • Value used to identify a packet



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

def id_value
  @id_value
end

#limitsPacketItemLimits

Returns All information regarding limits for this PacketItem.

Returns:



86
87
88
# File 'lib/cosmos/packets/packet_item.rb', line 86

def limits
  @limits
end

#rangeRange

The valid range of values for this item. Returns nil for items with data_type of :STRING or :BLOCK items.

Returns:

  • (Range)

    Valid range of values or nil



67
68
69
# File 'lib/cosmos/packets/packet_item.rb', line 67

def range
  @range
end

#read_conversionConversion

Conversion instance used when reading the PacketItem

Returns:



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

def read_conversion
  @read_conversion
end

#requiredBoolean

default value. true if it must be specified.

Returns:

  • (Boolean)

    Whether this item must be specified or can use its



71
72
73
# File 'lib/cosmos/packets/packet_item.rb', line 71

def required
  @required
end

#state_colorsHash

Colors associated with states

Returns:

  • (Hash)

    State colors given as STATE_NAME => COLOR



80
81
82
# File 'lib/cosmos/packets/packet_item.rb', line 80

def state_colors
  @state_colors
end

#statesHash

States are used to convert from a numeric value to a String.

Returns:

  • (Hash)

    Item states given as STATE_NAME => VALUE



45
46
47
# File 'lib/cosmos/packets/packet_item.rb', line 45

def states
  @states
end

#unitsString

Returns the abbreviated units of the item. For example, if the item represents a voltage, this would return “V”.

Returns:

  • (String)

    Abbreviated units of the item



58
59
60
# File 'lib/cosmos/packets/packet_item.rb', line 58

def units
  @units
end

#units_fullString

Returns the fully spelled out units of the item. For example, if the item represents a voltage, this would return “Voltage”.

Returns:

  • (String)

    Units of the item



53
54
55
# File 'lib/cosmos/packets/packet_item.rb', line 53

def units_full
  @units_full
end

#write_conversionConversion

Conversion instance used when writing the PacketItem

Returns:



37
38
39
# File 'lib/cosmos/packets/packet_item.rb', line 37

def write_conversion
  @write_conversion
end

Class Method Details

.from_json(hash) ⇒ Object



489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
# File 'lib/cosmos/packets/packet_item.rb', line 489

def self.from_json(hash)
  # Convert strings to symbols
  endianness = hash['endianness'] ? hash['endianness'].intern : nil
  data_type = hash['data_type'] ? hash['data_type'].intern : nil
  overflow = hash['overflow'] ? hash['overflow'].intern : nil
  item = PacketItem.new(hash['name'], hash['bit_offset'], hash['bit_size'],
    data_type, endianness, hash['array_size'], overflow)
  item.description = hash['description']
  item.id_value = hash['id_value']
  item.default = hash['default']
  item.range = (hash['minimum']..hash['maximum']) if hash['minimum'] && hash['maximum']
  item.required = hash['required']
  item.format_string = hash['format_string']
  item.units = hash['units']
  item.units_full = hash['units_full']
  if hash['states']
    item.states = {}
    item.hazardous = {}
    item.state_colors = {}
    hash['states'].each do |state_name, state|
      item.states[state_name] = state['value']
      item.hazardous[state_name] = state['hazardous']
      item.state_colors[state_name] = state['color'].to_sym if state['color']
    end
  end
  # Recreate COSMOS built-in conversions
  if hash['read_conversion']
    begin
      item.read_conversion = Cosmos::const_get(hash['read_conversion']['class']).new(*hash['read_conversion']['params'])
    rescue => error
      Logger.instance.error "#{item.name} read_conversion of #{hash['read_conversion']} could not be instantiated due to #{error}"
    end
  end
  if hash['write_conversion']
    begin
      item.write_conversion = Cosmos::const_get(hash['write_conversion']['class']).new(*hash['write_conversion']['params'])
    rescue => error
      Logger.instance.error "#{item.name} write_conversion of #{hash['write_conversion']} could not be instantiated due to #{error}"
    end
  end

  if hash['limits']
    item.limits = PacketItemLimits.new
    # Delete these keys so the only ones left are limits sets
    item.limits.persistence_setting = hash['limits'].delete('persistence_setting')
    item.limits.enabled = true if hash['limits'].delete('enabled')
    values = {}
    hash['limits'].each do |set, items|
      values[set.to_sym] = [items['red_low'], items['yellow_low'], items['yellow_high'], items['red_high']]
      values[set.to_sym].concat([items['green_low'], items['green_high']]) if items['green_low'] && items['green_high']
    end
    item.limits.values = values
  end
  item.meta = hash['meta']
  item
end

Instance Method Details

#as_jsonObject



427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
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
479
480
481
482
483
484
485
486
487
# File 'lib/cosmos/packets/packet_item.rb', line 427

def as_json
  config = {}
  config['name'] = self.name
  config['bit_offset'] = self.bit_offset
  config['bit_size'] = self.bit_size
  config['data_type'] = self.data_type.to_s
  config['array_size'] = self.array_size if self.array_size
  config['description'] = self.description
  config['id_value'] = self.id_value.as_json if self.id_value
  if @default
    config['default'] = @default.as_json
  end
  if self.range
    config['minimum'] = self.range.first.as_json
    config['maximum'] = self.range.last.as_json
  end
  config['endianness'] = self.endianness.to_s
  config['required'] = self.required
  config['format_string'] = self.format_string if self.format_string
  if self.units
    config['units'] = self.units
    config['units_full'] = self.units_full
  end
  config['overflow'] = self.overflow.to_s
  if @states
    states = {}
    config['states'] = states
    @states.each do |state_name, state_value|
      state = {}
      states[state_name] = state
      state['value'] = state_value.as_json
      state['hazardous'] = @hazardous[state_name] if @hazardous and @hazardous[state_name]
      state['color'] = @state_colors[state_name].to_s if @state_colors and @state_colors[state_name]
    end
  end

  config['read_conversion'] = self.read_conversion.as_json if self.read_conversion
  config['write_conversion'] = self.write_conversion.as_json if self.write_conversion

  if self.limits
    if self.limits.values
      config['limits'] ||= {}
      config['limits']['persistence_setting'] = self.limits.persistence_setting
      config['limits']['enabled'] = true if self.limits.enabled
      self.limits.values.each do |limits_set, limits_values|
        limits = {}
        limits['red_low'] =  limits_values[0]
        limits['yellow_low'] = limits_values[1]
        limits['yellow_high'] = limits_values[2]
        limits['red_high'] = limits_values[3]
        limits['green_low'] = limits_values[4] if limits_values[4]
        limits['green_high'] = limits_values[5] if limits_values[5]
        config['limits'][limits_set] = limits
      end
    end
    config['limits_response'] = self.limits.response.as_json if self.limits.response
  end

  config['meta'] = @meta if @meta
  config
end

#calculate_rangeObject



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# File 'lib/cosmos/packets/packet_item.rb', line 332

def calculate_range
  first = range.first
  last = range.last
  if data_type == :FLOAT
    if bit_size == 32
      if range.first == -3.402823e38
        first = 'MIN'
      end
      if range.last == 3.402823e38
        last = 'MAX'
      end
    else
      if range.first == -Float::MAX
        first = 'MIN'
      end
      if range.last == Float::MAX
        last = 'MAX'
      end
    end
  end
  return [first, last]
end

#check_default_and_range_data_typesObject



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

def check_default_and_range_data_types
  if @default and !@write_conversion
    if @array_size
      raise ArgumentError, "#{@name}: default must be an Array but is a #{default.class}" unless Array === @default
    else
      case data_type
      when :INT, :UINT
        raise ArgumentError, "#{@name}: default must be a Integer but is a #{@default.class}" unless Integer === @default

        if @range
          raise ArgumentError, "#{@name}: minimum must be a Integer but is a #{@range.first.class}" unless Integer === @range.first
          raise ArgumentError, "#{@name}: maximum must be a Integer but is a #{@range.last.class}" unless Integer === @range.last
        end
      when :FLOAT
        raise ArgumentError, "#{@name}: default must be a Float but is a #{@default.class}" unless Float === @default or Integer === @default

        @default = @default.to_f
        if @range
          raise ArgumentError, "#{@name}: minimum must be a Float but is a #{@range.first.class}" unless Float === @range.first or Integer === @range.first
          raise ArgumentError, "#{@name}: maximum must be a Float but is a #{@range.last.class}" unless Float === @range.last or Integer === @range.last

          @range = ((@range.first.to_f)..(@range.last.to_f))
        end
      when :BLOCK, :STRING
        raise ArgumentError, "#{@name}: default must be a String but is a #{@default.class}" unless String === @default

        @default = @default.clone.freeze
      end
    end
  end
end

#cloneObject Also known as: dup

Make a light weight clone of this item



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/cosmos/packets/packet_item.rb', line 285

def clone
  item = super()
  item.format_string = self.format_string.clone if self.format_string
  item.read_conversion = self.read_conversion.clone if self.read_conversion
  item.write_conversion = self.write_conversion.clone if self.write_conversion
  item.states = self.states.clone if self.states
  item.description = self.description.clone if self.description
  item.units_full = self.units_full.clone if self.units_full
  item.units = self.units.clone if self.units
  item.default = self.default.clone if self.default and String === self.default
  item.hazardous = self.hazardous.clone if self.hazardous
  item.state_colors = self.state_colors.clone if self.state_colors
  item.limits = self.limits.clone if self.limits
  item.meta = self.meta.clone if @meta
  item
end

#metaObject



270
271
272
# File 'lib/cosmos/packets/packet_item.rb', line 270

def meta
  @meta ||= {}
end

#meta=(meta) ⇒ Object



274
275
276
277
278
279
280
281
282
# File 'lib/cosmos/packets/packet_item.rb', line 274

def meta=(meta)
  if meta
    raise ArgumentError, "#{@name}: meta must be a Hash but is a #{meta.class}" unless Hash === meta

    @meta = meta.clone
  else
    @meta = nil
  end
end

#to_config(cmd_or_tlm, default_endianness) ⇒ Object



355
356
357
358
359
360
361
362
363
364
365
366
367
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
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
# File 'lib/cosmos/packets/packet_item.rb', line 355

def to_config(cmd_or_tlm, default_endianness)
  config = ''
  if cmd_or_tlm == :TELEMETRY
    if self.array_size
      config << "  ARRAY_ITEM #{self.name.to_s.quote_if_necessary} #{self.bit_offset} #{self.bit_size} #{self.data_type} #{self.array_size} \"#{self.description.to_s.gsub("\"", "'")}\""
    elsif self.id_value
      id_value = self.id_value
      if self.data_type == :BLOCK || self.data_type == :STRING
        unless self.id_value.is_printable?
          id_value = "0x" + self.id_value.simple_formatted
        else
          id_value = "\"#{self.id_value}\""
        end
      end
      config << "  ID_ITEM #{self.name.to_s.quote_if_necessary} #{self.bit_offset} #{self.bit_size} #{self.data_type} #{id_value} \"#{self.description.to_s.gsub("\"", "'")}\""
    else
      config << "  ITEM #{self.name.to_s.quote_if_necessary} #{self.bit_offset} #{self.bit_size} #{self.data_type} \"#{self.description.to_s.gsub("\"", "'")}\""
    end
  else # :COMMAND
    if self.array_size
      config << "  ARRAY_PARAMETER #{self.name.to_s.quote_if_necessary} #{self.bit_offset} #{self.bit_size} #{self.data_type} #{self.array_size} \"#{self.description.to_s.gsub("\"", "'")}\""
    else
      config << parameter_config()
    end
  end
  config << " #{self.endianness}" if self.endianness != default_endianness && self.data_type != :STRING && self.data_type != :BLOCK
  config << "\n"

  config << "    REQUIRED\n" if self.required
  config << "    FORMAT_STRING #{self.format_string.to_s.quote_if_necessary}\n" if self.format_string
  config << "    UNITS #{self.units_full.to_s.quote_if_necessary} #{self.units.to_s.quote_if_necessary}\n" if self.units
  config << "    OVERFLOW #{self.overflow}\n" if self.overflow != :ERROR

  if @states
    @states.each do |state_name, state_value|
      config << "    STATE #{state_name.to_s.quote_if_necessary} #{state_value.to_s.quote_if_necessary}"
      if @hazardous and @hazardous[state_name]
        config << " HAZARDOUS #{@hazardous[state_name].to_s.quote_if_necessary}"
      end
      if @state_colors and @state_colors[state_name]
        config << " #{@state_colors[state_name]}"
      end
      config << "\n"
    end
  end

  config << self.read_conversion.to_config(:READ) if self.read_conversion
  config << self.write_conversion.to_config(:WRITE) if self.write_conversion

  if self.limits
    if self.limits.values
      self.limits.values.each do |limits_set, limits_values|
        config << "    LIMITS #{limits_set} #{self.limits.persistence_setting} #{self.limits.enabled ? 'ENABLED' : 'DISABLED'} #{limits_values[0]} #{limits_values[1]} #{limits_values[2]} #{limits_values[3]}"
        if limits_values[4] && limits_values[5]
          config << " #{limits_values[4]} #{limits_values[5]}\n"
        else
          config << "\n"
        end
      end
    end
    config << self.limits.response.to_config if self.limits.response
  end

  if @meta
    @meta.each do |key, values|
      config << "    META #{key.to_s.quote_if_necessary} #{values.map { |a| a.to_s.quote_if_necessary }.join(" ")}\n"
    end
  end

  config
end

#to_hashObject



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

def to_hash
  hash = super()
  hash['format_string'] = self.format_string
  if self.read_conversion
    hash['read_conversion'] = self.read_conversion.to_s
  else
    hash['read_conversion'] = nil
  end
  if self.write_conversion
    hash['write_conversion'] = self.write_conversion.to_s
  else
    hash['write_conversion'] = nil
  end
  hash['id_value'] = self.id_value
  hash['states'] = self.states
  hash['description'] = self.description
  hash['units_full'] = self.units_full
  hash['units'] = self.units
  hash['default'] = self.default
  hash['range'] = self.range
  hash['required'] = self.required
  hash['hazardous'] = self.hazardous
  hash['state_colors'] = self.state_colors
  hash['limits'] = self.limits.to_hash
  hash['meta'] = nil
  hash['meta'] = @meta if @meta
  hash
end