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