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
@@ -19,9 +19,31 @@ module Cosmos
19
19
  ERROR = "ERROR" # on CRC mismatch
20
20
  DISCONNECT = "DISCONNECT" # on CRC mismatch
21
21
 
22
- def initialize(write_item_name, strip_crc, bad_strategy, bit_offset,
23
- bit_size = 32, endianness = 'BIG_ENDIAN',
24
- poly = nil, seed = nil, xor = nil, reflect = nil)
22
+ # @param write_item_name [String/nil] Item to fill with calculated CRC value for outgoing packets (nil = don't fill)
23
+ # @param strip_crc [Boolean] Whether or not to remove the CRC from incoming packets
24
+ # @param bad_strategy [ERROR/DISCONNECT] How to handle CRC errors on incoming packets. ERROR = Just log the error, DISCONNECT = Disconnect interface
25
+ # @param bit_offset [Integer] Bit offset of the CRC in the data. Can be negative to indicate distance from end of packet
26
+ # @param bit_size [Integer] Bit size of the CRC - Must be 16, 32, or 64
27
+ # @param endianness [BIG_ENDIAN/LITTLE_ENDIAN] Endianness of the CRC
28
+ # @param poly [Integer] Polynomial to use when calculating the CRC
29
+ # @param seed [Integer] Seed value to start the calculation
30
+ # @param xor [Boolean] Whether to XOR the CRC result with 0xFFFF
31
+ # @param reflect [Boolean] Whether to bit reverse each byte of data before calculating the CRC
32
+ # @param allow_empty_data [true/false/nil] See Protocol#initialize
33
+ def initialize(
34
+ write_item_name = nil,
35
+ strip_crc = false,
36
+ bad_strategy = "ERROR",
37
+ bit_offset = -32,
38
+ bit_size = 32,
39
+ endianness = 'BIG_ENDIAN',
40
+ poly = nil,
41
+ seed = nil,
42
+ xor = nil,
43
+ reflect = nil,
44
+ allow_empty_data = nil
45
+ )
46
+ super(allow_empty_data)
25
47
  @write_item_name = ConfigParser.handle_nil(write_item_name)
26
48
  @strip_crc = ConfigParser.handle_true_false(strip_crc)
27
49
  raise "Invalid strip CRC of '#{strip_crc}'. Must be TRUE or FALSE." unless !!@strip_crc == @strip_crc
@@ -95,6 +117,8 @@ module Cosmos
95
117
  end
96
118
 
97
119
  def read_data(data)
120
+ return super(data) if (data.length <= 0)
121
+
98
122
  crc = BinaryAccessor.read(@bit_offset, @bit_size, :UINT, data, @endianness)
99
123
  calculated_crc = @crc.calc(data[0...(@bit_offset / 8)])
100
124
  if calculated_crc != crc
@@ -25,15 +25,17 @@ module Cosmos
25
25
  # telemetry (true) or commands (false)
26
26
  # @param fill_fields (see BurstProtocol#initialize)
27
27
  # @param unknown_raise Whether to raise an exception on an unknown packet
28
+ # @param allow_empty_data [true/false/nil] See Protocol#initialize
28
29
  def initialize(
29
30
  min_id_size,
30
31
  discard_leading_bytes = 0,
31
32
  sync_pattern = nil,
32
33
  telemetry = true,
33
34
  fill_fields = false,
34
- unknown_raise = false
35
+ unknown_raise = false,
36
+ allow_empty_data = nil
35
37
  )
36
- super(discard_leading_bytes, sync_pattern, fill_fields)
38
+ super(discard_leading_bytes, sync_pattern, fill_fields, allow_empty_data)
37
39
  @min_id_size = Integer(min_id_size)
38
40
  @telemetry = telemetry
39
41
  @unknown_raise = ConfigParser::handle_true_false(unknown_raise)
@@ -33,6 +33,7 @@ module Cosmos
33
33
  # @param max_length [Integer] The maximum allowed value of the length field
34
34
  # @param fill_length_and_sync_pattern [Boolean] Fill the length field and sync
35
35
  # pattern when writing packets
36
+ # @param allow_empty_data [true/false/nil] See Protocol#initialize
36
37
  def initialize(
37
38
  length_bit_offset = 0,
38
39
  length_bit_size = 16,
@@ -42,9 +43,10 @@ module Cosmos
42
43
  discard_leading_bytes = 0,
43
44
  sync_pattern = nil,
44
45
  max_length = nil,
45
- fill_length_and_sync_pattern = false
46
+ fill_length_and_sync_pattern = false,
47
+ allow_empty_data = nil
46
48
  )
47
- super(discard_leading_bytes, sync_pattern, fill_length_and_sync_pattern)
49
+ super(discard_leading_bytes, sync_pattern, fill_length_and_sync_pattern, allow_empty_data)
48
50
 
49
51
  # Save length field attributes
50
52
  @length_bit_offset = Integer(length_bit_offset)
@@ -17,8 +17,8 @@ module Cosmos
17
17
  # methods. Clearing the override requires calling normalize_tlm.
18
18
  class OverrideProtocol < Protocol
19
19
 
20
- # @param allow_empty_data [true/false] Whether STOP should be returned on empty data
21
- def initialize(allow_empty_data = false)
20
+ # @param allow_empty_data [true/false/nil] See Protocol#initialize
21
+ def initialize(allow_empty_data = nil)
22
22
  super(allow_empty_data)
23
23
  end
24
24
 
@@ -16,8 +16,9 @@ module Cosmos
16
16
 
17
17
  # @param sync_pattern (see BurstProtocol#initialize)
18
18
  # @param max_length [Integer] The maximum allowed value of the length field
