cosmos 4.1.0 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Manifest.txt +5 -0
- data/appveyor.yml +2 -0
- data/autohotkey/tools/replay.ahk +45 -45
- data/autohotkey/tools/script_runner.ahk +3 -9
- data/cosmos.gemspec +1 -1
- data/data/config/interface_modifiers.yaml +23 -0
- data/data/config/screen.yaml +1 -1
- data/data/crc.txt +20 -18
- data/demo/config/targets/INST/cmd_tlm_server.txt +1 -1
- data/lib/cosmos/config/config_parser.rb +8 -3
- data/lib/cosmos/gui/dialogs/exception_dialog.rb +20 -5
- data/lib/cosmos/interfaces/protocols/burst_protocol.rb +13 -3
- data/lib/cosmos/interfaces/protocols/crc_protocol.rb +27 -3
- data/lib/cosmos/interfaces/protocols/fixed_protocol.rb +4 -2
- data/lib/cosmos/interfaces/protocols/length_protocol.rb +4 -2
- data/lib/cosmos/interfaces/protocols/override_protocol.rb +2 -2
- data/lib/cosmos/interfaces/protocols/preidentified_protocol.rb +3 -2
- data/lib/cosmos/interfaces/protocols/protocol.rb +16 -4
- data/lib/cosmos/interfaces/protocols/template_protocol.rb +7 -2
- data/lib/cosmos/interfaces/protocols/terminated_protocol.rb +4 -2
- data/lib/cosmos/packets/packet_config.rb +19 -859
- data/lib/cosmos/packets/packet_item.rb +56 -201
- data/lib/cosmos/packets/parsers/xtce_converter.rb +440 -0
- data/lib/cosmos/packets/parsers/xtce_parser.rb +682 -0
- data/lib/cosmos/tools/config_editor/config_editor.rb +143 -5
- data/lib/cosmos/tools/tlm_viewer/screen.rb +1 -1
- data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +5 -3
- data/lib/cosmos/tools/tlm_viewer/tlm_viewer_config.rb +40 -27
- data/lib/cosmos/version.rb +4 -4
- data/spec/config/config_parser_spec.rb +39 -2
- data/spec/install/config/targets/INST/screens/hs.txt +42 -0
- data/spec/install/config/targets/INST/target.txt +2 -0
- data/spec/interfaces/protocols/burst_protocol_spec.rb +18 -0
- data/spec/interfaces/protocols/length_protocol_spec.rb +49 -0
- data/spec/interfaces/udp_interface_spec.rb +0 -9
- data/spec/packets/packet_config_spec.rb +21 -144
- data/spec/packets/packet_item_spec.rb +68 -4
- data/spec/packets/parsers/packet_item_parser_spec.rb +12 -0
- data/spec/packets/parsers/xtce_parser_spec.rb +398 -0
- data/spec/tools/tlm_viewer/tlm_viewer_config_spec.rb +401 -0
- metadata +9 -10
@@ -13,7 +13,6 @@ require 'cosmos/packets/packet_item_limits'
|
|
13
13
|
require 'cosmos/conversions/conversion'
|
14
14
|
|
15
15
|
module Cosmos
|
16
|
-
|
17
16
|
# Maintains knowledge of an item in a Packet
|
18
17
|
class PacketItem < StructureItem
|
19
18
|
# @return [String] Printf-style string used to format the item
|
@@ -305,13 +304,44 @@ module Cosmos
|
|
305
304
|
hash
|
306
305
|
end
|
307
306
|
|
307
|
+
def calculate_range
|
308
|
+
first = range.first
|
309
|
+
last = range.last
|
310
|
+
if data_type == :FLOAT
|
311
|
+
if bit_size == 32
|
312
|
+
if range.first == -3.402823e38
|
313
|
+
first = 'MIN'
|
314
|
+
end
|
315
|
+
if range.last == 3.402823e38
|
316
|
+
last = 'MAX'
|
317
|
+
end
|
318
|
+
else
|
319
|
+
if range.first == -Float::MAX
|
320
|
+
first = 'MIN'
|
321
|
+
end
|
322
|
+
if range.last == Float::MAX
|
323
|
+
last = 'MAX'
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
return [first, last]
|
328
|
+
end
|
329
|
+
|
308
330
|
def to_config(cmd_or_tlm, default_endianness)
|
309
331
|
config = ''
|
310
332
|
if cmd_or_tlm == :TELEMETRY
|
311
333
|
if self.array_size
|
312
334
|
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("\"", "'")}\""
|
313
335
|
elsif self.id_value
|
314
|
-
|
336
|
+
id_value = self.id_value
|
337
|
+
if self.data_type == :BLOCK || self.data_type == :STRING
|
338
|
+
unless self.id_value.is_printable?
|
339
|
+
id_value = "0x" + self.id_value.simple_formatted
|
340
|
+
else
|
341
|
+
id_value = "\"#{self.id_value}\""
|
342
|
+
end
|
343
|
+
end
|
344
|
+
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("\"", "'")}\""
|
315
345
|
else
|
316
346
|
config << " ITEM #{self.name.to_s.quote_if_necessary} #{self.bit_offset} #{self.bit_size} #{self.data_type} \"#{self.description.to_s.gsub("\"", "'")}\""
|
317
347
|
end
|
@@ -320,15 +350,27 @@ module Cosmos
|
|
320
350
|
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("\"", "'")}\""
|
321
351
|
elsif self.id_value
|
322
352
|
if self.data_type == :BLOCK or self.data_type == :STRING
|
323
|
-
|
353
|
+
unless self.id_value.is_printable?
|
354
|
+
id_value = "0x" + self.id_value.simple_formatted
|
355
|
+
else
|
356
|
+
id_value = "\"#{self.id_value}\""
|
357
|
+
end
|
358
|
+
config << " ID_PARAMETER #{self.name.to_s.quote_if_necessary} #{self.bit_offset} #{self.bit_size} #{self.data_type} #{id_value} \"#{self.description.to_s.gsub("\"", "'")}\""
|
324
359
|
else
|
325
|
-
|
360
|
+
first, last = calculate_range()
|
361
|
+
config << " ID_PARAMETER #{self.name.to_s.quote_if_necessary} #{self.bit_offset} #{self.bit_size} #{self.data_type} #{first} #{last} #{self.id_value} \"#{self.description.to_s.gsub("\"", "'")}\""
|
326
362
|
end
|
327
363
|
else
|
328
364
|
if self.data_type == :BLOCK or self.data_type == :STRING
|
329
|
-
|
365
|
+
unless self.default.is_printable?
|
366
|
+
default = "0x" + self.default.simple_formatted
|
367
|
+
else
|
368
|
+
default = "\"#{self.default}\""
|
369
|
+
end
|
370
|
+
config << " PARAMETER #{self.name.to_s.quote_if_necessary} #{self.bit_offset} #{self.bit_size} #{self.data_type} #{default} \"#{self.description.to_s.gsub("\"", "'")}\""
|
330
371
|
else
|
331
|
-
|
372
|
+
first, last = calculate_range()
|
373
|
+
config << " PARAMETER #{self.name.to_s.quote_if_necessary} #{self.bit_offset} #{self.bit_size} #{self.data_type} #{first} #{last} #{self.default} \"#{self.description.to_s.gsub("\"", "'")}\""
|
332
374
|
end
|
333
375
|
end
|
334
376
|
end
|
@@ -359,7 +401,12 @@ module Cosmos
|
|
359
401
|
if self.limits
|
360
402
|
if self.limits.values
|
361
403
|
self.limits.values.each do |limits_set, limits_values|
|
362
|
-
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]}
|
404
|
+
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]}"
|
405
|
+
if limits_values[4] && limits_values[5]
|
406
|
+
config << " #{limits_values[4]} #{limits_values[5]}\n"
|
407
|
+
else
|
408
|
+
config << "\n"
|
409
|
+
end
|
363
410
|
end
|
364
411
|
end
|
365
412
|
config << self.limits.response.to_config if self.limits.response
|
@@ -374,198 +421,8 @@ module Cosmos
|
|
374
421
|
config
|
375
422
|
end
|
376
423
|
|
377
|
-
def to_xtce_type(param_or_arg, xml)
|
378
|
-
# TODO: Spline Conversions
|
379
|
-
case self.data_type
|
380
|
-
when :INT, :UINT
|
381
|
-
attrs = { :name => (self.name + '_Type') }
|
382
|
-
attrs[:initialValue] = self.default if self.default and !self.array_size
|
383
|
-
attrs[:shortDescription] = self.description if self.description
|
384
|
-
if @states and self.default and @states.key(self.default)
|
385
|
-
attrs[:initialValue] = @states.key(self.default) and !self.array_size
|
386
|
-
end
|
387
|
-
if self.data_type == :INT
|
388
|
-
signed = 'true'
|
389
|
-
encoding = 'twosCompliment'
|
390
|
-
else
|
391
|
-
signed = 'false'
|
392
|
-
encoding = 'unsigned'
|
393
|
-
end
|
394
|
-
if @states
|
395
|
-
xml['xtce'].send('Enumerated' + param_or_arg + 'Type', attrs) do
|
396
|
-
to_xtce_endianness(xml)
|
397
|
-
to_xtce_units(xml)
|
398
|
-
xml['xtce'].IntegerDataEncoding(:sizeInBits => self.bit_size, :encoding => encoding)
|
399
|
-
xml['xtce'].EnumerationList do
|
400
|
-
@states.each do |state_name, state_value|
|
401
|
-
xml['xtce'].Enumeration(:value => state_value, :label => state_name)
|
402
|
-
end
|
403
|
-
end
|
404
|
-
end
|
405
|
-
else
|
406
|
-
if (self.read_conversion and self.read_conversion.class == PolynomialConversion) or (self.write_conversion and self.write_conversion.class == PolynomialConversion)
|
407
|
-
type_string = 'Float' + param_or_arg + 'Type'
|
408
|
-
else
|
409
|
-
type_string = 'Integer' + param_or_arg + 'Type'
|
410
|
-
attrs[:signed] = signed
|
411
|
-
end
|
412
|
-
xml['xtce'].send(type_string, attrs) do
|
413
|
-
to_xtce_endianness(xml)
|
414
|
-
to_xtce_units(xml)
|
415
|
-
if (self.read_conversion and self.read_conversion.class == PolynomialConversion) or (self.write_conversion and self.write_conversion.class == PolynomialConversion)
|
416
|
-
xml['xtce'].IntegerDataEncoding(:sizeInBits => self.bit_size, :encoding => encoding) do
|
417
|
-
to_xtce_conversion(xml)
|
418
|
-
end
|
419
|
-
else
|
420
|
-
xml['xtce'].IntegerDataEncoding(:sizeInBits => self.bit_size, :encoding => encoding)
|
421
|
-
end
|
422
|
-
if self.limits
|
423
|
-
if self.limits.values
|
424
|
-
self.limits.values.each do |limits_set, limits_values|
|
425
|
-
if limits_set == :DEFAULT
|
426
|
-
xml['xtce'].DefaultAlarm do
|
427
|
-
xml['xtce'].StaticAlarmRanges do
|
428
|
-
xml['xtce'].WarningRange(:minInclusive => limits_values[1], :maxInclusive => limits_values[2])
|
429
|
-
xml['xtce'].CriticalRange(:minInclusive => limits_values[0], :maxInclusive => limits_values[3])
|
430
|
-
end
|
431
|
-
end
|
432
|
-
end
|
433
|
-
end
|
434
|
-
end
|
435
|
-
end
|
436
|
-
if self.range
|
437
|
-
xml['xtce'].ValidRange(:minInclusive => self.range.first, :maxInclusive => self.range.last)
|
438
|
-
end
|
439
|
-
end # Type
|
440
|
-
end # if @states
|
441
|
-
when :FLOAT
|
442
|
-
attrs = { :name => (self.name + '_Type'), :sizeInBits => self.bit_size }
|
443
|
-
attrs[:initialValue] = self.default if self.default and !self.array_size
|
444
|
-
attrs[:shortDescription] = self.description if self.description
|
445
|
-
xml['xtce'].send('Float' + param_or_arg + 'Type', attrs) do
|
446
|
-
to_xtce_endianness(xml)
|
447
|
-
to_xtce_units(xml)
|
448
|
-
if (self.read_conversion and self.read_conversion.class == PolynomialConversion) or (self.write_conversion and self.write_conversion.class == PolynomialConversion)
|
449
|
-
xml['xtce'].FloatDataEncoding(:sizeInBits => self.bit_size, :encoding => 'IEEE754_1985') do
|
450
|
-
to_xtce_conversion(xml)
|
451
|
-
end
|
452
|
-
else
|
453
|
-
xml['xtce'].FloatDataEncoding(:sizeInBits => self.bit_size, :encoding => 'IEEE754_1985')
|
454
|
-
end
|
455
|
-
|
456
|
-
if self.limits
|
457
|
-
if self.limits.values
|
458
|
-
self.limits.values.each do |limits_set, limits_values|
|
459
|
-
if limits_set == :DEFAULT
|
460
|
-
xml['xtce'].DefaultAlarm do
|
461
|
-
xml['xtce'].StaticAlarmRanges do
|
462
|
-
xml['xtce'].WarningRange(:minInclusive => limits_values[1], :maxInclusive => limits_values[2])
|
463
|
-
xml['xtce'].CriticalRange(:minInclusive => limits_values[0], :maxInclusive => limits_values[3])
|
464
|
-
end
|
465
|
-
end
|
466
|
-
end
|
467
|
-
end
|
468
|
-
end
|
469
|
-
end
|
470
|
-
|
471
|
-
if self.range
|
472
|
-
xml['xtce'].ValidRange(:minInclusive => self.range.first, :maxInclusive => self.range.last)
|
473
|
-
end
|
474
|
-
|
475
|
-
end # Type
|
476
|
-
when :STRING
|
477
|
-
# TODO: COSMOS Variably sized strings are not supported in XTCE
|
478
|
-
attrs = { :name => (self.name + '_Type'), :characterWidth => 8 }
|
479
|
-
attrs[:initialValue] = self.default if self.default and !self.array_size
|
480
|
-
attrs[:shortDescription] = self.description if self.description
|
481
|
-
xml['xtce'].send('String' + param_or_arg + 'Type', attrs) do
|
482
|
-
to_xtce_endianness(xml)
|
483
|
-
to_xtce_units(xml)
|
484
|
-
xml['xtce'].StringDataEncoding(:encoding => 'UTF-8') do
|
485
|
-
xml['xtce'].SizeInBits do
|
486
|
-
xml['xtce'].Fixed do
|
487
|
-
xml['xtce'].FixedValue(self.bit_size.to_s)
|
488
|
-
end
|
489
|
-
end
|
490
|
-
end
|
491
|
-
end
|
492
|
-
when :BLOCK
|
493
|
-
# TODO: COSMOS Variably sized blocks are not supported in XTCE
|
494
|
-
# TODO: Write string to hex method to support initial value
|
495
|
-
attrs = { :name => (self.name + '_Type') }
|
496
|
-
attrs[:shortDescription] = self.description if self.description
|
497
|
-
#attrs[:initialValue] = self.default if self.default and !self.array_size
|
498
|
-
xml['xtce'].send('Binary' + param_or_arg + 'Type', attrs) do
|
499
|
-
to_xtce_endianness(xml)
|
500
|
-
to_xtce_units(xml)
|
501
|
-
xml['xtce'].BinaryDataEncoding do
|
502
|
-
xml['xtce'].SizeInBits do
|
503
|
-
xml['xtce'].FixedValue(self.bit_size.to_s)
|
504
|
-
end
|
505
|
-
end
|
506
|
-
end
|
507
|
-
when :DERIVED
|
508
|
-
raise "DERIVED data type not supported in XTCE"
|
509
|
-
end
|
510
|
-
|
511
|
-
# Handle arrays
|
512
|
-
if self.array_size
|
513
|
-
# The above will have created the type for the array entries. Now we create the type for the actual array.
|
514
|
-
attrs = { :name => (self.name + '_ArrayType') }
|
515
|
-
attrs[:shortDescription] = self.description if self.description
|
516
|
-
attrs[:arrayTypeRef] = (self.name + '_Type')
|
517
|
-
attrs[:numberOfDimensions] = '1' # COSMOS Only supports one-dimensional arrays
|
518
|
-
xml['xtce'].send('Array' + param_or_arg + 'Type', attrs)
|
519
|
-
end
|
520
|
-
end
|
521
|
-
|
522
|
-
def to_xtce_item(param_or_arg, xml)
|
523
|
-
if self.array_size
|
524
|
-
xml['xtce'].send(param_or_arg, :name => self.name, "#{param_or_arg.downcase}TypeRef" => self.name + '_ArrayType')
|
525
|
-
else
|
526
|
-
xml['xtce'].send(param_or_arg, :name => self.name, "#{param_or_arg.downcase}TypeRef" => self.name + '_Type')
|
527
|
-
end
|
528
|
-
end
|
529
|
-
|
530
424
|
protected
|
531
425
|
|
532
|
-
def to_xtce_units(xml)
|
533
|
-
if self.units
|
534
|
-
xml['xtce'].UnitSet do
|
535
|
-
xml['xtce'].Unit(self.units, :description => self.units_full)
|
536
|
-
end
|
537
|
-
else
|
538
|
-
xml['xtce'].UnitSet
|
539
|
-
end
|
540
|
-
end
|
541
|
-
|
542
|
-
def to_xtce_endianness(xml)
|
543
|
-
if self.endianness == :LITTLE_ENDIAN and self.bit_size > 8
|
544
|
-
xml['xtce'].ByteOrderList do
|
545
|
-
(((self.bit_size - 1)/ 8) + 1).times do |byte_significance|
|
546
|
-
xml['xtce'].Byte(:byteSignificance => byte_significance)
|
547
|
-
end
|
548
|
-
end
|
549
|
-
end
|
550
|
-
end
|
551
|
-
|
552
|
-
def to_xtce_conversion(xml)
|
553
|
-
if self.read_conversion
|
554
|
-
conversion = self.read_conversion
|
555
|
-
else
|
556
|
-
conversion = self.write_conversion
|
557
|
-
end
|
558
|
-
if conversion and conversion.class == PolynomialConversion
|
559
|
-
xml['xtce'].DefaultCalibrator do
|
560
|
-
xml['xtce'].PolynomialCalibrator do
|
561
|
-
conversion.coeffs.each_with_index do |coeff, index|
|
562
|
-
xml['xtce'].Term(:coefficient => coeff, :exponent => index)
|
563
|
-
end
|
564
|
-
end
|
565
|
-
end
|
566
|
-
end
|
567
|
-
end
|
568
|
-
|
569
426
|
# Convert a value into the given data type
|
570
427
|
def convert(value, data_type)
|
571
428
|
case data_type
|
@@ -579,7 +436,5 @@ module Cosmos
|
|
579
436
|
rescue
|
580
437
|
raise ArgumentError, "#{@name}: Invalid value: #{value} for data type: #{data_type}"
|
581
438
|
end
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
end # module Cosmos
|
439
|
+
end
|
440
|
+
end
|
@@ -0,0 +1,440 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2014 Ball Aerospace & Technologies Corp.
|
4
|
+
# All Rights Reserved.
|
5
|
+
#
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
7
|
+
# under the terms of the GNU General Public License
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
10
|
+
|
11
|
+
require 'nokogiri'
|
12
|
+
require 'cosmos/packets/parsers/xtce_parser'
|
13
|
+
|
14
|
+
module Cosmos
|
15
|
+
class XtceConverter
|
16
|
+
attr_accessor :current_target_name
|
17
|
+
|
18
|
+
# Output a previously parsed definition file into the XTCE format
|
19
|
+
#
|
20
|
+
# @param commands [Hash<String=>Packet>] Hash of all the command packets
|
21
|
+
# keyed by the packet name.
|
22
|
+
# @param telemetry [Hash<String=>Packet>] Hash of all the telemetry packets
|
23
|
+
# keyed by the packet name.
|
24
|
+
# that were created while parsing the configuration
|
25
|
+
# @param output_dir [String] The name of the output directory to generate
|
26
|
+
# the XTCE files. A file is generated for each target.
|
27
|
+
def self.convert(commands, telemetry, output_dir)
|
28
|
+
XtceConverter.new(commands, telemetry, output_dir)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def initialize(commands, telemetry, output_dir)
|
34
|
+
FileUtils.mkdir_p(output_dir)
|
35
|
+
|
36
|
+
# Build target list
|
37
|
+
targets = []
|
38
|
+
telemetry.each { |target_name, packets| targets << target_name }
|
39
|
+
commands.each { |target_name, packets| targets << target_name }
|
40
|
+
targets.uniq!
|
41
|
+
|
42
|
+
targets.each do |target_name|
|
43
|
+
next if target_name == 'UNKNOWN'
|
44
|
+
|
45
|
+
# Reverse order of packets for the target so things are expected (reverse) order for xtce
|
46
|
+
XtceParser.reverse_packet_order(target_name, commands)
|
47
|
+
XtceParser.reverse_packet_order(target_name, telemetry)
|
48
|
+
|
49
|
+
FileUtils.mkdir_p(File.join(output_dir, target_name, 'cmd_tlm'))
|
50
|
+
filename = File.join(output_dir, target_name, 'cmd_tlm', target_name.downcase + '.xtce')
|
51
|
+
begin
|
52
|
+
File.delete(filename)
|
53
|
+
rescue
|
54
|
+
# Doesn't exist
|
55
|
+
end
|
56
|
+
|
57
|
+
# Create the xtce file for this target
|
58
|
+
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
|
59
|
+
xml['xtce'].SpaceSystem("xmlns:xtce" => "http://www.omg.org/space/xtce",
|
60
|
+
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
|
61
|
+
"name" => target_name,
|
62
|
+
"xsi:schemaLocation" => "http://www.omg.org/space/xtce http://www.omg.org/spec/XTCE/20061101/06-11-06.xsd") do
|
63
|
+
create_telemetry(xml, telemetry, target_name)
|
64
|
+
create_commands(xml, commands, target_name)
|
65
|
+
end # SpaceSystem
|
66
|
+
end # builder
|
67
|
+
File.open(filename, 'w') do |file|
|
68
|
+
file.puts builder.to_xml
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_telemetry(xml, telemetry, target_name)
|
74
|
+
# Gather and make unique all the packet items
|
75
|
+
unique_items = telemetry[target_name] ? get_unique(telemetry[target_name]) : {}
|
76
|
+
|
77
|
+
xml['xtce'].TelemetryMetaData do
|
78
|
+
xml['xtce'].ParameterTypeSet do
|
79
|
+
unique_items.each do |item_name, item|
|
80
|
+
to_xtce_type(item, 'Parameter', xml)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
xml['xtce'].ParameterSet do
|
85
|
+
unique_items.each do |item_name, item|
|
86
|
+
to_xtce_item(item, 'Parameter', xml)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
if telemetry[target_name]
|
91
|
+
xml['xtce'].ContainerSet do
|
92
|
+
telemetry[target_name].each do |packet_name, packet|
|
93
|
+
attrs = { :name => (packet_name + '_Base'), :abstract => "true" }
|
94
|
+
xml['xtce'].SequenceContainer(attrs) do
|
95
|
+
process_entry_list(xml, packet, :TELEMETRY)
|
96
|
+
end
|
97
|
+
|
98
|
+
attrs = { :name => packet_name }
|
99
|
+
attrs['shortDescription'] = packet.description if packet.description
|
100
|
+
xml['xtce'].SequenceContainer(attrs) do
|
101
|
+
xml['xtce'].EntryList
|
102
|
+
xml['xtce'].BaseContainer(:containerRef => (packet_name + '_Base')) do
|
103
|
+
if packet.id_items && packet.id_items.length > 0
|
104
|
+
xml['xtce'].RestrictionCriteria do
|
105
|
+
xml['xtce'].ComparisonList do
|
106
|
+
packet.id_items.each do |item|
|
107
|
+
xml['xtce'].Comparison(:parameterRef => item.name, :value => item.id_value)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end # SequenceContainer
|
114
|
+
end # telemetry.each
|
115
|
+
end # ContainerSet
|
116
|
+
end # if telemetry[target_name]
|
117
|
+
end # TelemetryMetaData
|
118
|
+
end
|
119
|
+
|
120
|
+
def create_commands(xml, commands, target_name)
|
121
|
+
return unless commands[target_name]
|
122
|
+
|
123
|
+
xml['xtce'].CommandMetaData do
|
124
|
+
xml['xtce'].ArgumentTypeSet do
|
125
|
+
get_unique(commands[target_name]).each do |arg_name, arg|
|
126
|
+
to_xtce_type(arg, 'Argument', xml)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
xml['xtce'].MetaCommandSet do
|
130
|
+
commands[target_name].each do |packet_name, packet|
|
131
|
+
attrs = { :name => packet_name + "_Base", :abstract => "true" }
|
132
|
+
xml['xtce'].MetaCommand(attrs) do
|
133
|
+
xml['xtce'].ArgumentList do
|
134
|
+
packet.sorted_items.each do |item|
|
135
|
+
next if item.data_type == :DERIVED
|
136
|
+
to_xtce_item(item, 'Argument', xml)
|
137
|
+
end
|
138
|
+
end # ArgumentList
|
139
|
+
xml['xtce'].CommandContainer(:name => "#{target_name}_#{packet_name}_CommandContainer") do
|
140
|
+
process_entry_list(xml, packet, :COMMAND)
|
141
|
+
end
|
142
|
+
end # Abstract MetaCommand
|
143
|
+
|
144
|
+
attrs = { :name => packet_name }
|
145
|
+
attrs['shortDescription'] = packet.description if packet.description
|
146
|
+
xml['xtce'].MetaCommand(attrs) do
|
147
|
+
xml['xtce'].BaseMetaCommand(:metaCommandRef => packet_name + "_Base") do
|
148
|
+
if packet.id_items && packet.id_items.length > 0
|
149
|
+
xml['xtce'].ArgumentAssignmentList do
|
150
|
+
packet.id_items.each do |item|
|
151
|
+
xml['xtce'].ArgumentAssignment(:argumentName => item.name, :argumentValue => item.id_value)
|
152
|
+
end
|
153
|
+
end # ArgumentAssignmentList
|
154
|
+
end
|
155
|
+
end # BaseMetaCommand
|
156
|
+
end # Actual MetaCommand
|
157
|
+
end # commands.each
|
158
|
+
end # MetaCommandSet
|
159
|
+
end # CommandMetaData
|
160
|
+
end
|
161
|
+
|
162
|
+
def get_unique(items)
|
163
|
+
unique = {}
|
164
|
+
items.each do |packet_name, packet|
|
165
|
+
packet.sorted_items.each do |item|
|
166
|
+
next if item.data_type == :DERIVED
|
167
|
+
unique[item.name] ||= []
|
168
|
+
unique[item.name] << item
|
169
|
+
end
|
170
|
+
end
|
171
|
+
unique.each do |item_name, items|
|
172
|
+
if items.length <= 1
|
173
|
+
unique[item_name] = items[0]
|
174
|
+
next
|
175
|
+
end
|
176
|
+
# TODO: need to make sure all the items in the array are exactly the same
|
177
|
+
unique[item_name] = items[0]
|
178
|
+
end
|
179
|
+
unique
|
180
|
+
end
|
181
|
+
|
182
|
+
# This method is almost the same for commands and telemetry except for the
|
183
|
+
# XML element name: [Array]ArgumentRefEntry vs [Array]ParameterRefEntry,
|
184
|
+
# and XML reference: argumentRef vs parameterRef.
|
185
|
+
# Thus we build the name and use send to dynamically dispatch.
|
186
|
+
def process_entry_list(xml, packet, cmd_vs_tlm)
|
187
|
+
if cmd_vs_tlm == :COMMAND
|
188
|
+
type = "Argument"
|
189
|
+
else # :TELEMETRY
|
190
|
+
type = "Parameter"
|
191
|
+
end
|
192
|
+
xml['xtce'].EntryList do
|
193
|
+
packed = packet.packed?
|
194
|
+
packet.sorted_items.each do |item|
|
195
|
+
next if item.data_type == :DERIVED
|
196
|
+
# TODO: Handle nonunique item names
|
197
|
+
if item.array_size
|
198
|
+
# Requiring parameterRef for argument arrays appears to be a defect in the schema
|
199
|
+
xml['xtce'].send("Array#{type}RefEntry".intern, :parameterRef => item.name) do
|
200
|
+
set_fixed_value(xml, item) if !packed
|
201
|
+
xml['xtce'].DimensionList do
|
202
|
+
xml['xtce'].Dimension do
|
203
|
+
xml['xtce'].StartingIndex do
|
204
|
+
xml['xtce'].FixedValue(0)
|
205
|
+
end
|
206
|
+
xml['xtce'].EndingIndex do
|
207
|
+
xml['xtce'].FixedValue((item.array_size / item.bit_size) - 1)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
else
|
213
|
+
if packed
|
214
|
+
xml['xtce'].send("#{type}RefEntry".intern, "#{type.downcase}Ref".intern => item.name)
|
215
|
+
else
|
216
|
+
xml['xtce'].send("#{type}RefEntry".intern, "#{type.downcase}Ref".intern => item.name) do
|
217
|
+
set_fixed_value(xml, item)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def set_fixed_value(xml, item)
|
226
|
+
if item.bit_offset >= 0
|
227
|
+
xml['xtce'].LocationInContainerInBits(:referenceLocation => 'containerStart') do
|
228
|
+
xml['xtce'].FixedValue(item.bit_offset)
|
229
|
+
end
|
230
|
+
else
|
231
|
+
xml['xtce'].LocationInContainerInBits(:referenceLocation => 'containerEnd') do
|
232
|
+
xml['xtce'].FixedValue(-item.bit_offset)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def to_xtce_type(item, param_or_arg, xml)
|
238
|
+
# TODO: Spline Conversions
|
239
|
+
case item.data_type
|
240
|
+
when :INT, :UINT
|
241
|
+
to_xtce_int(item, param_or_arg, xml)
|
242
|
+
when :FLOAT
|
243
|
+
to_xtce_float(item, param_or_arg, xml)
|
244
|
+
when :STRING
|
245
|
+
to_xtce_string(item, param_or_arg, xml, 'String')
|
246
|
+
when :BLOCK
|
247
|
+
to_xtce_string(item, param_or_arg, xml, 'Binary')
|
248
|
+
when :DERIVED
|
249
|
+
raise "DERIVED data type not supported in XTCE"
|
250
|
+
end
|
251
|
+
|
252
|
+
# Handle arrays
|
253
|
+
if item.array_size
|
254
|
+
# The above will have created the type for the array entries. Now we create the type for the actual array.
|
255
|
+
attrs = { :name => (item.name + '_ArrayType') }
|
256
|
+
attrs[:shortDescription] = item.description if item.description
|
257
|
+
attrs[:arrayTypeRef] = (item.name + '_Type')
|
258
|
+
attrs[:numberOfDimensions] = '1' # COSMOS Only supports one-dimensional arrays
|
259
|
+
xml['xtce'].send('Array' + param_or_arg + 'Type', attrs)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def to_xtce_int(item, param_or_arg, xml)
|
264
|
+
attrs = { :name => (item.name + '_Type') }
|
265
|
+
attrs[:initialValue] = item.default if item.default and !item.array_size
|
266
|
+
attrs[:shortDescription] = item.description if item.description
|
267
|
+
if item.states and item.default and item.states.key(item.default)
|
268
|
+
attrs[:initialValue] = item.states.key(item.default) and !item.array_size
|
269
|
+
end
|
270
|
+
if item.data_type == :INT
|
271
|
+
signed = 'true'
|
272
|
+
encoding = 'twosCompliment'
|
273
|
+
else
|
274
|
+
signed = 'false'
|
275
|
+
encoding = 'unsigned'
|
276
|
+
end
|
277
|
+
if item.states
|
278
|
+
xml['xtce'].send('Enumerated' + param_or_arg + 'Type', attrs) do
|
279
|
+
to_xtce_endianness(item, xml)
|
280
|
+
to_xtce_units(item, xml)
|
281
|
+
xml['xtce'].IntegerDataEncoding(:sizeInBits => item.bit_size, :encoding => encoding)
|
282
|
+
xml['xtce'].EnumerationList do
|
283
|
+
item.states.each do |state_name, state_value|
|
284
|
+
xml['xtce'].Enumeration(:value => state_value, :label => state_name)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
else
|
289
|
+
if (item.read_conversion and item.read_conversion.class == PolynomialConversion) or (item.write_conversion and item.write_conversion.class == PolynomialConversion)
|
290
|
+
type_string = 'Float' + param_or_arg + 'Type'
|
291
|
+
else
|
292
|
+
type_string = 'Integer' + param_or_arg + 'Type'
|
293
|
+
attrs[:signed] = signed
|
294
|
+
end
|
295
|
+
xml['xtce'].send(type_string, attrs) do
|
296
|
+
to_xtce_endianness(item, xml)
|
297
|
+
to_xtce_units(item, xml)
|
298
|
+
if (item.read_conversion and item.read_conversion.class == PolynomialConversion) or (item.write_conversion and item.write_conversion.class == PolynomialConversion)
|
299
|
+
xml['xtce'].IntegerDataEncoding(:sizeInBits => item.bit_size, :encoding => encoding) do
|
300
|
+
to_xtce_conversion(item, xml)
|
301
|
+
end
|
302
|
+
else
|
303
|
+
xml['xtce'].IntegerDataEncoding(:sizeInBits => item.bit_size, :encoding => encoding)
|
304
|
+
end
|
305
|
+
if item.limits
|
306
|
+
if item.limits.values
|
307
|
+
item.limits.values.each do |limits_set, limits_values|
|
308
|
+
if limits_set == :DEFAULT
|
309
|
+
xml['xtce'].DefaultAlarm do
|
310
|
+
xml['xtce'].StaticAlarmRanges do
|
311
|
+
xml['xtce'].WarningRange(:minInclusive => limits_values[1], :maxInclusive => limits_values[2])
|
312
|
+
xml['xtce'].CriticalRange(:minInclusive => limits_values[0], :maxInclusive => limits_values[3])
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
if item.range
|
320
|
+
xml['xtce'].ValidRange(:minInclusive => item.range.first, :maxInclusive => item.range.last)
|
321
|
+
end
|
322
|
+
end # Type
|
323
|
+
end # if item.states
|
324
|
+
end
|
325
|
+
|
326
|
+
def to_xtce_float(item, param_or_arg, xml)
|
327
|
+
attrs = { :name => (item.name + '_Type'), :sizeInBits => item.bit_size }
|
328
|
+
attrs[:initialValue] = item.default if item.default and !item.array_size
|
329
|
+
attrs[:shortDescription] = item.description if item.description
|
330
|
+
xml['xtce'].send('Float' + param_or_arg + 'Type', attrs) do
|
331
|
+
to_xtce_endianness(item, xml)
|
332
|
+
to_xtce_units(item, xml)
|
333
|
+
if (item.read_conversion and item.read_conversion.class == PolynomialConversion) or (item.write_conversion and item.write_conversion.class == PolynomialConversion)
|
334
|
+
xml['xtce'].FloatDataEncoding(:sizeInBits => item.bit_size, :encoding => 'IEEE754_1985') do
|
335
|
+
to_xtce_conversion(item, xml)
|
336
|
+
end
|
337
|
+
else
|
338
|
+
xml['xtce'].FloatDataEncoding(:sizeInBits => item.bit_size, :encoding => 'IEEE754_1985')
|
339
|
+
end
|
340
|
+
|
341
|
+
if item.limits
|
342
|
+
if item.limits.values
|
343
|
+
item.limits.values.each do |limits_set, limits_values|
|
344
|
+
if limits_set == :DEFAULT
|
345
|
+
xml['xtce'].DefaultAlarm do
|
346
|
+
xml['xtce'].StaticAlarmRanges do
|
347
|
+
xml['xtce'].WarningRange(:minInclusive => limits_values[1], :maxInclusive => limits_values[2])
|
348
|
+
xml['xtce'].CriticalRange(:minInclusive => limits_values[0], :maxInclusive => limits_values[3])
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
if item.range
|
357
|
+
xml['xtce'].ValidRange(:minInclusive => item.range.first, :maxInclusive => item.range.last)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def to_xtce_string(item, param_or_arg, xml, string_or_binary)
|
363
|
+
# TODO: COSMOS Variably sized strings are not supported in XTCE
|
364
|
+
attrs = { :name => (item.name + '_Type') }
|
365
|
+
attrs[:characterWidth] = 8 if string_or_binary == 'String'
|
366
|
+
if item.default && !item.array_size
|
367
|
+
unless item.default.is_printable?
|
368
|
+
attrs[:initialValue] = '0x' + item.default.simple_formatted
|
369
|
+
else
|
370
|
+
attrs[:initialValue] = item.default.inspect
|
371
|
+
end
|
372
|
+
end
|
373
|
+
attrs[:shortDescription] = item.description if item.description
|
374
|
+
xml['xtce'].send(string_or_binary + param_or_arg + 'Type', attrs) do
|
375
|
+
to_xtce_endianness(item, xml)
|
376
|
+
to_xtce_units(item, xml)
|
377
|
+
if string_or_binary == 'String'
|
378
|
+
xml['xtce'].StringDataEncoding(:encoding => 'UTF-8') do
|
379
|
+
xml['xtce'].SizeInBits do
|
380
|
+
xml['xtce'].Fixed do
|
381
|
+
xml['xtce'].FixedValue(item.bit_size.to_s)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
else
|
386
|
+
xml['xtce'].BinaryDataEncoding do
|
387
|
+
xml['xtce'].SizeInBits do
|
388
|
+
xml['xtce'].FixedValue(item.bit_size.to_s)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def to_xtce_item(item, param_or_arg, xml)
|
396
|
+
if item.array_size
|
397
|
+
xml['xtce'].send(param_or_arg, :name => item.name, "#{param_or_arg.downcase}TypeRef" => item.name + '_ArrayType')
|
398
|
+
else
|
399
|
+
xml['xtce'].send(param_or_arg, :name => item.name, "#{param_or_arg.downcase}TypeRef" => item.name + '_Type')
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def to_xtce_units(item, xml)
|
404
|
+
if item.units
|
405
|
+
xml['xtce'].UnitSet do
|
406
|
+
xml['xtce'].Unit(item.units, :description => item.units_full)
|
407
|
+
end
|
408
|
+
else
|
409
|
+
xml['xtce'].UnitSet
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
def to_xtce_endianness(item, xml)
|
414
|
+
if item.endianness == :LITTLE_ENDIAN and item.bit_size > 8
|
415
|
+
xml['xtce'].ByteOrderList do
|
416
|
+
(((item.bit_size - 1)/ 8) + 1).times do |byte_significance|
|
417
|
+
xml['xtce'].Byte(:byteSignificance => byte_significance)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
def to_xtce_conversion(item, xml)
|
424
|
+
if item.read_conversion
|
425
|
+
conversion = item.read_conversion
|
426
|
+
else
|
427
|
+
conversion = item.write_conversion
|
428
|
+
end
|
429
|
+
if conversion && conversion.class == PolynomialConversion
|
430
|
+
xml['xtce'].DefaultCalibrator do
|
431
|
+
xml['xtce'].PolynomialCalibrator do
|
432
|
+
conversion.coeffs.each_with_index do |coeff, index|
|
433
|
+
xml['xtce'].Term(:coefficient => coeff, :exponent => index)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|