cosmos 3.7.1 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Manifest.txt +1 -0
- data/bin/xtce_converter +83 -0
- data/cosmos.gemspec +2 -0
- data/data/crc.txt +279 -279
- data/demo/config/data/crc.txt +171 -171
- data/demo/config/targets/INST/cmd_tlm/inst_tlm.txt +1 -1
- data/demo/config/targets/INST/lib/sim_inst.rb +17 -1
- data/demo/config/targets/INST/screens/hs.txt +4 -1
- data/demo/config/targets/INST/screens/limits.txt +13 -13
- data/install/config/data/crc.txt +113 -113
- data/lib/cosmos/conversions/conversion.rb +6 -0
- data/lib/cosmos/conversions/generic_conversion.rb +12 -0
- data/lib/cosmos/conversions/new_packet_log_conversion.rb +6 -0
- data/lib/cosmos/conversions/polynomial_conversion.rb +6 -0
- data/lib/cosmos/conversions/processor_conversion.rb +10 -0
- data/lib/cosmos/conversions/segmented_polynomial_conversion.rb +10 -0
- data/lib/cosmos/conversions/unix_time_conversion.rb +6 -0
- data/lib/cosmos/core_ext/string.rb +18 -5
- data/lib/cosmos/gui/line_graph/lines.rb +13 -12
- data/lib/cosmos/packets/limits_response.rb +4 -0
- data/lib/cosmos/packets/packet.rb +59 -8
- data/lib/cosmos/packets/packet_config.rb +679 -6
- data/lib/cosmos/packets/packet_item.rb +230 -0
- data/lib/cosmos/packets/parsers/packet_parser.rb +30 -22
- data/lib/cosmos/processors/new_packet_log_processor.rb +5 -0
- data/lib/cosmos/processors/processor.rb +5 -0
- data/lib/cosmos/processors/statistics_processor.rb +5 -0
- data/lib/cosmos/processors/watermark_processor.rb +5 -0
- data/lib/cosmos/script/extract.rb +1 -1
- data/lib/cosmos/tools/packet_viewer/packet_viewer.rb +7 -2
- data/lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb +3 -1
- data/lib/cosmos/tools/tlm_grapher/data_objects/xy_data_object.rb +3 -1
- data/lib/cosmos/tools/tlm_viewer/widgets/limits_widget.rb +10 -0
- data/lib/cosmos/tools/tlm_viewer/widgets/limitsbar_widget.rb +14 -6
- data/lib/cosmos/tools/tlm_viewer/widgets/limitscolumn_widget.rb +14 -6
- data/lib/cosmos/tools/tlm_viewer/widgets/rangebar_widget.rb +14 -6
- data/lib/cosmos/tools/tlm_viewer/widgets/rangecolumn_widget.rb +14 -6
- data/lib/cosmos/utilities/simulated_target.rb +1 -0
- data/lib/cosmos/version.rb +5 -5
- metadata +18 -2
@@ -209,40 +209,40 @@ module Cosmos
|
|
209
209
|
|
210
210
|
def add_line(text, y, x = nil, y_labels = nil, x_labels = nil, y_states = nil, x_states = nil, color = 'blue', axis = :LEFT, max_points_plotted = nil)
|
211
211
|
# Validate y data
|
212
|
-
raise ArgumentError, "
|
213
|
-
raise ArgumentError, "
|
214
|
-
raise ArgumentError, "
|
212
|
+
raise ArgumentError, "y data must be given in an array-like class" unless y.respond_to?(:[])
|
213
|
+
raise ArgumentError, "y data cannot be empty" if y.empty?
|
214
|
+
raise ArgumentError, "y data must be Numeric" unless y[0].kind_of?(Numeric)
|
215
215
|
|
216
216
|
# Validate x data
|
217
217
|
if x.respond_to?(:[])
|
218
|
-
raise ArgumentError, "
|
219
|
-
raise ArgumentError, "
|
218
|
+
raise ArgumentError, "x and y data must be the same size" unless y.length == x.length
|
219
|
+
raise ArgumentError, "x data must be Numeric" unless x[0].kind_of?(Numeric)
|
220
220
|
else
|
221
|
-
raise ArgumentError, "
|
221
|
+
raise ArgumentError, "x data must be given in an array-like class" unless x.nil?
|
222
222
|
end
|
223
223
|
|
224
224
|
# Validate y_labels data
|
225
225
|
if y_labels.respond_to?(:[])
|
226
|
-
raise ArgumentError, "
|
226
|
+
raise ArgumentError, "y_labels and y data must be the same size" unless y.length == y_labels.length
|
227
227
|
else
|
228
|
-
raise ArgumentError, "
|
228
|
+
raise ArgumentError, "y_labels data must be given in an array-like class" unless y_labels.nil?
|
229
229
|
end
|
230
230
|
|
231
231
|
# Validate x_labels data
|
232
232
|
if x_labels.respond_to?(:[])
|
233
|
-
raise ArgumentError, "
|
233
|
+
raise ArgumentError, "x_labels and y data must be the same size" unless y.length == x_labels.length
|
234
234
|
else
|
235
|
-
raise ArgumentError, "
|
235
|
+
raise ArgumentError, "x_labels data must be given in an array-like class" unless x_labels.nil?
|
236
236
|
end
|
237
237
|
|
238
238
|
# Validate y_states data
|
239
239
|
unless y_states.respond_to?(:index)
|
240
|
-
raise ArgumentError, "
|
240
|
+
raise ArgumentError, "y_states data must be given in an hash-like class" unless y_states.nil?
|
241
241
|
end
|
242
242
|
|
243
243
|
# Validate x_states data
|
244
244
|
unless x_states.respond_to?(:index)
|
245
|
-
raise ArgumentError, "
|
245
|
+
raise ArgumentError, "x_states data must be given in an hash-like class" unless x_states.nil?
|
246
246
|
end
|
247
247
|
|
248
248
|
if max_points_plotted and y.length > max_points_plotted
|
@@ -278,6 +278,7 @@ module Cosmos
|
|
278
278
|
unless x
|
279
279
|
x = (1..(y.length)).to_a_to_f
|
280
280
|
end
|
281
|
+
|
281
282
|
y_labels = y unless y_labels
|
282
283
|
# x_labels are only set if the formatted time item is used
|
283
284
|
y_states = y_states.clone if y_states
|
@@ -20,6 +20,8 @@ module Cosmos
|
|
20
20
|
# as managing PacketItem's limit states.
|
21
21
|
class Packet < Structure
|
22
22
|
|
23
|
+
RESERVED_ITEM_NAMES = ['RECEIVED_TIMESECONDS'.freeze, 'RECEIVED_TIMEFORMATTED'.freeze, 'RECEIVED_COUNT'.freeze]
|
24
|
+
|
23
25
|
# @return [String] Name of the target this packet is associated with
|
24
26
|
attr_reader :target_name
|
25
27
|
|
@@ -62,6 +64,9 @@ module Cosmos
|
|
62
64
|
# @return [Boolean] Whether or not messages should be printed for this packet
|
63
65
|
attr_accessor :messages_disabled
|
64
66
|
|
67
|
+
# @return [Boolean] Whether or not this is a 'abstract' packet
|
68
|
+
attr_accessor :abstract
|
69
|
+
|
65
70
|
# Valid format types
|
66
71
|
VALUE_TYPES = [:RAW, :CONVERTED, :FORMATTED, :WITH_UNITS]
|
67
72
|
|
@@ -642,6 +647,60 @@ module Cosmos
|
|
642
647
|
end
|
643
648
|
alias dup clone
|
644
649
|
|
650
|
+
def update_id_items(item)
|
651
|
+
if item.id_value
|
652
|
+
@id_items ||= []
|
653
|
+
@id_items << item
|
654
|
+
end
|
655
|
+
item
|
656
|
+
end
|
657
|
+
|
658
|
+
def to_config(cmd_or_tlm)
|
659
|
+
config = ''
|
660
|
+
|
661
|
+
if cmd_or_tlm == :TELEMETRY
|
662
|
+
config << "TELEMETRY #{self.target_name.to_s.quote_if_necessary} #{self.packet_name.to_s.quote_if_necessary} #{@default_endianness} \"#{self.description}\"\n"
|
663
|
+
else
|
664
|
+
config << "COMMAND #{self.target_name.to_s.quote_if_necessary} #{self.packet_name.to_s.quote_if_necessary} #{@default_endianness} \"#{self.description}\"\n"
|
665
|
+
end
|
666
|
+
config << " ALLOW_SHORT\n" if self.short_buffer_allowed
|
667
|
+
config << " HAZARDOUS #{self.hazardous_description.to_s.quote_if_necessary}\n" if self.hazardous
|
668
|
+
config << " DISABLE_MESSAGES\n" if self.messages_disabled
|
669
|
+
if self.disabled
|
670
|
+
config << " DISABLED\n"
|
671
|
+
elsif self.hidden
|
672
|
+
config << " HIDDEN\n"
|
673
|
+
end
|
674
|
+
|
675
|
+
if @processors
|
676
|
+
@processors.each do |processor_name, processor|
|
677
|
+
config << processor.to_config
|
678
|
+
end
|
679
|
+
end
|
680
|
+
|
681
|
+
if @meta
|
682
|
+
@meta.each do |key, values|
|
683
|
+
config << " META #{key.to_s.quote_if_necessary} #{values.map {|a| a..to_s.quote_if_necessary}.join(" ")}\n"
|
684
|
+
end
|
685
|
+
end
|
686
|
+
|
687
|
+
# Items with derived items last
|
688
|
+
self.sorted_items.each do |item|
|
689
|
+
if item.data_type != :DERIVED
|
690
|
+
config << item.to_config(cmd_or_tlm, @default_endianness)
|
691
|
+
end
|
692
|
+
end
|
693
|
+
self.sorted_items.each do |item|
|
694
|
+
if item.data_type == :DERIVED
|
695
|
+
unless RESERVED_ITEM_NAMES.include?(item.name)
|
696
|
+
config << item.to_config(cmd_or_tlm, @default_endianness)
|
697
|
+
end
|
698
|
+
end
|
699
|
+
end
|
700
|
+
|
701
|
+
config
|
702
|
+
end
|
703
|
+
|
645
704
|
protected
|
646
705
|
|
647
706
|
# Performs packet specific processing on the packet. Intended to only be run once for each packet received
|
@@ -773,14 +832,6 @@ module Cosmos
|
|
773
832
|
item
|
774
833
|
end
|
775
834
|
|
776
|
-
def update_id_items(item)
|
777
|
-
if item.id_value
|
778
|
-
@id_items ||= []
|
779
|
-
@id_items << item
|
780
|
-
end
|
781
|
-
item
|
782
|
-
end
|
783
|
-
|
784
835
|
end # class Packet
|
785
836
|
|
786
837
|
end # module Cosmos
|
@@ -20,6 +20,8 @@ require 'cosmos/packets/parsers/format_string_parser'
|
|
20
20
|
require 'cosmos/packets/parsers/processor_parser'
|
21
21
|
require 'cosmos/conversions'
|
22
22
|
require 'cosmos/processors'
|
23
|
+
require 'nokogiri'
|
24
|
+
require 'ostruct'
|
23
25
|
|
24
26
|
module Cosmos
|
25
27
|
|
@@ -78,11 +80,7 @@ module Cosmos
|
|
78
80
|
@telemetry['UNKNOWN'] = {}
|
79
81
|
@telemetry['UNKNOWN']['UNKNOWN'] = Packet.new('UNKNOWN', 'UNKNOWN', :BIG_ENDIAN)
|
80
82
|
|
81
|
-
|
82
|
-
@current_cmd_or_tlm = nil
|
83
|
-
@current_packet = nil
|
84
|
-
@current_item = nil
|
85
|
-
@current_limits_group = nil
|
83
|
+
reset_processing_variables()
|
86
84
|
end
|
87
85
|
|
88
86
|
#########################################################################
|
@@ -95,6 +93,12 @@ module Cosmos
|
|
95
93
|
# @param filename [String] The name of the configuration file
|
96
94
|
# @param target_name [String] The target name
|
97
95
|
def process_file(filename, process_target_name)
|
96
|
+
# Handle .xtce files
|
97
|
+
if File.extname(filename).to_s.downcase == ".xtce"
|
98
|
+
process_xtce(filename, process_target_name)
|
99
|
+
return
|
100
|
+
end
|
101
|
+
|
98
102
|
# Partial files are included into another file and thus aren't directly processed
|
99
103
|
return if File.basename(filename)[0] == '_' # Partials start with underscore
|
100
104
|
|
@@ -203,8 +207,678 @@ module Cosmos
|
|
203
207
|
finish_packet()
|
204
208
|
end
|
205
209
|
|
210
|
+
# Read in a target definition from a .xtce file
|
211
|
+
def process_xtce(filename, override_target_name = nil)
|
212
|
+
doc = File.open(filename) { |f| Nokogiri::XML(f, nil, nil, Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NOBLANKS) }
|
213
|
+
xtce_process_element(doc.root, 0)
|
214
|
+
@current_target_name = override_target_name if override_target_name
|
215
|
+
doc.root.children.each do |child|
|
216
|
+
xtce_recurse_element(child, 1) do |element, depth|
|
217
|
+
xtce_process_element(element, depth)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
finish_packet()
|
221
|
+
|
222
|
+
# Remove abstract
|
223
|
+
@commands[@current_target_name].delete_if {|packet_name, packet| packet.abstract}
|
224
|
+
@telemetry[@current_target_name].delete_if {|packet_name, packet| packet.abstract}
|
225
|
+
|
226
|
+
# Reverse order of packets for the target so ids work correctly
|
227
|
+
reverse_packet_order(@current_target_name, @commands)
|
228
|
+
reverse_packet_order(@current_target_name, @telemetry)
|
229
|
+
|
230
|
+
reset_processing_variables()
|
231
|
+
end
|
232
|
+
|
233
|
+
# Convert the PacketConfig back to COSMOS configuration files for each target
|
234
|
+
def to_config(output_dir)
|
235
|
+
FileUtils.mkdir_p(output_dir)
|
236
|
+
|
237
|
+
@telemetry.each do |target_name, packets|
|
238
|
+
next if target_name == 'UNKNOWN'
|
239
|
+
FileUtils.mkdir_p(File.join(output_dir, target_name, 'cmd_tlm'))
|
240
|
+
filename = File.join(output_dir, target_name, 'cmd_tlm', target_name.downcase + '_tlm.txt')
|
241
|
+
begin
|
242
|
+
File.delete(filename)
|
243
|
+
rescue
|
244
|
+
# Doesn't exist
|
245
|
+
end
|
246
|
+
packets.each do |packet_name, packet|
|
247
|
+
File.open(filename, 'a') do |file|
|
248
|
+
file.puts packet.to_config(:TELEMETRY)
|
249
|
+
file.puts ""
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
@commands.each do |target_name, packets|
|
255
|
+
next if target_name == 'UNKNOWN'
|
256
|
+
FileUtils.mkdir_p(File.join(output_dir, target_name, 'cmd_tlm'))
|
257
|
+
filename = File.join(output_dir, target_name, 'cmd_tlm', target_name.downcase + '_cmd.txt')
|
258
|
+
begin
|
259
|
+
File.delete(filename)
|
260
|
+
rescue
|
261
|
+
# Doesn't exist
|
262
|
+
end
|
263
|
+
packets.each do |packet_name, packet|
|
264
|
+
File.open(filename, 'a') do |file|
|
265
|
+
file.puts packet.to_config(:COMMAND)
|
266
|
+
file.puts ""
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# Put limits groups into SYSTEM target
|
272
|
+
if @limits_groups.length > 0
|
273
|
+
FileUtils.mkdir_p(File.join(output_dir, 'SYSTEM', 'cmd_tlm'))
|
274
|
+
filename = File.join(output_dir, 'SYSTEM', 'cmd_tlm', 'limits_groups.txt')
|
275
|
+
File.open(filename, 'w') do |file|
|
276
|
+
@limits_groups.each do |limits_group_name, limits_group_items|
|
277
|
+
file.puts "LIMITS_GROUP #{limits_group_name.to_s.quote_if_necessary}"
|
278
|
+
limits_group_items.each do |target_name, packet_name, item_name|
|
279
|
+
file.puts " LIMITS_GROUP_ITEM #{target_name.to_s.quote_if_necessary} #{packet_name.to_s.quote_if_necessary} #{item_name.to_s.quote_if_necessary}"
|
280
|
+
end
|
281
|
+
file.puts ""
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
end # def to_config
|
287
|
+
|
288
|
+
# Convert the PacketConfig into a .xtce file for each target
|
289
|
+
def to_xtce(output_dir)
|
290
|
+
FileUtils.mkdir_p(output_dir)
|
291
|
+
|
292
|
+
# Build target list
|
293
|
+
targets = []
|
294
|
+
@telemetry.each { |target_name, packets| targets << target_name }
|
295
|
+
@commands.each { |target_name, packets| targets << target_name }
|
296
|
+
targets.uniq!
|
297
|
+
|
298
|
+
targets.each do |target_name|
|
299
|
+
next if target_name == 'UNKNOWN'
|
300
|
+
|
301
|
+
# Reverse order of packets for the target so things are expected (reverse) order for xtce
|
302
|
+
reverse_packet_order(target_name, @commands)
|
303
|
+
reverse_packet_order(target_name, @telemetry)
|
304
|
+
|
305
|
+
FileUtils.mkdir_p(File.join(output_dir, target_name, 'cmd_tlm'))
|
306
|
+
filename = File.join(output_dir, target_name, 'cmd_tlm', target_name.downcase + '.xtce')
|
307
|
+
begin
|
308
|
+
File.delete(filename)
|
309
|
+
rescue
|
310
|
+
# Doesn't exist
|
311
|
+
end
|
312
|
+
|
313
|
+
# Gather an make unique all the packet items
|
314
|
+
unique_items = {}
|
315
|
+
@telemetry[target_name].each do |packet_name, packet|
|
316
|
+
packet.sorted_items.each do |item|
|
317
|
+
next if item.data_type == :DERIVED
|
318
|
+
unique_items[item.name] ||= []
|
319
|
+
unique_items[item.name] << item
|
320
|
+
end
|
321
|
+
end
|
322
|
+
unique_items.each do |item_name, items|
|
323
|
+
if items.length <= 1
|
324
|
+
unique_items[item_name] = items[0]
|
325
|
+
next
|
326
|
+
end
|
327
|
+
# TODO: need to make sure all the items in the array are exactly the same
|
328
|
+
unique_items[item_name] = items[0]
|
329
|
+
end
|
330
|
+
|
331
|
+
# Gather and make unique all the command parameters
|
332
|
+
unique_arguments = {}
|
333
|
+
@commands[target_name].each do |packet_name, packet|
|
334
|
+
packet.sorted_items.each do |item|
|
335
|
+
next if item.data_type == :DERIVED
|
336
|
+
unique_arguments[item.name] ||= []
|
337
|
+
unique_arguments[item.name] << item
|
338
|
+
end
|
339
|
+
end
|
340
|
+
unique_arguments.each do |item_name, items|
|
341
|
+
if items.length <= 1
|
342
|
+
unique_arguments[item_name] = items[0]
|
343
|
+
next
|
344
|
+
end
|
345
|
+
# TODO: need to make sure all the items in the array are exactly the same
|
346
|
+
unique_arguments[item_name] = items[0]
|
347
|
+
end
|
348
|
+
|
349
|
+
# Create the xtce file for this target
|
350
|
+
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
|
351
|
+
xml['xtce'].SpaceSystem("xmlns:xtce" => "http://www.omg.org/space/xtce",
|
352
|
+
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
|
353
|
+
"name" => target_name,
|
354
|
+
"xsi:schemaLocation" => "http://www.omg.org/space/xtce http://www.omg.org/spec/XTCE/20061101/06-11-06.xsd") do
|
355
|
+
xml['xtce'].TelemetryMetaData do
|
356
|
+
xml['xtce'].ParameterTypeSet do
|
357
|
+
unique_items.each do |item_name, item|
|
358
|
+
item.to_xtce_type('Parameter', xml)
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
xml['xtce'].ParameterSet do
|
363
|
+
unique_items.each do |item_name, item|
|
364
|
+
item.to_xtce_item('Parameter', xml)
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
xml['xtce'].ContainerSet do
|
369
|
+
@telemetry[target_name].each do |packet_name, packet|
|
370
|
+
attrs = { :name => (packet_name + '_Base'), :abstract => "true" }
|
371
|
+
xml['xtce'].SequenceContainer(attrs) do
|
372
|
+
xml['xtce'].EntryList do
|
373
|
+
packet.sorted_items.each do |item|
|
374
|
+
next if item.data_type == :DERIVED
|
375
|
+
# TODO: Handle explicit bit offset in packet and nonunique item names
|
376
|
+
xml['xtce'].ParameterRefEntry(:parameterRef => item.name)
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end # Abstract SequenceContainer
|
380
|
+
|
381
|
+
attrs = { :name => packet_name }
|
382
|
+
attrs['shortDescription'] = packet.description if packet.description
|
383
|
+
xml['xtce'].SequenceContainer(attrs) do
|
384
|
+
xml['xtce'].EntryList
|
385
|
+
xml['xtce'].BaseContainer(:containerRef => (packet_name + '_Base')) do
|
386
|
+
if packet.id_items and packet.id_items.length > 0
|
387
|
+
xml['xtce'].RestrictionCriteria do
|
388
|
+
xml['xtce'].ComparisonList do
|
389
|
+
packet.id_items.each do |item|
|
390
|
+
xml['xtce'].Comparison(:parameterRef => item.name, :value => item.id_value)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end # Actual SequenceContainer
|
397
|
+
|
398
|
+
end # @telemetry.each
|
399
|
+
end # ContainerSet
|
400
|
+
end # TelemetryMetaData
|
401
|
+
|
402
|
+
xml['xtce'].CommandMetaData do
|
403
|
+
xml['xtce'].ArgumentTypeSet do
|
404
|
+
unique_arguments.each do |arg_name, arg|
|
405
|
+
arg.to_xtce_type('Argument', xml)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
xml['xtce'].MetaCommandSet do
|
409
|
+
@commands[target_name].each do |packet_name, packet|
|
410
|
+
attrs = { :name => packet_name + "_Base", :abstract => "true" }
|
411
|
+
xml['xtce'].MetaCommand(attrs) do
|
412
|
+
xml['xtce'].ArgumentList do
|
413
|
+
packet.sorted_items.each do |item|
|
414
|
+
next if item.data_type == :DERIVED
|
415
|
+
item.to_xtce_item('Argument', xml)
|
416
|
+
end
|
417
|
+
end # ArgumentList
|
418
|
+
xml['xtce'].CommandContainer(:name => "#{target_name}_#{packet_name}_CommandContainer") do
|
419
|
+
xml['xtce'].EntryList do
|
420
|
+
packet.sorted_items.each do |item|
|
421
|
+
next if item.data_type == :DERIVED
|
422
|
+
xml['xtce'].ArgumentRefEntry(:argumentRef => item.name)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end # Abstract MetaCommand
|
427
|
+
|
428
|
+
attrs = { :name => packet_name }
|
429
|
+
attrs['shortDescription'] = packet.description if packet.description
|
430
|
+
xml['xtce'].MetaCommand(attrs) do
|
431
|
+
xml['xtce'].BaseMetaCommand(:metaCommandRef => packet_name + "_Base") do
|
432
|
+
if packet.id_items and packet.id_items.length > 0
|
433
|
+
xml['xtce'].ArgumentAssignmentList do
|
434
|
+
packet.id_items.each do |item|
|
435
|
+
xml['xtce'].ArgumentAssignment(:argumentName => item.name, :argumentValue => item.id_value)
|
436
|
+
end
|
437
|
+
end # ArgumentAssignmentList
|
438
|
+
end
|
439
|
+
end # BaseMetaCommand
|
440
|
+
end # Actual MetaCommand
|
441
|
+
end # @commands.each
|
442
|
+
end # MetaCommandSet
|
443
|
+
end # CommandMetaData
|
444
|
+
end # SpaceSystem
|
445
|
+
end # builder
|
446
|
+
File.open(filename, 'w') do |file|
|
447
|
+
file.puts builder.to_xml
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
206
452
|
protected
|
207
453
|
|
454
|
+
def reset_processing_variables
|
455
|
+
# Used during packet processing
|
456
|
+
@current_cmd_or_tlm = nil
|
457
|
+
@current_packet = nil
|
458
|
+
@current_item = nil
|
459
|
+
@current_limits_group = nil
|
460
|
+
|
461
|
+
# Used for xtce processing
|
462
|
+
@current_target_name = nil
|
463
|
+
@current_type = nil
|
464
|
+
@current_meta_command = nil
|
465
|
+
@current_parameter = nil
|
466
|
+
@current_argument = nil
|
467
|
+
@parameter_types = {}
|
468
|
+
@argument_types = {}
|
469
|
+
@parameters = {}
|
470
|
+
@arguments = {}
|
471
|
+
@containers = {}
|
472
|
+
end
|
473
|
+
|
474
|
+
def reverse_packet_order(target_name, cmd_or_tlm_hash)
|
475
|
+
packets = []
|
476
|
+
names_to_remove = []
|
477
|
+
cmd_or_tlm_hash[target_name].each do |packet_name, packet|
|
478
|
+
packets << packet
|
479
|
+
names_to_remove << packet_name
|
480
|
+
end
|
481
|
+
cmd_or_tlm_hash[target_name].length.times do |i|
|
482
|
+
cmd_or_tlm_hash[target_name].delete(names_to_remove[i])
|
483
|
+
end
|
484
|
+
packets.reverse.each do |packet|
|
485
|
+
cmd_or_tlm_hash[target_name][packet.packet_name] = packet
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
XTCE_IGNORED_ELEMENTS = ['text', 'AliasSet', 'Alias', 'Header']
|
490
|
+
|
491
|
+
def xtce_process_element(element, depth)
|
492
|
+
if XTCE_IGNORED_ELEMENTS.include?(element.name)
|
493
|
+
return false
|
494
|
+
end
|
495
|
+
#~ if element.name == 'LongDescription'
|
496
|
+
#~ puts "#{' ' * depth}<#{element.name}> #{xtce_format_attributes(element)} #{element.text}"
|
497
|
+
#~ else
|
498
|
+
#~ puts "#{' ' * depth}<#{element.name}> #{xtce_format_attributes(element)}"
|
499
|
+
#~ end
|
500
|
+
|
501
|
+
case element.name
|
502
|
+
when 'SpaceSystem'
|
503
|
+
@current_target_name = element["name"].to_s.upcase
|
504
|
+
|
505
|
+
when 'TelemetryMetaData'
|
506
|
+
finish_packet()
|
507
|
+
@current_cmd_or_tlm = TELEMETRY
|
508
|
+
|
509
|
+
when 'CommandMetaData'
|
510
|
+
finish_packet()
|
511
|
+
@current_cmd_or_tlm = COMMAND
|
512
|
+
|
513
|
+
when 'ParameterTypeSet', 'EnumerationList', 'ParameterSet', 'ContainerSet', 'EntryList', 'DefaultCalibrator', 'DefaultAlarm', 'RestrictionCriteria', 'ComparisonList', 'MetaCommandSet', 'DefaultCalibrator', 'ArgumentTypeSet', 'ArgumentList', 'ArgumentAssignmentList'
|
514
|
+
# Do Nothing
|
515
|
+
|
516
|
+
when 'EnumeratedParameterType', 'EnumeratedArgumentType', 'IntegerParameterType', 'IntegerArgumentType', 'FloatParameterType', 'FloatArgumentType', 'StringParameterType', 'StringArgumentType', 'BinaryParameterType', 'BinaryArgumentType'
|
517
|
+
@current_type = OpenStruct.new
|
518
|
+
element.attributes.each do |att_name, att|
|
519
|
+
@current_type[att.name] = att.value
|
520
|
+
end
|
521
|
+
if element.name =~ /Argument/
|
522
|
+
@argument_types[element["name"]] = @current_type
|
523
|
+
else
|
524
|
+
@parameter_types[element["name"]] = @current_type
|
525
|
+
end
|
526
|
+
|
527
|
+
case element.name
|
528
|
+
when 'EnumeratedParameterType', 'EnumeratedArgumentType'
|
529
|
+
@current_type.xtce_encoding = 'IntegerDataEncoding'
|
530
|
+
@current_type.sizeInBits = 8 # This is undocumented but appears to be the design
|
531
|
+
when 'IntegerParameterType', 'IntegerArgumentType'
|
532
|
+
@current_type.xtce_encoding = 'IntegerDataEncoding'
|
533
|
+
@current_type.sizeInBits = 32
|
534
|
+
when 'FloatParameterType', 'FloatArgumentType'
|
535
|
+
@current_type.xtce_encoding = 'FloatDataEncoding'
|
536
|
+
@current_type.sizeInBits = 32
|
537
|
+
when 'StringParameterType', 'StringArgumentType'
|
538
|
+
@current_type.xtce_encoding = 'StringDataEncoding'
|
539
|
+
when 'BinaryParameterType', 'BinaryArgumentType'
|
540
|
+
@current_type.xtce_encoding = 'BinaryDataEncoding'
|
541
|
+
@current_type.sizeInBits = 8 # This is undocumented but appears to be the design
|
542
|
+
end
|
543
|
+
|
544
|
+
when "SizeInBits"
|
545
|
+
xtce_recurse_element(element, depth + 1) do |element, depth|
|
546
|
+
if element.name == 'FixedValue'
|
547
|
+
@current_type.sizeInBits = Integer(element.text)
|
548
|
+
false
|
549
|
+
else
|
550
|
+
true
|
551
|
+
end
|
552
|
+
end
|
553
|
+
return false # Already recursed
|
554
|
+
|
555
|
+
when 'UnitSet'
|
556
|
+
xtce_recurse_element(element, depth + 1) do |element, depth|
|
557
|
+
if element.name == 'Unit'
|
558
|
+
@current_type.units ||= ''
|
559
|
+
if @current_type.units.empty?
|
560
|
+
@current_type.units << element.text.to_s
|
561
|
+
else
|
562
|
+
@current_type.units << ('/' + element.text.to_s)
|
563
|
+
end
|
564
|
+
@current_type.units << "^#{element['power']}" if element['power']
|
565
|
+
@current_type.units_full ||= ''
|
566
|
+
description = element['description'].to_s
|
567
|
+
if description.empty?
|
568
|
+
@current_type.units_full = @current_type.units
|
569
|
+
else
|
570
|
+
if @current_type.units_full.empty?
|
571
|
+
@current_type.units_full << description
|
572
|
+
else
|
573
|
+
@current_type.units_full << ('/' + description)
|
574
|
+
end
|
575
|
+
end
|
576
|
+
end
|
577
|
+
true
|
578
|
+
end
|
579
|
+
return false # Already recursed
|
580
|
+
|
581
|
+
when 'PolynomialCalibrator'
|
582
|
+
xtce_recurse_element(element, depth + 1) do |element, depth|
|
583
|
+
if element.name == 'Term'
|
584
|
+
index = Integer(element['exponent'])
|
585
|
+
coeff = Float(element['coefficient'])
|
586
|
+
@current_type.conversion ||= PolynomialConversion.new([])
|
587
|
+
@current_type.conversion.coeffs[index] = coeff
|
588
|
+
@current_type.conversion.coeffs.each_with_index do |value, index|
|
589
|
+
@current_type.conversion.coeffs[index] = 0.0 if value.nil?
|
590
|
+
end
|
591
|
+
end
|
592
|
+
true
|
593
|
+
end
|
594
|
+
return false # Already recursed
|
595
|
+
|
596
|
+
when 'StaticAlarmRanges'
|
597
|
+
xtce_recurse_element(element, depth + 1) do |element, depth|
|
598
|
+
if element.name == 'WarningRange'
|
599
|
+
@current_type.limits ||= [0.0, 0.0, 0.0, 0.0]
|
600
|
+
@current_type.limits[1] = Float(element['minInclusive']) if element['minInclusive']
|
601
|
+
@current_type.limits[2] = Float(element['maxInclusive']) if element['maxInclusive']
|
602
|
+
elsif element.name == 'CriticalRange'
|
603
|
+
@current_type.limits ||= [0.0, 0.0, 0.0, 0.0]
|
604
|
+
@current_type.limits[0] = Float(element['minInclusive']) if element['minInclusive']
|
605
|
+
@current_type.limits[3] = Float(element['maxInclusive']) if element['maxInclusive']
|
606
|
+
end
|
607
|
+
true
|
608
|
+
end
|
609
|
+
return false # Already recursed
|
610
|
+
|
611
|
+
when "ValidRange"
|
612
|
+
@current_type.minInclusive = element['minInclusive']
|
613
|
+
@current_type.maxInclusive = element['maxInclusive']
|
614
|
+
|
615
|
+
when 'Enumeration'
|
616
|
+
@current_type.states ||= {}
|
617
|
+
@current_type.states[element['label']] = Integer(element['value'])
|
618
|
+
|
619
|
+
when 'IntegerDataEncoding', 'FloatDataEncoding', 'StringDataEncoding', 'BinaryDataEncoding'
|
620
|
+
@current_type.xtce_encoding = element.name
|
621
|
+
element.attributes.each do |att_name, att|
|
622
|
+
@current_type[att.name] = att.value
|
623
|
+
end
|
624
|
+
@current_type.sizeInBits = 8 unless element.attributes['sizeInBits']
|
625
|
+
|
626
|
+
when 'Parameter'
|
627
|
+
@current_parameter = OpenStruct.new
|
628
|
+
element.attributes.each do |att_name, att|
|
629
|
+
@current_parameter[att.name] = att.value
|
630
|
+
end
|
631
|
+
@parameters[element["name"]] = @current_parameter
|
632
|
+
|
633
|
+
when 'Argument'
|
634
|
+
@current_argument = OpenStruct.new
|
635
|
+
element.attributes.each do |att_name, att|
|
636
|
+
@current_argument[att.name] = att.value
|
637
|
+
end
|
638
|
+
@arguments[element["name"]] = @current_argument
|
639
|
+
|
640
|
+
when 'ParameterProperties'
|
641
|
+
element.attributes.each do |att_name, att|
|
642
|
+
@current_parameter[att.name] = att.value
|
643
|
+
end
|
644
|
+
|
645
|
+
when "SequenceContainer"
|
646
|
+
finish_packet()
|
647
|
+
@current_packet = Packet.new(@current_target_name, element['name'], :BIG_ENDIAN, element['shortDescription'])
|
648
|
+
@current_packet.abstract = ConfigParser.handle_true_false_nil(element['abstract'])
|
649
|
+
@containers[element['name']] = @current_packet
|
650
|
+
PacketParser.finish_create_telemetry(@current_packet, @telemetry, @latest_data, @warnings)
|
651
|
+
|
652
|
+
# Need to check for a BaseContainer now because if we hit it later it will be too late
|
653
|
+
xtce_handle_base_container('BaseContainer', element)
|
654
|
+
|
655
|
+
when 'LongDescription'
|
656
|
+
if @current_packet and !@current_packet.description
|
657
|
+
@current_packet.description = element.text
|
658
|
+
end
|
659
|
+
|
660
|
+
when 'ParameterRefEntry', 'ArgumentRefEntry'
|
661
|
+
if element.name =~ /Argument/
|
662
|
+
# Look up the argument and argument type
|
663
|
+
argument = @arguments[element['argumentRef']]
|
664
|
+
raise "argumentRef #{element['argumentRef']} not found" unless argument
|
665
|
+
argument_type = @argument_types[argument.argumentTypeRef]
|
666
|
+
raise "argumentTypeRef #{argument.argumentTypeRef} not found" unless argument_type
|
667
|
+
refName = 'argumentRef'
|
668
|
+
object = argument
|
669
|
+
type = argument_type
|
670
|
+
else
|
671
|
+
# Look up the parameter and parameter type
|
672
|
+
parameter = @parameters[element['parameterRef']]
|
673
|
+
raise "parameterRef #{element['parameterRef']} not found" unless parameter
|
674
|
+
parameter_type = @parameter_types[parameter.parameterTypeRef]
|
675
|
+
raise "parameterTypeRef #{parameter.parameterTypeRef} not found" unless parameter_type
|
676
|
+
refName = 'parameterRef'
|
677
|
+
object = parameter
|
678
|
+
type = parameter_type
|
679
|
+
end
|
680
|
+
|
681
|
+
# Add item to packet
|
682
|
+
data_type = nil
|
683
|
+
case type.xtce_encoding
|
684
|
+
when 'IntegerDataEncoding'
|
685
|
+
if type.signed == 'false' or type.encoding == 'unsigned'
|
686
|
+
data_type = :UINT
|
687
|
+
else
|
688
|
+
data_type = :INT
|
689
|
+
end
|
690
|
+
when 'FloatDataEncoding'
|
691
|
+
data_type = :FLOAT
|
692
|
+
when 'StringDataEncoding'
|
693
|
+
data_type = :STRING
|
694
|
+
when 'BinaryDataEncoding'
|
695
|
+
data_type = :BLOCK
|
696
|
+
else
|
697
|
+
raise "Referenced Parameter/Argument has no xtce_encoding: #{element[refName]}"
|
698
|
+
end
|
699
|
+
|
700
|
+
item = @current_packet.append_item(object.name, Integer(type.sizeInBits), data_type) #, array_size = nil, endianness = @default_endianness, overflow = :ERROR, format_string = nil, read_conversion = nil, write_conversion = nil, id_value = nil)
|
701
|
+
item.description = type.shortDescription if type.shortDescription
|
702
|
+
if type.states
|
703
|
+
item.states = type.states
|
704
|
+
end
|
705
|
+
if type.units and type.units_full
|
706
|
+
item.units = type.units
|
707
|
+
item.units_full = type.units_full
|
708
|
+
end
|
709
|
+
if @current_cmd_or_tlm == COMMAND
|
710
|
+
# Possibly add write conversion
|
711
|
+
if type.conversion and type.conversion.class == PolynomialConversion
|
712
|
+
item.write_conversion = type.conversion
|
713
|
+
end
|
714
|
+
|
715
|
+
# Need to set min, max, and default
|
716
|
+
if data_type == :INT
|
717
|
+
item.range = (-(2 ** (Integer(type.sizeInBits) - 1)))..((2 ** (Integer(type.sizeInBits) - 1)) - 1)
|
718
|
+
if type.minInclusive and type.maxInclusive
|
719
|
+
item.range = Integer(type.minInclusive)..Integer(type.maxInclusive)
|
720
|
+
end
|
721
|
+
item.default = 0
|
722
|
+
if item.states and item.states[type.initialValue.to_s.upcase]
|
723
|
+
item.default = Integer(item.states[type.initialvalue.to_s.upcase])
|
724
|
+
else
|
725
|
+
item.default = Integer(type.initialValue) if type.initialValue
|
726
|
+
end
|
727
|
+
elsif data_type == :UINT
|
728
|
+
item.range = 0..((2 ** Integer(type.sizeInBits)) - 1)
|
729
|
+
if type.minInclusive and type.maxInclusive
|
730
|
+
item.range = Integer(type.minInclusive)..Integer(type.maxInclusive)
|
731
|
+
end
|
732
|
+
item.default = 0
|
733
|
+
if item.states and item.states[type.initialValue.to_s.upcase]
|
734
|
+
item.default = Integer(item.states[type.initialValue.to_s.upcase])
|
735
|
+
else
|
736
|
+
item.default = Integer(type.initialValue) if type.initialValue
|
737
|
+
end
|
738
|
+
elsif data_type == :FLOAT
|
739
|
+
if Integer(type.sizeInBits) == 32
|
740
|
+
item.range = -3.402823e38..3.402823e38
|
741
|
+
else
|
742
|
+
item.range = -Float::MAX..Float::MAX
|
743
|
+
end
|
744
|
+
if type.minInclusive and type.maxInclusive
|
745
|
+
item.range = Float(type.minInclusive)..Float(type.maxInclusive)
|
746
|
+
end
|
747
|
+
item.default = 0.0
|
748
|
+
item.default = Float(type.initialValue) if type.initialValue
|
749
|
+
elsif data_type == :STRING
|
750
|
+
if type.initialValue
|
751
|
+
item.default = type.initialValue
|
752
|
+
else
|
753
|
+
item.default = ''
|
754
|
+
end
|
755
|
+
elsif data_type == :BLOCK
|
756
|
+
if type.initialValue
|
757
|
+
item.default = type.initialValue
|
758
|
+
else
|
759
|
+
item.default = ''
|
760
|
+
end
|
761
|
+
end
|
762
|
+
else
|
763
|
+
# Possibly add read conversion
|
764
|
+
if type.conversion and type.conversion.class == PolynomialConversion
|
765
|
+
item.read_conversion = type.conversion
|
766
|
+
end
|
767
|
+
|
768
|
+
# Possibly add default limits
|
769
|
+
if type.limits
|
770
|
+
item.limits.enabled = true
|
771
|
+
values = {}
|
772
|
+
values[:DEFAULT] = type.limits
|
773
|
+
item.limits.values = values
|
774
|
+
end
|
775
|
+
end
|
776
|
+
|
777
|
+
when 'BaseContainer'
|
778
|
+
# Handled in SequenceContainer/CommandContainer
|
779
|
+
|
780
|
+
when 'BaseMetaCommand'
|
781
|
+
# Handled in MetaCommand
|
782
|
+
|
783
|
+
when 'Comparison'
|
784
|
+
# Need to set ID value for item
|
785
|
+
item = @current_packet.get_item(element['parameterRef'])
|
786
|
+
item.id_value = Integer(element['value'])
|
787
|
+
if @current_cmd_or_tlm == COMMAND
|
788
|
+
item.default = item.id_value
|
789
|
+
end
|
790
|
+
@current_packet.update_id_items(item)
|
791
|
+
|
792
|
+
when 'MetaCommand'
|
793
|
+
finish_packet()
|
794
|
+
@current_packet = Packet.new(@current_target_name, element['name'], :BIG_ENDIAN, element['shortDescription'])
|
795
|
+
@current_packet.abstract = ConfigParser.handle_true_false_nil(element['abstract'])
|
796
|
+
PacketParser.finish_create_command(@current_packet, @commands, @warnings)
|
797
|
+
|
798
|
+
# Need to check for a BaseContainer now because if we hit it later it will be too late
|
799
|
+
xtce_handle_base_container('BaseMetaCommand', element)
|
800
|
+
|
801
|
+
when 'CommandContainer'
|
802
|
+
@containers[element['name']] = @current_packet
|
803
|
+
|
804
|
+
# Need to check for a BaseContainer now because if we hit it later it will be too late
|
805
|
+
xtce_handle_base_container('BaseContainer', element)
|
806
|
+
|
807
|
+
when 'ArgumentAssignment'
|
808
|
+
# Need to set ID value for item
|
809
|
+
item = @current_packet.get_item(element['argumentName'])
|
810
|
+
value = element['argumentValue']
|
811
|
+
if item.states and item.states[value.to_s.upcase]
|
812
|
+
item.id_value = item.states[value.to_s.upcase]
|
813
|
+
item.default = item.id_value
|
814
|
+
else
|
815
|
+
item.id_value = Integer(value)
|
816
|
+
item.default = item.id_value
|
817
|
+
end
|
818
|
+
@current_packet.update_id_items(item)
|
819
|
+
|
820
|
+
else
|
821
|
+
puts " Ignoring Unknown: <#{element.name}>"
|
822
|
+
|
823
|
+
end # case element.name
|
824
|
+
|
825
|
+
return true # Recurse further
|
826
|
+
end
|
827
|
+
|
828
|
+
def xtce_format_attributes(element)
|
829
|
+
string = ''
|
830
|
+
element.attributes.each do |att_name, att|
|
831
|
+
string << "#{att.name}:#{att.value} "
|
832
|
+
end
|
833
|
+
if string.length > 0
|
834
|
+
string = '( ' + string + ')'
|
835
|
+
end
|
836
|
+
return string
|
837
|
+
end
|
838
|
+
|
839
|
+
def xtce_recurse_element(element, depth, &block)
|
840
|
+
return unless yield(element, depth)
|
841
|
+
element.children.each do |child_element|
|
842
|
+
xtce_recurse_element(child_element, depth + 1, &block)
|
843
|
+
end
|
844
|
+
end
|
845
|
+
|
846
|
+
def xtce_handle_base_container(base_name, element)
|
847
|
+
if element.name == base_name
|
848
|
+
# Need to add BaseContainer items to current_packet
|
849
|
+
# Lookup the base packet
|
850
|
+
if base_name == 'BaseMetaCommand'
|
851
|
+
base_packet = @commands[@current_packet.target_name][element['metaCommandRef'].to_s.upcase]
|
852
|
+
else
|
853
|
+
base_packet = @containers[element['containerRef']]
|
854
|
+
end
|
855
|
+
if base_packet
|
856
|
+
count = 0
|
857
|
+
base_packet.sorted_items.each do |item|
|
858
|
+
unless ['RECEIVED_TIMESECONDS', 'RECEIVED_TIMEFORMATTED', 'RECEIVED_COUNT'].include?(item.name)
|
859
|
+
begin
|
860
|
+
@current_packet.get_item(item.name)
|
861
|
+
rescue
|
862
|
+
# Item hasn't already been added so define it
|
863
|
+
@current_packet.define(item.clone)
|
864
|
+
count += 1
|
865
|
+
end
|
866
|
+
end
|
867
|
+
end
|
868
|
+
return
|
869
|
+
else
|
870
|
+
if base_name == 'BaseMetaCommand'
|
871
|
+
raise "Unknown #{base_name}: #{element['metaCommandRef']}"
|
872
|
+
else
|
873
|
+
raise "Unknown #{base_name}: #{element['containerRef']}"
|
874
|
+
end
|
875
|
+
end
|
876
|
+
end
|
877
|
+
element.children.each do |child_element|
|
878
|
+
xtce_handle_base_container(base_name, child_element)
|
879
|
+
end
|
880
|
+
end
|
881
|
+
|
208
882
|
def process_current_packet(parser, keyword, params)
|
209
883
|
case keyword
|
210
884
|
|
@@ -449,7 +1123,6 @@ module Cosmos
|
|
449
1123
|
finish_item()
|
450
1124
|
if @current_packet
|
451
1125
|
@warnings += @current_packet.check_bit_offsets
|
452
|
-
|
453
1126
|
if @current_cmd_or_tlm == COMMAND
|
454
1127
|
PacketParser.check_item_data_types(@current_packet)
|
455
1128
|
@commands[@current_packet.target_name][@current_packet.packet_name] = @current_packet
|