cosmos 4.1.0 → 4.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Manifest.txt +5 -0
  3. data/appveyor.yml +2 -0
  4. data/autohotkey/tools/replay.ahk +45 -45
  5. data/autohotkey/tools/script_runner.ahk +3 -9
  6. data/cosmos.gemspec +1 -1
  7. data/data/config/interface_modifiers.yaml +23 -0
  8. data/data/config/screen.yaml +1 -1
  9. data/data/crc.txt +20 -18
  10. data/demo/config/targets/INST/cmd_tlm_server.txt +1 -1
  11. data/lib/cosmos/config/config_parser.rb +8 -3
  12. data/lib/cosmos/gui/dialogs/exception_dialog.rb +20 -5
  13. data/lib/cosmos/interfaces/protocols/burst_protocol.rb +13 -3
  14. data/lib/cosmos/interfaces/protocols/crc_protocol.rb +27 -3
  15. data/lib/cosmos/interfaces/protocols/fixed_protocol.rb +4 -2
  16. data/lib/cosmos/interfaces/protocols/length_protocol.rb +4 -2
  17. data/lib/cosmos/interfaces/protocols/override_protocol.rb +2 -2
  18. data/lib/cosmos/interfaces/protocols/preidentified_protocol.rb +3 -2
  19. data/lib/cosmos/interfaces/protocols/protocol.rb +16 -4
  20. data/lib/cosmos/interfaces/protocols/template_protocol.rb +7 -2
  21. data/lib/cosmos/interfaces/protocols/terminated_protocol.rb +4 -2
  22. data/lib/cosmos/packets/packet_config.rb +19 -859
  23. data/lib/cosmos/packets/packet_item.rb +56 -201
  24. data/lib/cosmos/packets/parsers/xtce_converter.rb +440 -0
  25. data/lib/cosmos/packets/parsers/xtce_parser.rb +682 -0
  26. data/lib/cosmos/tools/config_editor/config_editor.rb +143 -5
  27. data/lib/cosmos/tools/tlm_viewer/screen.rb +1 -1
  28. data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +5 -3
  29. data/lib/cosmos/tools/tlm_viewer/tlm_viewer_config.rb +40 -27
  30. data/lib/cosmos/version.rb +4 -4
  31. data/spec/config/config_parser_spec.rb +39 -2
  32. data/spec/install/config/targets/INST/screens/hs.txt +42 -0
  33. data/spec/install/config/targets/INST/target.txt +2 -0
  34. data/spec/interfaces/protocols/burst_protocol_spec.rb +18 -0
  35. data/spec/interfaces/protocols/length_protocol_spec.rb +49 -0
  36. data/spec/interfaces/udp_interface_spec.rb +0 -9
  37. data/spec/packets/packet_config_spec.rb +21 -144
  38. data/spec/packets/packet_item_spec.rb +68 -4
  39. data/spec/packets/parsers/packet_item_parser_spec.rb +12 -0
  40. data/spec/packets/parsers/xtce_parser_spec.rb +398 -0
  41. data/spec/tools/tlm_viewer/tlm_viewer_config_spec.rb +401 -0
  42. 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
- config << " ID_ITEM #{self.name.to_s.quote_if_necessary} #{self.bit_offset} #{self.bit_size} #{self.data_type} #{self.id_value} \"#{self.description.to_s.gsub("\"", "'")}\""
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
- config << " ID_PARAMETER #{self.name.to_s.quote_if_necessary} #{self.bit_offset} #{self.bit_size} #{self.data_type} \"#{self.default}\" \"#{self.description.to_s.gsub("\"", "'")}\""
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
- config << " ID_PARAMETER #{self.name.to_s.quote_if_necessary} #{self.bit_offset} #{self.bit_size} #{self.data_type} #{self.range.first} #{self.range.last} #{self.default} \"#{self.description.to_s.gsub("\"", "'")}\""
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
- config << " PARAMETER #{self.name.to_s.quote_if_necessary} #{self.bit_offset} #{self.bit_size} #{self.data_type} \"#{self.default}\" \"#{self.description.to_s.gsub("\"", "'")}\""
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
- config << " PARAMETER #{self.name.to_s.quote_if_necessary} #{self.bit_offset} #{self.bit_size} #{self.data_type} #{self.range.first} #{self.range.last} #{self.default} \"#{self.description.to_s.gsub("\"", "'")}\""
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]} #{limits_values[4]} #{limits_values[5]}\n"
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
- end # class PacketItem
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