19
- def initialize(sync_pattern = nil, max_length = nil)
20
- super(0, sync_pattern)
19
+ # @param allow_empty_data [true/false/nil] See Protocol#initialize
20
+ def initialize(sync_pattern = nil, max_length = nil, allow_empty_data = nil)
21
+ super(0, sync_pattern, false, allow_empty_data)
21
22
  @max_length = ConfigParser.handle_nil(max_length)
22
23
  @max_length = Integer(@max_length) if @max_length
23
24
  end
@@ -17,10 +17,12 @@ module Cosmos
17
17
  attr_accessor :interface
18
18
  attr_accessor :allow_empty_data
19
19
 
20
- # @param allow_empty_data [true/false] Whether STOP should be returned on empty data
21
- def initialize(allow_empty_data = false)
20
+ # @param allow_empty_data [true/false/nil] Whether or not this protocol will allow an empty string
21
+ # to be passed down to later Protocols (instead of returning :STOP). Can be true, false, or nil, where
22
+ # nil is interpreted as true unless the Protocol is the last Protocol of the chain.
23
+ def initialize(allow_empty_data = nil)
22
24
  @interface = nil
23
- @allow_empty_data = ConfigParser.handle_true_false(allow_empty_data)
25
+ @allow_empty_data = ConfigParser.handle_true_false_nil(allow_empty_data)
24
26
  reset()
25
27
  end
26
28
 
@@ -37,7 +39,17 @@ module Cosmos
37
39
 
38
40
  # Ensure we have some data in case this is the only protocol
39
41
  def read_data(data)
40
- return :STOP if (data.length <= 0) && !@allow_empty_data
42
+ if (data.length <= 0)
43
+ if @allow_empty_data.nil?
44
+ if @interface and @interface.read_protocols[-1] == self
45
+ # Last read interface in chain with auto @allow_empty_data
46
+ return :STOP
47
+ end
48
+ elsif !@allow_empty_data
49
+ # Don't @allow_empty_data means STOP
50
+ return :STOP
51
+ end
52
+ end
41
53
  data
42
54
  end
43
55
 
@@ -36,6 +36,7 @@ module Cosmos
36
36
  # for a response
37
37
  # @param raise_exceptions [String] Whether to raise exceptions when errors
38
38
  # occur in the protocol like unexpected responses or response timeouts.
39
+ # @param allow_empty_data [true/false/nil] See Protocol#initialize
39
40
  def initialize(
40
41
  write_termination_characters,
41
42
  read_termination_characters,
@@ -48,7 +49,8 @@ module Cosmos
48
49
  fill_fields = false,
49
50
  response_timeout = 5.0,
50
51
  response_polling_period = 0.02,
51
- raise_exceptions = false
52
+ raise_exceptions = false,
53
+ allow_empty_data = nil
52
54
  )
53
55
  super(
54
56
  write_termination_characters,
@@ -56,7 +58,8 @@ module Cosmos
56
58
  strip_read_termination,
57
59
  discard_leading_bytes,
58
60
  sync_pattern,
59
- fill_fields)
61
+ fill_fields,
62
+ allow_empty_data)
60
63
  @response_template = nil
61
64
  @response_packet = nil
62
65
  @response_packets = []
@@ -93,6 +96,8 @@ module Cosmos
93
96
  end
94
97
 
95
98
  def read_data(data)
99
+ return super(data) if (data.length <= 0)
100
+
96
101
  # Drop all data until the initial_read_delay is complete.
97
102
  # This gets rid of unused welcome messages,
98
103
  # prompts, and other junk on initial connections
@@ -27,19 +27,21 @@ module Cosmos
27
27
  # @param discard_leading_bytes (see BurstProtocol#initialize)
28
28
  # @param sync_pattern (see BurstProtocol#initialize)
29
29
  # @param fill_fields (see BurstProtocol#initialize)
30
+ # @param allow_empty_data [true/false/nil] See Protocol#initialize
30
31
  def initialize(
31
32
  write_termination_characters,
32
33
  read_termination_characters,
33
34
  strip_read_termination = true,
34
35
  discard_leading_bytes = 0,
35
36
  sync_pattern = nil,
36
- fill_fields = false
37
+ fill_fields = false,
38
+ allow_empty_data = nil
37
39
  )
38
40
  @write_termination_characters = write_termination_characters.hex_to_byte_string
39
41
  @read_termination_characters = read_termination_characters.hex_to_byte_string
40
42
  @strip_read_termination = ConfigParser.handle_true_false(strip_read_termination)
41
43
 
42
- super(discard_leading_bytes, sync_pattern, fill_fields)
44
+ super(discard_leading_bytes, sync_pattern, fill_fields, allow_empty_data)
43
45
  end
44
46
 
45
47
  def write_data(data)
@@ -18,6 +18,8 @@ require 'cosmos/packets/parsers/limits_response_parser'
18
18
  require 'cosmos/packets/parsers/state_parser'
19
19
  require 'cosmos/packets/parsers/format_string_parser'
20
20
  require 'cosmos/packets/parsers/processor_parser'
21
+ require 'cosmos/packets/parsers/xtce_parser'
22
+ require 'cosmos/packets/parsers/xtce_converter'
21
23
  require 'cosmos/conversions'
22
24
  require 'cosmos/processors'
23
25
  require 'nokogiri'
@@ -93,7 +95,7 @@ module Cosmos
93
95
  def process_file(filename, process_target_name)
94
96
  # Handle .xtce files
95
97
  if File.extname(filename).to_s.downcase == ".xtce"
96
- process_xtce(filename, process_target_name)
98
+ XtceParser.process(@commands, @telemetry, @warnings, filename, process_target_name)
97
99
  return
98
100
  end
99
101
 
@@ -205,33 +207,6 @@ module Cosmos
205
207
  finish_packet()
206
208
  end
207
209
 
