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.
- checksums.yaml +4 -4
- data/Manifest.txt +5 -0
- data/appveyor.yml +2 -0
- data/autohotkey/tools/replay.ahk +45 -45
- data/autohotkey/tools/script_runner.ahk +3 -9
- data/cosmos.gemspec +1 -1
- data/data/config/interface_modifiers.yaml +23 -0
- data/data/config/screen.yaml +1 -1
- data/data/crc.txt +20 -18
- data/demo/config/targets/INST/cmd_tlm_server.txt +1 -1
- data/lib/cosmos/config/config_parser.rb +8 -3
- data/lib/cosmos/gui/dialogs/exception_dialog.rb +20 -5
- data/lib/cosmos/interfaces/protocols/burst_protocol.rb +13 -3
- data/lib/cosmos/interfaces/protocols/crc_protocol.rb +27 -3
- data/lib/cosmos/interfaces/protocols/fixed_protocol.rb +4 -2
- data/lib/cosmos/interfaces/protocols/length_protocol.rb +4 -2
- data/lib/cosmos/interfaces/protocols/override_protocol.rb +2 -2
- data/lib/cosmos/interfaces/protocols/preidentified_protocol.rb +3 -2
- data/lib/cosmos/interfaces/protocols/protocol.rb +16 -4
- data/lib/cosmos/interfaces/protocols/template_protocol.rb +7 -2
- data/lib/cosmos/interfaces/protocols/terminated_protocol.rb +4 -2
- data/lib/cosmos/packets/packet_config.rb +19 -859
- data/lib/cosmos/packets/packet_item.rb +56 -201
- data/lib/cosmos/packets/parsers/xtce_converter.rb +440 -0
- data/lib/cosmos/packets/parsers/xtce_parser.rb +682 -0
- data/lib/cosmos/tools/config_editor/config_editor.rb +143 -5
- data/lib/cosmos/tools/tlm_viewer/screen.rb +1 -1
- data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +5 -3
- data/lib/cosmos/tools/tlm_viewer/tlm_viewer_config.rb +40 -27
- data/lib/cosmos/version.rb +4 -4
- data/spec/config/config_parser_spec.rb +39 -2
- data/spec/install/config/targets/INST/screens/hs.txt +42 -0
- data/spec/install/config/targets/INST/target.txt +2 -0
- data/spec/interfaces/protocols/burst_protocol_spec.rb +18 -0
- data/spec/interfaces/protocols/length_protocol_spec.rb +49 -0
- data/spec/interfaces/udp_interface_spec.rb +0 -9
- data/spec/packets/packet_config_spec.rb +21 -144
- data/spec/packets/packet_item_spec.rb +68 -4
- data/spec/packets/parsers/packet_item_parser_spec.rb +12 -0
- data/spec/packets/parsers/xtce_parser_spec.rb +398 -0
- data/spec/tools/tlm_viewer/tlm_viewer_config_spec.rb +401 -0
- 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
|