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
@@ -0,0 +1,682 @@
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 'ostruct'
13
+ require 'cosmos/packets/packet_config'
14
+
15
+ module Cosmos
16
+
17
+ class XtceParser
18
+ attr_accessor :current_target_name
19
+
20
+ # Processes a XTCE formatted COSMOS configuration file
21
+ #
22
+ # @param commands [Hash<String=>Packet>] Hash of all the command packets
23
+ # keyed by the packet name.
24
+ # @param telemetry [Hash<String=>Packet>] Hash of all the telemetry packets
25
+ # keyed by the packet name.
26
+ # @param warnings [Array<String>] Array of strings listing all the warnings
27
+ # that were created while parsing the configuration
28
+ # @param filename [String] The name of the configuration file
29
+ # @param target_name [String] The target name
30
+ def self.process(commands, telemetry, warnings, filename, target_name)
31
+ XtceParser.new(commands, telemetry, warnings, filename, target_name)
32
+ end
33
+
34
+ def self.reverse_packet_order(target_name, cmd_or_tlm_hash)
35
+ if cmd_or_tlm_hash[target_name]
36
+ packets = []
37
+ names_to_remove = []
38
+ cmd_or_tlm_hash[target_name].each do |packet_name, packet|
39
+ packets << packet
40
+ names_to_remove << packet_name
41
+ end
42
+ cmd_or_tlm_hash[target_name].length.times do |i|
43
+ cmd_or_tlm_hash[target_name].delete(names_to_remove[i])
44
+ end
45
+ packets.reverse.each do |packet|
46
+ cmd_or_tlm_hash[target_name][packet.packet_name] = packet
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def initialize(commands, telemetry, warnings, filename, target_name)
54
+ reset_processing_variables()
55
+ @commands = commands
56
+ @telemetry = telemetry
57
+ @warnings = warnings
58
+ parse(filename, target_name)
59
+ end
60
+
61
+ def parse(filename, target_name)
62
+ doc = File.open(filename) { |f| Nokogiri::XML(f, nil, nil, Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NOBLANKS) }
63
+ xtce_process_element(doc.root, 0)
64
+ @current_target_name = target_name if target_name
65
+ doc.root.children.each do |child|
66
+ xtce_recurse_element(child, 1) do |element, depth|
67
+ xtce_process_element(element, depth)
68
+ end
69
+ end
70
+ finish_packet()
71
+
72
+ # Remove abstract
73
+ if @commands[@current_target_name]
74
+ @commands[@current_target_name].delete_if {|packet_name, packet| packet.abstract}
75
+ end
76
+ if @telemetry[@current_target_name]
77
+ @telemetry[@current_target_name].delete_if {|packet_name, packet| packet.abstract}
78
+ end
79
+
80
+ # Reverse order of packets for the target so ids work correctly
81
+ XtceParser.reverse_packet_order(@current_target_name, @commands)
82
+ XtceParser.reverse_packet_order(@current_target_name, @telemetry)
83
+
84
+ reset_processing_variables()
85
+ end
86
+
87
+ # Add current packet into hash if it exists
88
+ def finish_packet()
89
+ if @current_packet
90
+ @warnings += @current_packet.check_bit_offsets
91
+ set_packet_endianness()
92
+ if @current_cmd_or_tlm == PacketConfig::COMMAND
93
+ PacketParser.check_item_data_types(@current_packet)
94
+ @commands[@current_packet.target_name][@current_packet.packet_name] = @current_packet
95
+ else
96
+ @telemetry[@current_packet.target_name][@current_packet.packet_name] = @current_packet
97
+ end
98
+ @current_packet = nil
99
+ end
100
+ end
101
+
102
+ def set_packet_endianness
103
+ item_endianness = @current_packet.sorted_items.collect { |item| item.endianness }.uniq
104
+ if item_endianness.length == 1 # All items have the same endianness
105
+ # default_endianness is read_only since it affects how items are added
106
+ # thus we have to use instance_variable_set here to override it
107
+ @current_packet.instance_variable_set(:@default_endianness, item_endianness[0])
108
+ end
109
+ end
110
+
111
+ def reset_processing_variables
112
+ @current_target_name = nil
113
+ @current_cmd_or_tlm = nil
114
+ @current_type = nil
115
+ @current_meta_command = nil
116
+ @current_parameter = nil
117
+ @current_argument = nil
118
+ @parameter_types = {}
119
+ @argument_types = {}
120
+ @parameters = {}
121
+ @arguments = {}
122
+ @containers = {}
123
+ end
124
+
125
+ def create_new_type(element)
126
+ current_type = OpenStruct.new
127
+ element.attributes.each do |att_name, att|
128
+ current_type[att.name] = att.value
129
+ end
130
+ if element.name =~ /Argument/
131
+ @argument_types[element["name"]] = current_type
132
+ else
133
+ @parameter_types[element["name"]] = current_type
134
+ end
135
+ current_type
136
+ end
137
+
138
+ XTCE_IGNORED_ELEMENTS = ['text', 'AliasSet', 'Alias', 'Header']
139
+
140
+ def xtce_process_element(element, depth)
141
+ if XTCE_IGNORED_ELEMENTS.include?(element.name)
142
+ return false
143
+ end
144
+
145
+ case element.name
146
+ when 'SpaceSystem'
147
+ @current_target_name = element["name"].to_s.upcase
148
+
149
+ when 'TelemetryMetaData'
150
+ finish_packet()
151
+ @current_cmd_or_tlm = PacketConfig::TELEMETRY
152
+
153
+ when 'CommandMetaData'
154
+ finish_packet()
155
+ @current_cmd_or_tlm = PacketConfig::COMMAND
156
+
157
+ when 'ParameterTypeSet', 'EnumerationList', 'ParameterSet', 'ContainerSet', 'EntryList', 'DefaultCalibrator', 'DefaultAlarm',
158
+ 'RestrictionCriteria', 'ComparisonList', 'MetaCommandSet', 'DefaultCalibrator', 'ArgumentTypeSet', 'ArgumentList', 'ArgumentAssignmentList',
159
+ 'LocationInContainerInBits'
160
+
161
+ # Do Nothing
162
+
163
+ when 'EnumeratedParameterType', 'EnumeratedArgumentType', 'IntegerParameterType', 'IntegerArgumentType', 'FloatParameterType', 'FloatArgumentType',
164
+ 'StringParameterType', 'StringArgumentType', 'BinaryParameterType', 'BinaryArgumentType'
165
+ @current_type = create_new_type(element)
166
+ @current_type.endianness = :BIG_ENDIAN
167
+
168
+ case element.name
169
+ when 'EnumeratedParameterType', 'EnumeratedArgumentType'
170
+ @current_type.xtce_encoding = 'IntegerDataEncoding'
171
+ @current_type.sizeInBits = 8 # This is undocumented but appears to be the design
172
+ when 'IntegerParameterType', 'IntegerArgumentType'
173
+ @current_type.xtce_encoding = 'IntegerDataEncoding'
174
+ @current_type.sizeInBits = 32
175
+ when 'FloatParameterType', 'FloatArgumentType'
176
+ @current_type.xtce_encoding = 'FloatDataEncoding'
177
+ @current_type.sizeInBits = 32
178
+ when 'StringParameterType', 'StringArgumentType'
179
+ @current_type.xtce_encoding = 'StringDataEncoding'
180
+ when 'BinaryParameterType', 'BinaryArgumentType'
181
+ @current_type.xtce_encoding = 'BinaryDataEncoding'
182
+ @current_type.sizeInBits = 8 # This is undocumented but appears to be the design
183
+ end
184
+
185
+ when 'ArrayParameterType', 'ArrayArgumentType'
186
+ @current_type = create_new_type(element)
187
+
188
+ when 'ByteOrderList'
189
+ byte_list = []
190
+ xtce_recurse_element(element, depth + 1) do |element, depth|
191
+ if element.name == 'Byte'
192
+ if element['byteSignificance']
193
+ byte_list << element['byteSignificance'].to_i
194
+ end
195
+ end
196
+ true
197
+ end
198
+ if byte_list[0] == 0
199
+ # Little endian will always start with 0 - Its ok if a single byte item is marked little endian
200
+ @current_type.endianness = :LITTLE_ENDIAN
201
+ end
202
+
203
+ # Verify ordering of byte list is supported
204
+ if byte_list[0] >= byte_list[-1]
205
+ ordered_byte_list = byte_list.reverse
206
+ else
207
+ ordered_byte_list = byte_list.clone
208
+ end
209
+ if ordered_byte_list[0] != 0
210
+ msg = "Invalid ByteOrderList detected: #{byte_list.join(", ")}"
211
+ Logger.instance.warn msg
212
+ @warnings << msg
213
+ else
214
+ previous_byte = nil
215
+ ordered_byte_list.each do |byte|
216
+ if previous_byte
217
+ if byte - previous_byte != 1
218
+ msg = "Invalid ByteOrderList detected: #{byte_list.join(", ")}"
219
+ Logger.instance.warn msg
220
+ @warnings << msg
221
+ break
222
+ end
223
+ end
224
+ previous_byte = byte
225
+ end
226
+ end
227
+
228
+ return false # Already recursed
229
+
230
+ when "SizeInBits"
231
+ xtce_recurse_element(element, depth + 1) do |element, depth|
232
+ if element.name == 'FixedValue'
233
+ @current_type.sizeInBits = Integer(element.text)
234
+ false
235
+ else
236
+ true
237
+ end
238
+ end
239
+ return false # Already recursed
240
+
241
+ when 'UnitSet'
242
+ xtce_recurse_element(element, depth + 1) do |element, depth|
243
+ if element.name == 'Unit'
244
+ units = element.text.to_s
245
+ description = element['description'].to_s
246
+ description = units if description.empty?
247
+ units = description if units.empty?
248
+
249
+ @current_type.units ||= ''
250
+ if @current_type.units.empty?
251
+ @current_type.units << units
252
+ else
253
+ @current_type.units << ('/' + units)
254
+ end
255
+ @current_type.units << "^#{element['power']}" if element['power']
256
+
257
+ @current_type.units_full ||= ''
258
+ if @current_type.units_full.empty?
259
+ @current_type.units_full << description
260
+ else
261
+ @current_type.units_full << ('/' + description)
262
+ end
263
+ end
264
+ true
265
+ end
266
+ return false # Already recursed
267
+
268
+ when 'PolynomialCalibrator'
269
+ xtce_recurse_element(element, depth + 1) do |element, depth|
270
+ if element.name == 'Term'
271
+ index = Float(element['exponent']).to_i
272
+ coeff = Float(element['coefficient'])
273
+ @current_type.conversion ||= PolynomialConversion.new([])
274
+ @current_type.conversion.coeffs[index] = coeff
275
+ @current_type.conversion.coeffs.each_with_index do |value, index|
276
+ @current_type.conversion.coeffs[index] = 0.0 if value.nil?
277
+ end
278
+ end
279
+ true
280
+ end
281
+ return false # Already recursed
282
+
283
+ when 'StaticAlarmRanges'
284
+ xtce_recurse_element(element, depth + 1) do |element, depth|
285
+ if element.name == 'WarningRange'
286
+ @current_type.limits ||= [0.0, 0.0, 0.0, 0.0]
287
+ @current_type.limits[1] = Float(element['minInclusive']) if element['minInclusive']
288
+ @current_type.limits[2] = Float(element['maxInclusive']) if element['maxInclusive']
289
+ elsif element.name == 'CriticalRange'
290
+ @current_type.limits ||= [0.0, 0.0, 0.0, 0.0]
291
+ @current_type.limits[0] = Float(element['minInclusive']) if element['minInclusive']
292
+ @current_type.limits[3] = Float(element['maxInclusive']) if element['maxInclusive']
293
+ end
294
+ true
295
+ end
296
+ return false # Already recursed
297
+
298
+ when "ValidRange"
299
+ @current_type.minInclusive = element['minInclusive']
300
+ @current_type.maxInclusive = element['maxInclusive']
301
+
302
+ when 'Enumeration'
303
+ @current_type.states ||= {}
304
+ @current_type.states[element['label']] = Integer(element['value'])
305
+
306
+ when 'IntegerDataEncoding', 'FloatDataEncoding', 'StringDataEncoding', 'BinaryDataEncoding'
307
+ @current_type.xtce_encoding = element.name
308
+ element.attributes.each do |att_name, att|
309
+ @current_type[att.name] = att.value
310
+ end
311
+ @current_type.sizeInBits = 8 unless element.attributes['sizeInBits']
312
+
313
+ when 'Parameter'
314
+ @current_parameter = OpenStruct.new
315
+ element.attributes.each do |att_name, att|
316
+ @current_parameter[att.name] = att.value
317
+ end
318
+ @parameters[element["name"]] = @current_parameter
319
+
320
+ when 'Argument'
321
+ @current_argument = OpenStruct.new
322
+ element.attributes.each do |att_name, att|
323
+ @current_argument[att.name] = att.value
324
+ end
325
+ @arguments[element["name"]] = @current_argument
326
+
327
+ when 'ParameterProperties'
328
+ element.attributes.each do |att_name, att|
329
+ @current_parameter[att.name] = att.value
330
+ end
331
+
332
+ when "SequenceContainer"
333
+ finish_packet()
334
+ @current_packet = Packet.new(@current_target_name, element['name'], :BIG_ENDIAN, element['shortDescription'])
335
+ @current_packet.abstract = ConfigParser.handle_true_false_nil(element['abstract'])
336
+ @containers[element['name']] = @current_packet
337
+ PacketParser.finish_create_telemetry(@current_packet, @telemetry, {}, @warnings)
338
+
339
+ # Need to check for a BaseContainer now because if we hit it later it will be too late
340
+ xtce_handle_base_container('BaseContainer', element)
341
+
342
+ when 'LongDescription'
343
+ if @current_packet && !@current_packet.description
344
+ @current_packet.description = element.text
345
+ end
346
+
347
+ when 'ParameterRefEntry', 'ArgumentRefEntry', 'ArrayParameterRefEntry', 'ArrayArgumentRefEntry'
348
+ process_ref_entry(element, depth)
349
+ return false # Already recursed
350
+
351
+ when 'BaseContainer'
352
+ # Handled in SequenceContainer/CommandContainer
353
+
354
+ when 'BaseMetaCommand'
355
+ # Handled in MetaCommand
356
+
357
+ when 'Comparison'
358
+ # Need to set ID value for item
359
+ item = @current_packet.get_item(element['parameterRef'])
360
+ item.id_value = Integer(element['value'])
361
+ if @current_cmd_or_tlm == PacketConfig::COMMAND
362
+ item.default = item.id_value
363
+ end
364
+ @current_packet.update_id_items(item)
365
+
366
+ when 'MetaCommand'
367
+ finish_packet()
368
+ @current_packet = Packet.new(@current_target_name, element['name'], :BIG_ENDIAN, element['shortDescription'])
369
+ @current_packet.abstract = ConfigParser.handle_true_false_nil(element['abstract'])
370
+ PacketParser.finish_create_command(@current_packet, @commands, @warnings)
371
+
372
+ # Need to check for a BaseContainer now because if we hit it later it will be too late
373
+ xtce_handle_base_container('BaseMetaCommand', element)
374
+
375
+ when 'CommandContainer'
376
+ @containers[element['name']] = @current_packet
377
+
378
+ # Need to check for a BaseContainer now because if we hit it later it will be too late
379
+ xtce_handle_base_container('BaseContainer', element)
380
+
381
+ when 'ArgumentAssignment'
382
+ # Need to set ID value for item
383
+ item = @current_packet.get_item(element['argumentName'])
384
+ value = element['argumentValue']
385
+ if item.states && item.states[value.to_s.upcase]
386
+ item.id_value = item.states[value.to_s.upcase]
387
+ item.default = item.id_value
388
+ else
389
+ item.id_value = Integer(value)
390
+ item.default = item.id_value
391
+ end
392
+ @current_packet.update_id_items(item)
393
+
394
+ else
395
+ puts " Ignoring Unknown: <#{element.name}>"
396
+
397
+ end # case element.name
398
+
399
+ return true # Recurse further
400
+ end
401
+
402
+ def process_ref_entry(element, depth)
403
+ reference_location, bit_offset = xtce_handle_location_in_container_in_bits(element)
404
+ object, type, data_type, array_type = get_object_types(element)
405
+ bit_size = Integer(type.sizeInBits)
406
+ if array_type
407
+ array_bit_size = process_array_type(element, depth, bit_size)
408
+ else
409
+ array_bit_size = nil # in define_item, nil indicates the item is not an array
410
+ end
411
+
412
+ if bit_offset
413
+ case reference_location
414
+ when 'containerStart'
415
+ item = @current_packet.define_item(object.name, bit_offset, bit_size, data_type, array_bit_size, type.endianness) # overflow = :ERROR, format_string = nil, read_conversion = nil, write_conversion = nil, id_value = nil)
416
+ when 'containerEnd'
417
+ item = @current_packet.define_item(object.name, -bit_offset, bit_size, data_type, array_bit_size, type.endianness) # overflow = :ERROR, format_string = nil, read_conversion = nil, write_conversion = nil, id_value = nil)
418
+ when 'previousEntry', nil
419
+ item = @current_packet.define_item(object.name, @current_packet.length + bit_offset, bit_size, data_type, array_bit_size, type.endianness) # overflow = :ERROR, format_string = nil, read_conversion = nil, write_conversion = nil, id_value = nil)
420
+ when 'nextEntry'
421
+ raise 'nextEntry is not supported'
422
+ end
423
+ else
424
+ item = @current_packet.append_item(object.name, bit_size, data_type, array_bit_size, type.endianness) # overflow = :ERROR, format_string = nil, read_conversion = nil, write_conversion = nil, id_value = nil)
425
+ end
426
+
427
+ item.description = type.shortDescription if type.shortDescription
428
+ item.states = type.states if type.states
429
+ set_units(item, type)
430
+ set_conversion(item, type, data_type)
431
+ set_min_max_default(item, type, data_type)
432
+ set_limits(item, type)
433
+ end
434
+
435
+ def get_object_types(element)
436
+ array_type = nil
437
+ if element.name =~ /Parameter/
438
+ # Look up the parameter and parameter type
439
+ parameter = @parameters[element['parameterRef']]
440
+ raise "parameterRef #{element['parameterRef']} not found" unless parameter
441
+ parameter_type = @parameter_types[parameter.parameterTypeRef]
442
+ raise "parameterTypeRef #{parameter.parameterTypeRef} not found" unless parameter_type
443
+ if element.name == 'ArrayParameterRefEntry'
444
+ array_type = parameter_type
445
+ parameter_type = @parameter_types[array_type.arrayTypeRef]
446
+ raise "arrayTypeRef #{parameter.arrayTypeRef} not found" unless parameter_type
447
+ end
448
+ refName = 'parameterRef'
449
+ object = parameter
450
+ type = parameter_type
451
+ else
452
+ # Look up the argument and argument type
453
+ if element.name == 'ArrayArgumentRefEntry'
454
+ # Requiring parameterRef for argument arrays appears to be a defect in the schema
455
+ argument = @arguments[element['parameterRef']]
456
+ raise "parameterRef #{element['parameterRef']} not found" unless argument
457
+ argument_type = @argument_types[argument.argumentTypeRef]
458
+ raise "argumentTypeRef #{argument.argumentTypeRef} not found" unless argument_type
459
+ array_type = argument_type
460
+ argument_type = @argument_types[array_type.arrayTypeRef]
461
+ raise "arrayTypeRef #{array_type.arrayTypeRef} not found" unless argument_type
462
+ refName = 'parameterRef'
463
+ else
464
+ argument = @arguments[element['argumentRef']]
465
+ raise "argumentRef #{element['argumentRef']} not found" unless argument
466
+ argument_type = @argument_types[argument.argumentTypeRef]
467
+ raise "argumentTypeRef #{argument.argumentTypeRef} not found" unless argument_type
468
+ refName = 'argumentRef'
469
+ end
470
+ object = argument
471
+ type = argument_type
472
+ end
473
+
474
+ data_type = get_data_type(type)
475
+ raise "Referenced Parameter/Argument has no xtce_encoding: #{element[refName]}" unless data_type
476
+
477
+ return [object, type, data_type, array_type]
478
+ end
479
+
480
+ def process_array_type(element, depth, bit_size)
481
+ array_num_items = 1
482
+ # Need to determine dimensions
483
+ xtce_recurse_element(element, depth + 1) do |element, depth|
484
+ if element.name == 'Dimension'
485
+ starting_index = 0
486
+ ending_index = 0
487
+ element.children.each do |child_element|
488
+ if child_element.name == 'StartingIndex'
489
+ child_element.children.each do |child_element2|
490
+ if child_element2.name == 'FixedValue'
491
+ starting_index = child_element2.text.to_i
492
+ end
493
+ end
494
+ elsif child_element.name == 'EndingIndex'
495
+ child_element.children.each do |child_element2|
496
+ if child_element2.name == 'FixedValue'
497
+ ending_index = child_element2.text.to_i
498
+ end
499
+ end
500
+ array_num_items *= ((ending_index - starting_index).abs + 1)
501
+ end
502
+ false # Don't recurse again
503
+ end
504
+ false # Don't recurse again
505
+ else
506
+ true # Keep recursing
507
+ end
508
+ end
509
+ array_num_items * bit_size
510
+ end
511
+
512
+ def get_data_type(type)
513
+ data_type = nil
514
+ case type.xtce_encoding
515
+ when 'IntegerDataEncoding'
516
+ if type.signed == 'false' || type.encoding == 'unsigned'
517
+ data_type = :UINT
518
+ else
519
+ data_type = :INT
520
+ end
521
+ when 'FloatDataEncoding'
522
+ data_type = :FLOAT
523
+ when 'StringDataEncoding'
524
+ data_type = :STRING
525
+ when 'BinaryDataEncoding'
526
+ data_type = :BLOCK
527
+ end
528
+ data_type
529
+ end
530
+
531
+ def set_units(item, type)
532
+ if type.units && type.units_full
533
+ item.units = type.units
534
+ item.units_full = type.units_full
535
+ end
536
+ end
537
+
538
+ def set_conversion(item, type, data_type)
539
+ if type.conversion && type.conversion.class == PolynomialConversion
540
+ if @current_cmd_or_tlm == PacketConfig::COMMAND
541
+ item.write_conversion = type.conversion
542
+ else
543
+ item.read_conversion = type.conversion
544
+ end
545
+ end
546
+ end
547
+
548
+ def set_min_max_default(item, type, data_type)
549
+ return unless @current_cmd_or_tlm == PacketConfig::COMMAND
550
+ # Need to set min, max, and default
551
+ if data_type == :INT || data_type == :UINT
552
+ if data_type == :INT
553
+ item.range = (-(2 ** (Integer(type.sizeInBits) - 1)))..((2 ** (Integer(type.sizeInBits) - 1)) - 1)
554
+ else
555
+ item.range = 0..((2 ** Integer(type.sizeInBits)) - 1)
556
+ end
557
+ if type.minInclusive && type.maxInclusive
558
+ item.range = Integer(type.minInclusive)..Integer(type.maxInclusive)
559
+ end
560
+ if item.array_size
561
+ item.default = []
562
+ else
563
+ item.default = 0
564
+ if item.states && item.states[type.initialValue.to_s.upcase]
565
+ item.default = Integer(item.states[type.initialValue.to_s.upcase])
566
+ else
567
+ item.default = Integer(type.initialValue) if type.initialValue
568
+ end
569
+ end
570
+ elsif data_type == :FLOAT
571
+ if Integer(type.sizeInBits) == 32
572
+ item.range = -3.402823e38..3.402823e38
573
+ else
574
+ item.range = -Float::MAX..Float::MAX
575
+ end
576
+ if type.minInclusive && type.maxInclusive
577
+ item.range = Float(type.minInclusive)..Float(type.maxInclusive)
578
+ end
579
+ if item.array_size
580
+ item.default = []
581
+ else
582
+ item.default = 0.0
583
+ item.default = Float(type.initialValue) if type.initialValue
584
+ end
585
+ elsif data_type == :STRING || data_type == :BLOCK
586
+ if item.array_size
587
+ item.default = []
588
+ else
589
+ if type.initialValue
590
+ if type.initialValue.upcase.start_with?("0X")
591
+ item.default = type.initialValue.hex_to_byte_string
592
+ else
593
+ # Strip quotes from strings
594
+ if type.initialValue[0] == '"' && type.initialValue[-1] == '"'
595
+ item.default = type.initialValue[1..-2]
596
+ end
597
+ end
598
+ else
599
+ item.default = ''
600
+ end
601
+ end
602
+ end
603
+ end
604
+
605
+ def set_limits(item, type)
606
+ return unless @current_cmd_or_tlm == PacketConfig::TELEMETRY
607
+ if type.limits
608
+ item.limits.enabled = true
609
+ values = {}
610
+ values[:DEFAULT] = type.limits
611
+ item.limits.values = values
612
+ end
613
+ end
614
+
615
+ def xtce_format_attributes(element)
616
+ string = ''
617
+ element.attributes.each do |att_name, att|
618
+ string << "#{att.name}:#{att.value} "
619
+ end
620
+ if string.length > 0
621
+ string = '( ' + string + ')'
622
+ end
623
+ return string
624
+ end
625
+
626
+ def xtce_recurse_element(element, depth, &block)
627
+ return unless yield(element, depth)
628
+ element.children.each do |child_element|
629
+ xtce_recurse_element(child_element, depth + 1, &block)
630
+ end
631
+ end
632
+
633
+ def xtce_handle_base_container(base_name, element)
634
+ if element.name == base_name
635
+ # Need to add BaseContainer items to current_packet
636
+ # Lookup the base packet
637
+ if base_name == 'BaseMetaCommand'
638
+ base_packet = @commands[@current_packet.target_name][element['metaCommandRef'].to_s.upcase]
639
+ else
640
+ base_packet = @containers[element['containerRef']]
641
+ end
642
+ if base_packet
643
+ count = 0
644
+ base_packet.sorted_items.each do |item|
645
+ unless ['RECEIVED_TIMESECONDS', 'RECEIVED_TIMEFORMATTED', 'RECEIVED_COUNT'].include?(item.name)
646
+ begin
647
+ @current_packet.get_item(item.name)
648
+ rescue
649
+ # Item hasn't already been added so define it
650
+ @current_packet.define(item.clone)
651
+ count += 1
652
+ end
653
+ end
654
+ end
655
+ return
656
+ else
657
+ if base_name == 'BaseMetaCommand'
658
+ raise "Unknown #{base_name}: #{element['metaCommandRef']}"
659
+ else
660
+ raise "Unknown #{base_name}: #{element['containerRef']}"
661
+ end
662
+ end
663
+ end
664
+ element.children.each do |child_element|
665
+ xtce_handle_base_container(base_name, child_element)
666
+ end
667
+ end
668
+
669
+ def xtce_handle_location_in_container_in_bits(element)
670
+ element.children.each do |child_element|
671
+ if child_element.name == 'LocationInContainerInBits'
672
+ child_element.children.each do |child_element2|
673
+ if child_element2.name == 'FixedValue'
674
+ return [child_element['referenceLocation'], Integer(child_element2.text)]
675
+ end
676
+ end
677
+ end
678
+ end
679
+ return [nil, nil]
680
+ end
681
+ end
682
+ end