208
- # Read in a target definition from a .xtce file
209
- def process_xtce(filename, override_target_name = nil)
210
- doc = File.open(filename) { |f| Nokogiri::XML(f, nil, nil, Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NOBLANKS) }
211
- xtce_process_element(doc.root, 0)
212
- @current_target_name = override_target_name if override_target_name
213
- doc.root.children.each do |child|
214
- xtce_recurse_element(child, 1) do |element, depth|
215
- xtce_process_element(element, depth)
216
- end
217
- end
218
- finish_packet()
219
-
220
- # Remove abstract
221
- if @commands[@current_target_name]
222
- @commands[@current_target_name].delete_if {|packet_name, packet| packet.abstract}
223
- end
224
- if @telemetry[@current_target_name]
225
- @telemetry[@current_target_name].delete_if {|packet_name, packet| packet.abstract}
226
- end
227
-
228
- # Reverse order of packets for the target so ids work correctly
229
- reverse_packet_order(@current_target_name, @commands)
230
- reverse_packet_order(@current_target_name, @telemetry)
231
-
232
- reset_processing_variables()
233
- end
234
-
235
210
  # Convert the PacketConfig back to COSMOS configuration files for each target
236
211
  def to_config(output_dir)
237
212
  FileUtils.mkdir_p(output_dir)
@@ -284,832 +259,35 @@ module Cosmos
284
259
  end
285
260
  end
286
261
  end
287
-
288
262
  end # def to_config
289
263
 
290
- # Convert the PacketConfig into a .xtce file for each target
291
264
  def to_xtce(output_dir)
292
- FileUtils.mkdir_p(output_dir)
293
-
294
- # Build target list
295
- targets = []
296
- @telemetry.each { |target_name, packets| targets << target_name }
297
- @commands.each { |target_name, packets| targets << target_name }
298
- targets.uniq!
299
-
300
- targets.each do |target_name|
301
- next if target_name == 'UNKNOWN'
302
-
303
- # Reverse order of packets for the target so things are expected (reverse) order for xtce
304
- reverse_packet_order(target_name, @commands)
305
- reverse_packet_order(target_name, @telemetry)
306
-
307
- FileUtils.mkdir_p(File.join(output_dir, target_name, 'cmd_tlm'))
308
- filename = File.join(output_dir, target_name, 'cmd_tlm', target_name.downcase + '.xtce')
309
- begin
310
- File.delete(filename)
311
- rescue
312
- # Doesn't exist
313
- end
314
-
315
- # Gather an make unique all the packet items
316
- unique_items = {}
317
- @telemetry[target_name].each do |packet_name, packet|
318
- packet.sorted_items.each do |item|
319
- next if item.data_type == :DERIVED
320
- unique_items[item.name] ||= []
321
- unique_items[item.name] << item
322
- end
323
- end
324
- unique_items.each do |item_name, items|
325
- if items.length <= 1
326
- unique_items[item_name] = items[0]
327
- next
328
- end
329
- # TODO: need to make sure all the items in the array are exactly the same
330
- unique_items[item_name] = items[0]
331
- end
332
-
333
- # Gather and make unique all the command parameters
334
- unique_arguments = {}
335
- @commands[target_name].each do |packet_name, packet|
336
- packet.sorted_items.each do |item|
337
- next if item.data_type == :DERIVED
338
- unique_arguments[item.name] ||= []
339
- unique_arguments[item.name] << item
340
- end
341
- end
342
- unique_arguments.each do |item_name, items|
343
- if items.length <= 1
344
- unique_arguments[item_name] = items[0]
345
- next
346
- end
347
- # TODO: need to make sure all the items in the array are exactly the same
348
- unique_arguments[item_name] = items[0]
349
- end
350
-
351
- # Create the xtce file for this target
352
- builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
353
- xml['xtce'].SpaceSystem("xmlns:xtce" => "http://www.omg.org/space/xtce",
354
- "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
355
- "name" => target_name,
356
- "xsi:schemaLocation" => "http://www.omg.org/space/xtce http://www.omg.org/spec/XTCE/20061101/06-11-06.xsd") do
357
- xml['xtce'].TelemetryMetaData do
358
- xml['xtce'].ParameterTypeSet do
359
- unique_items.each do |item_name, item|
360
- item.to_xtce_type('Parameter', xml)
361
- end
362
- end
363
-
364
- xml['xtce'].ParameterSet do
365
- unique_items.each do |item_name, item|
366
- item.to_xtce_item('Parameter', xml)
367
- end
368
- end
265
+ XtceConverter.convert(@commands, @telemetry, output_dir)
266
+ end
369
267
 
370
- xml['xtce'].ContainerSet do
371
- @telemetry[target_name].each do |packet_name, packet|
372
- attrs = { :name => (packet_name + '_Base'), :abstract => "true" }
373
- xml['xtce'].SequenceContainer(attrs) do
374
- xml['xtce'].EntryList do
375
- packed = packet.packed?
376
- packet.sorted_items.each do |item|
377
- next if item.data_type == :DERIVED
378
- # TODO: Handle nonunique item names
379
- if item.array_size
380
- xml['xtce'].ArrayParameterRefEntry(:parameterRef => item.name) do
381
- if !packed
382
- if item.bit_offset >= 0
383
- xml['xtce'].LocationInContainerInBits(:referenceLocation => 'containerStart') do
384
- xml['xtce'].FixedValue(item.bit_offset)
385
- end
386
- else
387
- xml['xtce'].LocationInContainerInBits(:referenceLocation => 'containerEnd') do
388
- xml['xtce'].FixedValue(-item.bit_offset)
389
- end
390
- end
391
- end
392
- xml['xtce'].DimensionList do
393
- xml['xtce'].Dimension do
394
- xml['xtce'].StartingIndex do
395
- xml['xtce'].FixedValue(0)
396
- end
397
- xml['xtce'].EndingIndex do
398
- xml['xtce'].FixedValue((item.array_size / item.bit_size) - 1)
399
- end
400
- end
401
- end
402
- end
403
- else
404
- if packed
405
- xml['xtce'].ParameterRefEntry(:parameterRef => item.name)
406
- else
407
- xml['xtce'].ParameterRefEntry(:parameterRef => item.name) do
408
- if item.bit_offset >= 0
409
- xml['xtce'].LocationInContainerInBits(:referenceLocation => 'containerStart') do
410
- xml['xtce'].FixedValue(item.bit_offset)
411
- end
412
- else
413
- xml['xtce'].LocationInContainerInBits(:referenceLocation => 'containerEnd') do
414
- xml['xtce'].FixedValue(-item.bit_offset)
415
- end
416
- end
417
- end
418
- end
419
- end
420
- end
421
- end
422
- end # Abstract SequenceContainer
423
-
424
- attrs = { :name => packet_name }
425
- attrs['shortDescription'] = packet.description if packet.description
426
- xml['xtce'].SequenceContainer(attrs) do
427
- xml['xtce'].EntryList
428
- xml['xtce'].BaseContainer(:containerRef => (packet_name + '_Base')) do
429
- if packet.id_items and packet.id_items.length > 0
430
- xml['xtce'].RestrictionCriteria do
431
- xml['xtce'].ComparisonList do
432
- packet.id_items.each do |item|
433
- xml['xtce'].Comparison(:parameterRef => item.name, :value => item.id_value)
434
- end
435
- end
436
- end
437
- end
438
- end
439
- end # Actual SequenceContainer
440
-
441
- end # @telemetry.each
442
- end # ContainerSet
443
- end # TelemetryMetaData
444
-
445
- xml['xtce'].CommandMetaData do
446
- xml['xtce'].ArgumentTypeSet do
447
- unique_arguments.each do |arg_name, arg|
448
- arg.to_xtce_type('Argument', xml)
449
- end
450
- end
451
- xml['xtce'].MetaCommandSet do
452
- @commands[target_name].each do |packet_name, packet|
453
- attrs = { :name => packet_name + "_Base", :abstract => "true" }
454
- xml['xtce'].MetaCommand(attrs) do
455
- xml['xtce'].ArgumentList do
456
- packet.sorted_items.each do |item|
457
- next if item.data_type == :DERIVED
458
- item.to_xtce_item('Argument', xml)
459
- end
460
- end # ArgumentList
461
- xml['xtce'].CommandContainer(:name => "#{target_name}_#{packet_name}_CommandContainer") do
462
- xml['xtce'].EntryList do
463
- packed = packet.packed?
464
- packet.sorted_items.each do |item|
465
- next if item.data_type == :DERIVED
466
- if item.array_size
467
- xml['xtce'].ArrayArgumentRefEntry(:parameterRef => item.name) do
468
- if !packed
469
- if item.bit_offset >= 0
470
- xml['xtce'].LocationInContainerInBits(:referenceLocation => 'containerStart') do
471
- xml['xtce'].FixedValue(item.bit_offset)
472
- end
473
- else
474
- xml['xtce'].LocationInContainerInBits(:referenceLocation => 'containerEnd') do
475
- xml['xtce'].FixedValue(-item.bit_offset)
476
- end
477
- end
478
- end
479
- xml['xtce'].DimensionList do
480
- xml['xtce'].Dimension do
481
- xml['xtce'].StartingIndex do
482
- xml['xtce'].FixedValue(0)
483
- end
484
- xml['xtce'].EndingIndex do
485
- xml['xtce'].FixedValue((item.array_size / item.bit_size) - 1)
486
- end
487
- end
488
- end
489
- end
490
- else
491
- if packed
492
- xml['xtce'].ArgumentRefEntry(:argumentRef => item.name)
493
- else
494
- xml['xtce'].ArgumentRefEntry(:argumentRef => item.name) do
495
- if item.bit_offset >= 0
496
- xml['xtce'].LocationInContainerInBits(:referenceLocation => 'containerStart') do
497
- xml['xtce'].FixedValue(item.bit_offset)
498
- end
499
- else
500
- xml['xtce'].LocationInContainerInBits(:referenceLocation => 'containerEnd') do
501
- xml['xtce'].FixedValue(-item.bit_offset)
502
- end
503
- end
504
- end
505
- end
506
- end
507
- end
508
- end
509
- end
510
- end # Abstract MetaCommand
511
-
512
- attrs = { :name => packet_name }
513
- attrs['shortDescription'] = packet.description if packet.description
514
- xml['xtce'].MetaCommand(attrs) do
515
- xml['xtce'].BaseMetaCommand(:metaCommandRef => packet_name + "_Base") do
516
- if packet.id_items and packet.id_items.length > 0
517
- xml['xtce'].ArgumentAssignmentList do
518
- packet.id_items.each do |item|
519
- xml['xtce'].ArgumentAssignment(:argumentName => item.name, :argumentValue => item.id_value)
520
- end
521
- end # ArgumentAssignmentList
522
- end
523
- end # BaseMetaCommand
524
- end # Actual MetaCommand
525
- end # @commands.each
526
- end # MetaCommandSet
527
- end # CommandMetaData
528
- end # SpaceSystem
529
- end # builder
530
- File.open(filename, 'w') do |file|
531
- file.puts builder.to_xml
268
+ # Add current packet into hash if it exists
269
+ def finish_packet()
270
+ finish_item()
271
+ if @current_packet
272
+ @warnings += @current_packet.check_bit_offsets
273
+ if @current_cmd_or_tlm == COMMAND
274
+ PacketParser.check_item_data_types(@current_packet)
275
+ @commands[@current_packet.target_name][@current_packet.packet_name] = @current_packet
276
+ else
277
+ @telemetry[@current_packet.target_name][@current_packet.packet_name] = @current_packet
532
278
  end
279
+ @current_packet = nil
280
+ @current_item = nil
533
281
  end
534
282
  end
535
283
 
536
284
  protected
537
285
 
538
286
  def reset_processing_variables
539
- # Used during packet processing
540
287
  @current_cmd_or_tlm = nil
541
288
  @current_packet = nil
542
289
  @current_item = nil
543
290
  @current_limits_group = nil
544
-
545
- # Used for xtce processing
546
- @current_target_name = nil
547
- @current_type = nil
548
- @current_meta_command = nil
549
- @current_parameter = nil
550
- @current_argument = nil
551
- @parameter_types = {}
552
- @argument_types = {}
553
- @parameters = {}
554
- @arguments = {}
555
- @containers = {}
556
- end
557
-
558
- def reverse_packet_order(target_name, cmd_or_tlm_hash)
559
- if cmd_or_tlm_hash[target_name]
560
- packets = []
561
- names_to_remove = []
562
- cmd_or_tlm_hash[target_name].each do |packet_name, packet|
563
- packets << packet
564
- names_to_remove << packet_name
565
- end
566
- cmd_or_tlm_hash[target_name].length.times do |i|
567
- cmd_or_tlm_hash[target_name].delete(names_to_remove[i])
568
- end
569
- packets.reverse.each do |packet|
570
- cmd_or_tlm_hash[target_name][packet.packet_name] = packet
571
- end
572
- end
573
- end
574
-
575
- XTCE_IGNORED_ELEMENTS = ['text', 'AliasSet', 'Alias', 'Header']
576
-
577
- def xtce_process_element(element, depth)
578
- if XTCE_IGNORED_ELEMENTS.include?(element.name)
579
- return false
580
- end
581
-
582
- case element.name
583
- when 'SpaceSystem'
584
- @current_target_name = element["name"].to_s.upcase
585
-
586
- when 'TelemetryMetaData'
587
- finish_packet()
588
- @current_cmd_or_tlm = TELEMETRY
589
-
590
- when 'CommandMetaData'
591
- finish_packet()
592
- @current_cmd_or_tlm = COMMAND
593
-
594
- when 'ParameterTypeSet', 'EnumerationList', 'ParameterSet', 'ContainerSet', 'EntryList', 'DefaultCalibrator', 'DefaultAlarm',
595
- 'RestrictionCriteria', 'ComparisonList', 'MetaCommandSet', 'DefaultCalibrator', 'ArgumentTypeSet', 'ArgumentList', 'ArgumentAssignmentList',
596
- 'LocationInContainerInBits'
597
-
598
- # Do Nothing
599
-
600
- when 'EnumeratedParameterType', 'EnumeratedArgumentType', 'IntegerParameterType', 'IntegerArgumentType', 'FloatParameterType', 'FloatArgumentType',
601
- 'StringParameterType', 'StringArgumentType', 'BinaryParameterType', 'BinaryArgumentType'
602
-
603
- @current_type = OpenStruct.new
604
- @current_type.endianness = :BIG_ENDIAN
605
- element.attributes.each do |att_name, att|
606
- @current_type[att.name] = att.value
607
- end
608
- if element.name =~ /Argument/
609
- @argument_types[element["name"]] = @current_type
610
- else
611
- @parameter_types[element["name"]] = @current_type
612
- end
613
-
614
- case element.name
615
- when 'EnumeratedParameterType', 'EnumeratedArgumentType'
616
- @current_type.xtce_encoding = 'IntegerDataEncoding'
617
- @current_type.sizeInBits = 8 # This is undocumented but appears to be the design
618
- when 'IntegerParameterType', 'IntegerArgumentType'
619
- @current_type.xtce_encoding = 'IntegerDataEncoding'
620
- @current_type.sizeInBits = 32
621
- when 'FloatParameterType', 'FloatArgumentType'
622
- @current_type.xtce_encoding = 'FloatDataEncoding'
623
- @current_type.sizeInBits = 32
624
- when 'StringParameterType', 'StringArgumentType'
625
- @current_type.xtce_encoding = 'StringDataEncoding'
626
- when 'BinaryParameterType', 'BinaryArgumentType'
627
- @current_type.xtce_encoding = 'BinaryDataEncoding'
628
- @current_type.sizeInBits = 8 # This is undocumented but appears to be the design
629
- end
630
-
631
- when 'ArrayParameterType', 'ArrayArgumentType'
632
- @current_type = OpenStruct.new
633
- element.attributes.each do |att_name, att|
634
- @current_type[att.name] = att.value
635
- end
636
- if element.name =~ /Argument/
637
- @argument_types[element["name"]] = @current_type
638
- else
639
- @parameter_types[element["name"]] = @current_type
640
- end
641
-
642
- when 'ByteOrderList'
643
- byte_list = []
644
- xtce_recurse_element(element, depth + 1) do |element, depth|
645
- if element.name == 'Byte'
646
- if element['byteSignificance']
647
- byte_list << element['byteSignificance'].to_i
648
- end
649
- end
650
- true
651
- end
652
- if byte_list[0] == 0
653
- # Little endian will always start with 0 - Its ok if a single byte item is marked little endian
654
- @current_type.endianness = :LITTLE_ENDIAN
655
- end
656
-
657
- # Verify ordering of byte list is supported
658
- if byte_list[0] >= byte_list[-1]
659
- ordered_byte_list = byte_list.reverse
660
- else
661
- ordered_byte_list = byte_list.clone
662
- end
663
- if ordered_byte_list[0] != 0
664
- msg = "Invalid ByteOrderList detected: #{byte_list.join(", ")}"
665
- Logger.instance.warn msg
666
- @warnings << msg
667
- else
668
- previous_byte = nil
669
- ordered_byte_list.each do |byte|
670
- if previous_byte
671
- if byte - previous_byte != 1
672
- msg = "Invalid ByteOrderList detected: #{byte_list.join(", ")}"
673
- Logger.instance.warn msg
674
- @warnings << msg
675
- break
676
- end
677
- end
678
- previous_byte = byte
679
- end
680
- end
681
-
682
- return false # Already recursed
683
-
684
- when "SizeInBits"
685
- xtce_recurse_element(element, depth + 1) do |element, depth|
686
- if element.name == 'FixedValue'
687
- @current_type.sizeInBits = Integer(element.text)
688
- false
689
- else
690
- true
691
- end
692
- end
693
- return false # Already recursed
694
-
695
- when 'UnitSet'
696
- xtce_recurse_element(element, depth + 1) do |element, depth|
697
- if element.name == 'Unit'
698
- @current_type.units ||= ''
699
- if @current_type.units.empty?
700
- @current_type.units << element.text.to_s
701
- else
702
- @current_type.units << ('/' + element.text.to_s)
703
- end
704
- @current_type.units << "^#{element['power']}" if element['power']
705
- @current_type.units_full ||= ''
706
- description = element['description'].to_s
707
- if description.empty?
708
- @current_type.units_full = @current_type.units
709
- else
710
- if @current_type.units_full.empty?
711
- @current_type.units_full << description
712
- else
713
- @current_type.units_full << ('/' + description)
714
- end
715
- end
716
- end
717
- true
718
- end
719
- return false # Already recursed
720
-
721
- when 'PolynomialCalibrator'
722
- xtce_recurse_element(element, depth + 1) do |element, depth|
723
- if element.name == 'Term'
724
- index = Float(element['exponent']).to_i
725
- coeff = Float(element['coefficient'])
726
- @current_type.conversion ||= PolynomialConversion.new([])
727
- @current_type.conversion.coeffs[index] = coeff
728
- @current_type.conversion.coeffs.each_with_index do |value, index|
729
- @current_type.conversion.coeffs[index] = 0.0 if value.nil?
730
- end
731
- end
732
- true
733
- end
734
- return false # Already recursed
735
-
736
- when 'StaticAlarmRanges'
737
- xtce_recurse_element(element, depth + 1) do |element, depth|
738
- if element.name == 'WarningRange'
739
- @current_type.limits ||= [0.0, 0.0, 0.0, 0.0]
740
- @current_type.limits[1] = Float(element['minInclusive']) if element['minInclusive']
741
- @current_type.limits[2] = Float(element['maxInclusive']) if element['maxInclusive']
742
- elsif element.name == 'CriticalRange'
743
- @current_type.limits ||= [0.0, 0.0, 0.0, 0.0]
744
- @current_type.limits[0] = Float(element['minInclusive']) if element['minInclusive']
745
- @current_type.limits[3] = Float(element['maxInclusive']) if element['maxInclusive']
746
- end
747
- true
748
- end
749
- return false # Already recursed
750
-
751
- when "ValidRange"
752
- @current_type.minInclusive = element['minInclusive']
753
- @current_type.maxInclusive = element['maxInclusive']
754
-
755
- when 'Enumeration'
756
- @current_type.states ||= {}
757
- @current_type.states[element['label']] = Integer(element['value'])
758
-
759
- when 'IntegerDataEncoding', 'FloatDataEncoding', 'StringDataEncoding', 'BinaryDataEncoding'
760
- @current_type.xtce_encoding = element.name
761
- element.attributes.each do |att_name, att|
762
- @current_type[att.name] = att.value
763
- end
764
- @current_type.sizeInBits = 8 unless element.attributes['sizeInBits']
765
-
766
- when 'Parameter'
767
- @current_parameter = OpenStruct.new
768
- element.attributes.each do |att_name, att|
769
- @current_parameter[att.name] = att.value
770
- end
771
- @parameters[element["name"]] = @current_parameter
772
-
773
- when 'Argument'
774
- @current_argument = OpenStruct.new
775
- element.attributes.each do |att_name, att|
776
- @current_argument[att.name] = att.value
777
- end
778
- @arguments[element["name"]] = @current_argument
779
-
780
- when 'ParameterProperties'
781
- element.attributes.each do |att_name, att|
782
- @current_parameter[att.name] = att.value
783
- end
784
-
785
- when "SequenceContainer"
786
- finish_packet()
787
- @current_packet = Packet.new(@current_target_name, element['name'], :BIG_ENDIAN, element['shortDescription'])
788
- @current_packet.abstract = ConfigParser.handle_true_false_nil(element['abstract'])
789
- @containers[element['name']] = @current_packet
790
- PacketParser.finish_create_telemetry(@current_packet, @telemetry, @latest_data, @warnings)
791
-
792
- # Need to check for a BaseContainer now because if we hit it later it will be too late
793
- xtce_handle_base_container('BaseContainer', element)
794
-
795
- when 'LongDescription'
796
- if @current_packet and !@current_packet.description
797
- @current_packet.description = element.text
798
- end
799
-
800
- when 'ParameterRefEntry', 'ArgumentRefEntry', 'ArrayParameterRefEntry', 'ArrayArgumentRefEntry'
801
- reference_location, bit_offset = xtce_handle_location_in_container_in_bits(element)
802
-
803
- array_type = nil
804
- array_bit_size = nil
805
- if element.name =~ /Parameter/
806
- # Look up the parameter and parameter type
807
- parameter = @parameters[element['parameterRef']]
808
- raise "parameterRef #{element['parameterRef']} not found" unless parameter
809
- parameter_type = @parameter_types[parameter.parameterTypeRef]
810
- raise "parameterTypeRef #{parameter.parameterTypeRef} not found" unless parameter_type
811
- if element.name == 'ArrayParameterRefEntry'
812
- array_type = parameter_type
813
- parameter_type = @parameter_types[array_type.arrayTypeRef]
814
- raise "arrayTypeRef #{parameter.arrayTypeRef} not found" unless parameter_type
815
- end
816
- refName = 'parameterRef'
817
- object = parameter
818
- type = parameter_type
819
- else
820
- # Look up the argument and argument type
821
- if element.name == 'ArrayArgumentRefEntry'
822
- # Requiring parameterRef for argument arrays appears to be a defect in the schema
823
- argument = @arguments[element['parameterRef']]
824
- raise "parameterRef #{element['parameterRef']} not found" unless argument
825
- argument_type = @argument_types[argument.argumentTypeRef]
826
- raise "argumentTypeRef #{argument.argumentTypeRef} not found" unless argument_type
827
- array_type = argument_type
828
- argument_type = @argument_types[array_type.arrayTypeRef]
829
- raise "arrayTypeRef #{array_type.arrayTypeRef} not found" unless argument_type
830
- refName = 'parameterRef'
831
- else
832
- argument = @arguments[element['argumentRef']]
833
- raise "argumentRef #{element['argumentRef']} not found" unless argument
834
- argument_type = @argument_types[argument.argumentTypeRef]
835
- raise "argumentTypeRef #{argument.argumentTypeRef} not found" unless argument_type
836
- refName = 'argumentRef'
837
- end
838
- object = argument
839
- type = argument_type
840
- end
841
-
842
- bit_size = Integer(type.sizeInBits)
843
-
844
- if array_type
845
- array_num_items = 1
846
- # Need to determine dimensions
847
- xtce_recurse_element(element, depth + 1) do |element, depth|
848
- if element.name == 'Dimension'
849
- starting_index = 0
850
- ending_index = 0
851
- element.children.each do |child_element|
852
- if child_element.name == 'StartingIndex'
853
- child_element.children.each do |child_element2|
854
- if child_element2.name == 'FixedValue'
855
- starting_index = child_element2.text.to_i
856
- end
857
- end
858
- elsif child_element.name == 'EndingIndex'
859
- child_element.children.each do |child_element2|
860
- if child_element2.name == 'FixedValue'
861
- ending_index = child_element2.text.to_i
862
- end
863
- end
864
- array_num_items *= ((ending_index - starting_index).abs + 1)
865
- end
866
- false # Don't recurse again
867
- end
868
- false # Don't recurse again
869
- else
870
- true # Keep recursing
871
- end
872
- end
873
- array_bit_size = array_num_items * bit_size
874
- end
875
-
876
- # Add item to packet
877
- data_type = nil
878
- case type.xtce_encoding
879
- when 'IntegerDataEncoding'
880
- if type.signed == 'false' or type.encoding == 'unsigned'
881
- data_type = :UINT
882
- else
883
- data_type = :INT
884
- end
885
- when 'FloatDataEncoding'
886
- data_type = :FLOAT
887
- when 'StringDataEncoding'
888
- data_type = :STRING
889
- when 'BinaryDataEncoding'
890
- data_type = :BLOCK
891
- else
892
- raise "Referenced Parameter/Argument has no xtce_encoding: #{element[refName]}"
893
- end
894
-
895
- if bit_offset
896
- case reference_location
897
- when 'containerStart'
898
- 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)
899
- when 'containerEnd'
900
- 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)
901
- when 'previousEntry', nil
902
- 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)
903
- when 'nextEntry'
904
- raise 'nextEntry is not supported'
905
- end
906
- else
907
- 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)
908
- end
909
-
910
- item.description = type.shortDescription if type.shortDescription
911
- if type.states
912
- item.states = type.states
913
- end
914
- if type.units and type.units_full
915
- item.units = type.units
916
- item.units_full = type.units_full
917
- end
918
- if @current_cmd_or_tlm == COMMAND
919
- # Possibly add write conversion
920
- if type.conversion and type.conversion.class == PolynomialConversion
921
- item.write_conversion = type.conversion
922
- end
923
-
924
- # Need to set min, max, and default
925
- if data_type == :INT or data_type == :UINT
926
- if data_type == :INT
927
- item.range = (-(2 ** (Integer(type.sizeInBits) - 1)))..((2 ** (Integer(type.sizeInBits) - 1)) - 1)
928
- else
929
- item.range = 0..((2 ** Integer(type.sizeInBits)) - 1)
930
- end
931
- if type.minInclusive and type.maxInclusive
932
- item.range = Integer(type.minInclusive)..Integer(type.maxInclusive)
933
- end
934
- if item.array_size
935
- item.default = []
936
- else
937
- item.default = 0
938
- if item.states and item.states[type.initialValue.to_s.upcase]
939
- item.default = Integer(item.states[type.initialValue.to_s.upcase])
940
- else
941
- item.default = Integer(type.initialValue) if type.initialValue
942
- end
943
- end
944
- elsif data_type == :FLOAT
945
- if Integer(type.sizeInBits) == 32
946
- item.range = -3.402823e38..3.402823e38
947
- else
948
- item.range = -Float::MAX..Float::MAX
949
- end
950
- if type.minInclusive and type.maxInclusive
951
- item.range = Float(type.minInclusive)..Float(type.maxInclusive)
952
- end
953
- if item.array_size
954
- item.default = []
955
- else
956
- item.default = 0.0
957
- item.default = Float(type.initialValue) if type.initialValue
958
- end
959
- elsif data_type == :STRING
960
- if item.array_size
961
- item.default = []
962
- else
963
- if type.initialValue
964
- item.default = type.initialValue
965
- else
966
- item.default = ''
967
- end
968
- end
969
- elsif data_type == :BLOCK
970
- if item.array_size
971
- item.default = []
972
- else
973
- if type.initialValue
974
- item.default = type.initialValue
975
- else
976
- item.default = ''
977
- end
978
- end
979
- end
980
- else
981
- # Possibly add read conversion
982
- if type.conversion and type.conversion.class == PolynomialConversion
983
- item.read_conversion = type.conversion
984
- end
985
-
986
- # Possibly add default limits
987
- if type.limits
988
- item.limits.enabled = true
989
- values = {}
990
- values[:DEFAULT] = type.limits
991
- item.limits.values = values
992
- end
993
- end
994
-
995
- return false # Already recursed
996
-
997
- when 'BaseContainer'
998
- # Handled in SequenceContainer/CommandContainer
999
-
1000
- when 'BaseMetaCommand'
1001
- # Handled in MetaCommand
1002
-
1003
- when 'Comparison'
1004
- # Need to set ID value for item
1005
- item = @current_packet.get_item(element['parameterRef'])
1006
- item.id_value = Integer(element['value'])
1007
- if @current_cmd_or_tlm == COMMAND
1008
- item.default = item.id_value
1009
- end
1010
- @current_packet.update_id_items(item)
1011
-
1012
- when 'MetaCommand'
1013
- finish_packet()
1014
- @current_packet = Packet.new(@current_target_name, element['name'], :BIG_ENDIAN, element['shortDescription'])
1015
- @current_packet.abstract = ConfigParser.handle_true_false_nil(element['abstract'])
1016
- PacketParser.finish_create_command(@current_packet, @commands, @warnings)
1017
-
1018
- # Need to check for a BaseContainer now because if we hit it later it will be too late
1019
- xtce_handle_base_container('BaseMetaCommand', element)
1020
-
1021
- when 'CommandContainer'
1022
- @containers[element['name']] = @current_packet
1023
-
1024
- # Need to check for a BaseContainer now because if we hit it later it will be too late
1025
- xtce_handle_base_container('BaseContainer', element)
1026
-
1027
- when 'ArgumentAssignment'
1028
- # Need to set ID value for item
1029
- item = @current_packet.get_item(element['argumentName'])
1030
- value = element['argumentValue']
1031
- if item.states and item.states[value.to_s.upcase]
1032
- item.id_value = item.states[value.to_s.upcase]
1033
- item.default = item.id_value
1034
- else
1035
- item.id_value = Integer(value)
1036
- item.default = item.id_value
1037
- end
1038
- @current_packet.update_id_items(item)
1039
-
1040
- else
1041
- puts " Ignoring Unknown: <#{element.name}>"
1042
-
1043
- end # case element.name
1044
-
1045
- return true # Recurse further
1046
- end
1047
-
1048
- def xtce_format_attributes(element)
1049
- string = ''
1050
- element.attributes.each do |att_name, att|
1051
- string << "#{att.name}:#{att.value} "
1052
- end
1053
- if string.length > 0
1054
- string = '( ' + string + ')'
1055
- end
1056
- return string
1057
- end
1058
-
1059
- def xtce_recurse_element(element, depth, &block)
1060
- return unless yield(element, depth)
1061
- element.children.each do |child_element|
1062
- xtce_recurse_element(child_element, depth + 1, &block)
1063
- end
1064
- end
1065
-
1066
- def xtce_handle_base_container(base_name, element)
1067
- if element.name == base_name
1068
- # Need to add BaseContainer items to current_packet
1069
- # Lookup the base packet
1070
- if base_name == 'BaseMetaCommand'
1071
- base_packet = @commands[@current_packet.target_name][element['metaCommandRef'].to_s.upcase]
1072
- else
1073
- base_packet = @containers[element['containerRef']]
1074
- end
1075
- if base_packet
1076
- count = 0
1077
- base_packet.sorted_items.each do |item|
1078
- unless ['RECEIVED_TIMESECONDS', 'RECEIVED_TIMEFORMATTED', 'RECEIVED_COUNT'].include?(item.name)
1079
- begin
1080
- @current_packet.get_item(item.name)
1081
- rescue
1082
- # Item hasn't already been added so define it
1083
- @current_packet.define(item.clone)
1084
- count += 1
1085
- end
1086
- end
1087
- end
1088
- return
1089
- else
1090
- if base_name == 'BaseMetaCommand'
1091
- raise "Unknown #{base_name}: #{element['metaCommandRef']}"
1092
- else
1093
- raise "Unknown #{base_name}: #{element['containerRef']}"
1094
- end
1095
- end
1096
- end
1097
- element.children.each do |child_element|
1098
- xtce_handle_base_container(base_name, child_element)
1099
- end
1100
- end
1101
-
1102
- def xtce_handle_location_in_container_in_bits(element)
1103
- element.children.each do |child_element|
1104
- if child_element.name == 'LocationInContainerInBits'
1105
- child_element.children.each do |child_element2|
1106
- if child_element2.name == 'FixedValue'
1107
- return [child_element['referenceLocation'], Integer(child_element2.text)]
1108
- end
1109
- end
1110
- end
1111
- end
1112
- return [nil, nil]
1113
291
  end
1114
292
 
1115
293
  def process_current_packet(parser, keyword, params)
@@ -1352,22 +530,6 @@ module Cosmos
1352
530
  end
1353
531
  end
1354
532
 
1355
- # Add current packet into hash if it exists
1356
- def finish_packet
1357
- finish_item()
1358
- if @current_packet
1359
- @warnings += @current_packet.check_bit_offsets
1360
- if @current_cmd_or_tlm == COMMAND
1361
- PacketParser.check_item_data_types(@current_packet)
1362
- @commands[@current_packet.target_name][@current_packet.packet_name] = @current_packet
1363
- else
1364
- @telemetry[@current_packet.target_name][@current_packet.packet_name] = @current_packet
1365
- end
1366
- @current_packet = nil
1367
- @current_item = nil
1368
- end
1369
- end
1370
-
1371
533
  def start_item(parser)
1372
534
  finish_item()
1373
535
  @current_item = PacketItemParser.parse(parser, @current_packet, @current_cmd_or_tlm)
@@ -1387,7 +549,5 @@ module Cosmos
1387
549
  @current_item = nil
1388
550
  end
1389
551
  end
1390
-
1391
- end # class PacketConfig
1392
-
1393
- end # module Cosmos
552
+ end
553
+ end