openc3 6.10.2 → 6.10.3
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/bin/openc3cli +11 -1
- data/data/config/item_modifiers.yaml +2 -2
- data/data/config/parameter_modifiers.yaml +2 -2
- data/data/config/screen.yaml +23 -0
- data/data/config/target.yaml +8 -3
- data/data/config/widgets.yaml +30 -0
- data/lib/openc3/accessors/json_accessor.rb +1 -1
- data/lib/openc3/api/tlm_api.rb +1 -6
- data/lib/openc3/io/json_api_object.rb +38 -14
- data/lib/openc3/io/json_drb_object.rb +29 -11
- data/lib/openc3/microservices/interface_microservice.rb +8 -3
- data/lib/openc3/models/target_model.rb +2 -1
- data/lib/openc3/packets/packet.rb +3 -0
- data/lib/openc3/packets/packet_config.rb +2 -2
- data/lib/openc3/packets/parsers/packet_item_parser.rb +9 -0
- data/lib/openc3/packets/parsers/xtce_converter.rb +464 -100
- data/lib/openc3/version.rb +5 -5
- data/templates/tool_angular/package.json +2 -2
- data/templates/tool_react/package.json +1 -1
- data/templates/tool_svelte/package.json +1 -1
- data/templates/tool_vue/package.json +3 -3
- data/templates/widget/package.json +2 -2
- metadata +15 -1
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# GNU Affero General Public License for more details.
|
|
15
15
|
|
|
16
16
|
# Modified by OpenC3, Inc.
|
|
17
|
-
# All changes Copyright
|
|
17
|
+
# All changes Copyright 2025, OpenC3, Inc.
|
|
18
18
|
# All Rights Reserved
|
|
19
19
|
#
|
|
20
20
|
# This file may also be used under the terms of a commercial license
|
|
@@ -24,6 +24,16 @@ require 'nokogiri'
|
|
|
24
24
|
require 'openc3/packets/parsers/xtce_parser'
|
|
25
25
|
require 'fileutils'
|
|
26
26
|
|
|
27
|
+
DYNAMIC_STRING_LEN = 2048
|
|
28
|
+
INVALID_CHARS = '[]./'
|
|
29
|
+
REPLACEMENT_CHAR = '_'
|
|
30
|
+
ALIAS_NAMESPACE = 'COSMOS'
|
|
31
|
+
|
|
32
|
+
COMBINED_NAME = "COMBINED"
|
|
33
|
+
MAX_64_BIT_INT = 18446744073709551614
|
|
34
|
+
COSMOS_NATIVE_DERIVED_ITEMS = ['PACKET_TIMESECONDS', 'PACKET_TIMEFORMATTED', 'RECEIVED_TIMESECONDS', 'RECEIVED_TIMEFORMATTED', 'RECEIVED_COUNT']
|
|
35
|
+
PACKET_TIME_STRING = "PACKET_TIME"
|
|
36
|
+
|
|
27
37
|
module OpenC3
|
|
28
38
|
class XtceConverter
|
|
29
39
|
attr_accessor :current_target_name
|
|
@@ -37,13 +47,74 @@ module OpenC3
|
|
|
37
47
|
# that were created while parsing the configuration
|
|
38
48
|
# @param output_dir [String] The name of the output directory to generate
|
|
39
49
|
# the XTCE files. A file is generated for each target.
|
|
40
|
-
def self.convert(commands, telemetry, output_dir)
|
|
41
|
-
XtceConverter.new(commands, telemetry, output_dir)
|
|
50
|
+
def self.convert(commands, telemetry, output_dir, time_association_name)
|
|
51
|
+
XtceConverter.new(commands, telemetry, output_dir, time_association_name)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.combine_output_xtce(output_dir, root_target_name = nil)
|
|
55
|
+
combined_file_directory = File.join(output_dir, 'TARGETS_COMBINED', 'cmd_tlm')
|
|
56
|
+
begin
|
|
57
|
+
FileUtils.rm_rf(combined_file_directory)
|
|
58
|
+
rescue
|
|
59
|
+
# doesn't exist
|
|
60
|
+
end
|
|
61
|
+
file_pattern = File.join(output_dir, "**", "*.xtce")
|
|
62
|
+
xml_files = Dir.glob(file_pattern)
|
|
63
|
+
if xml_files.empty?
|
|
64
|
+
puts "No *.xtce files found to combine."
|
|
65
|
+
elsif xml_files.length == 1
|
|
66
|
+
puts "Only one *.xtce file found. No need to unify."
|
|
67
|
+
else
|
|
68
|
+
puts "Multiple targets found. Creating Unified XTCE representation."
|
|
69
|
+
FileUtils.mkdir_p(combined_file_directory)
|
|
70
|
+
file_basename = "combined"
|
|
71
|
+
xml_files.each do |file_path|
|
|
72
|
+
file_basename += "_#{File.basename(file_path, ".*")}"
|
|
73
|
+
end
|
|
74
|
+
full_file_name = File.join(combined_file_directory, file_basename.downcase + '.xtce')
|
|
75
|
+
begin
|
|
76
|
+
File.delete(full_file_name)
|
|
77
|
+
rescue
|
|
78
|
+
# Doesn't exist
|
|
79
|
+
end
|
|
80
|
+
xml_files.each do |file_path|
|
|
81
|
+
file_basename += File.basename(file_path, ".*")
|
|
82
|
+
end
|
|
83
|
+
root_builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
|
|
84
|
+
xml['xtce'].SpaceSystem("xmlns:xtce" => "http://www.omg.org/spec/XTCE/20180204",
|
|
85
|
+
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
|
|
86
|
+
"name" => root_target_name ? root_target_name : "root",
|
|
87
|
+
"xsi:schemaLocation" => "http://www.omg.org/spec/XTCE/20180204 https://www.omg.org/spec/XTCE/20180204/SpaceSystem.xsd")
|
|
88
|
+
end
|
|
89
|
+
new_doc = root_builder.doc
|
|
90
|
+
new_root = new_doc.root
|
|
91
|
+
xml_files.each do |file_path|
|
|
92
|
+
source_doc = Nokogiri::XML(File.open(file_path))
|
|
93
|
+
target_root = source_doc.root
|
|
94
|
+
target_root.attributes.each do |name, attr|
|
|
95
|
+
unless name == "name"
|
|
96
|
+
attr.remove
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
if root_target_name == target_root["name"]
|
|
100
|
+
nodes_to_add_reversed = target_root.children.to_a.reverse
|
|
101
|
+
nodes_to_add_reversed.each do |child_node|
|
|
102
|
+
new_root.prepend_child(child_node)
|
|
103
|
+
end
|
|
104
|
+
else
|
|
105
|
+
new_root.add_child(target_root)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
File.open(full_file_name, 'w') do |file|
|
|
109
|
+
file.puts new_doc.to_xml
|
|
110
|
+
end
|
|
111
|
+
full_file_name
|
|
112
|
+
end
|
|
42
113
|
end
|
|
43
114
|
|
|
44
115
|
private
|
|
45
116
|
|
|
46
|
-
def initialize(commands, telemetry, output_dir)
|
|
117
|
+
def initialize(commands, telemetry, output_dir, time_association_name)
|
|
47
118
|
FileUtils.mkdir_p(output_dir)
|
|
48
119
|
|
|
49
120
|
# Build target list
|
|
@@ -52,6 +123,8 @@ module OpenC3
|
|
|
52
123
|
commands.each { |target_name, packets| targets << target_name }
|
|
53
124
|
targets.uniq!
|
|
54
125
|
|
|
126
|
+
@packet_time_string = time_association_name
|
|
127
|
+
|
|
55
128
|
targets.each do |target_name|
|
|
56
129
|
next if target_name == 'UNKNOWN'
|
|
57
130
|
|
|
@@ -69,12 +142,19 @@ module OpenC3
|
|
|
69
142
|
|
|
70
143
|
# Create the xtce file for this target
|
|
71
144
|
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
|
|
72
|
-
xml['xtce'].SpaceSystem("xmlns:xtce" => "http://www.omg.org/
|
|
145
|
+
xml['xtce'].SpaceSystem("xmlns:xtce" => "http://www.omg.org/spec/XTCE/20180204",
|
|
73
146
|
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
|
|
74
147
|
"name" => target_name,
|
|
75
|
-
"xsi:schemaLocation" => "http://www.omg.org/
|
|
148
|
+
"xsi:schemaLocation" => "http://www.omg.org/spec/XTCE/20180204 https://www.omg.org/spec/XTCE/20180204/SpaceSystem.xsd") do
|
|
76
149
|
create_telemetry(xml, telemetry, target_name)
|
|
77
|
-
|
|
150
|
+
# Get the Telemetry items to avoid clashing parameters
|
|
151
|
+
if telemetry[target_name]
|
|
152
|
+
unique_tlm_params = telemetry[target_name] ? get_unique(telemetry[target_name]) : {}
|
|
153
|
+
else
|
|
154
|
+
unique_tlm_params = {}
|
|
155
|
+
end
|
|
156
|
+
create_commands(xml, commands, target_name, unique_tlm_params)
|
|
157
|
+
create_algorithms(xml, telemetry, target_name)
|
|
78
158
|
end # SpaceSystem
|
|
79
159
|
end # builder
|
|
80
160
|
File.open(filename, 'w') do |file|
|
|
@@ -83,8 +163,46 @@ module OpenC3
|
|
|
83
163
|
end
|
|
84
164
|
end
|
|
85
165
|
|
|
166
|
+
def create_algorithms(xml, telemetry, target_name)
|
|
167
|
+
return unless telemetry[target_name]
|
|
168
|
+
derived = {}
|
|
169
|
+
telemetry[target_name].each do |packet_name, packet|
|
|
170
|
+
packet.sorted_items.each do |item|
|
|
171
|
+
next if item.data_type != :DERIVED
|
|
172
|
+
next if COSMOS_NATIVE_DERIVED_ITEMS.include?(item.name)
|
|
173
|
+
next if item.name == @packet_time_string
|
|
174
|
+
derived[packet_name] = item
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
return unless derived.length > 0
|
|
178
|
+
algorithm_xml = Nokogiri::XML::Builder.new do |alg_xml|
|
|
179
|
+
alg_xml.AlgorithmSet do
|
|
180
|
+
derived.each do |packet_name, item|
|
|
181
|
+
alg_xml.CustomAlgorithm("name" => "#{packet_name.tr(INVALID_CHARS, REPLACEMENT_CHAR)}_" \
|
|
182
|
+
"#{item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)}_#{item.read_conversion.class.name}") do
|
|
183
|
+
alg_xml.ExternalAlgorithmSet do
|
|
184
|
+
alg_xml.ExternalAlgorithm("implementationName" => "TODO", "algorithmLocation" => "TODO")
|
|
185
|
+
end
|
|
186
|
+
alg_xml.InputSet do
|
|
187
|
+
alg_xml.InputParameterInstanceRef( :parameterRef => "TODO", :instance => "0", :useCalibratedValue => "TODO")
|
|
188
|
+
end
|
|
189
|
+
alg_xml.OutputSet do
|
|
190
|
+
alg_xml.OutputParameterRef( :parameterRef => "#{item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)}")
|
|
191
|
+
end
|
|
192
|
+
alg_xml.TriggerSet( :name => "triggerSet") do
|
|
193
|
+
alg_xml.OnParameterUpdateTrigger( :parameterRef => "TODO")
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
xml['xtce'].comment "TODO \n#{algorithm_xml.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::NO_DECLARATION | Nokogiri::XML::Node::SaveOptions::FORMAT)}\n"
|
|
200
|
+
end
|
|
201
|
+
|
|
86
202
|
def create_telemetry(xml, telemetry, target_name)
|
|
87
203
|
# Gather and make unique all the packet items
|
|
204
|
+
return unless telemetry[target_name]
|
|
205
|
+
|
|
88
206
|
unique_items = telemetry[target_name] ? get_unique(telemetry[target_name]) : {}
|
|
89
207
|
|
|
90
208
|
xml['xtce'].TelemetryMetaData do
|
|
@@ -93,31 +211,39 @@ module OpenC3
|
|
|
93
211
|
to_xtce_type(item, 'Parameter', xml)
|
|
94
212
|
end
|
|
95
213
|
end
|
|
214
|
+
has_packet_time = unique_items.include?(@packet_time_string)
|
|
96
215
|
|
|
97
216
|
xml['xtce'].ParameterSet do
|
|
98
217
|
unique_items.each do |item_name, item|
|
|
99
|
-
to_xtce_item(item, 'Parameter', xml)
|
|
218
|
+
to_xtce_item(item, 'Parameter', xml, has_packet_time: has_packet_time)
|
|
100
219
|
end
|
|
101
220
|
end
|
|
102
221
|
|
|
103
222
|
if telemetry[target_name]
|
|
104
223
|
xml['xtce'].ContainerSet do
|
|
105
224
|
telemetry[target_name].each do |packet_name, packet|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
process_entry_list(xml, packet, :TELEMETRY)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
attrs = { :name => packet_name }
|
|
225
|
+
# Replaces invalid characters if any exist
|
|
226
|
+
attrs = { :name => packet_name.tr(INVALID_CHARS, REPLACEMENT_CHAR) }
|
|
112
227
|
attrs['shortDescription'] = packet.description if packet.description
|
|
113
228
|
xml['xtce'].SequenceContainer(attrs) do
|
|
114
|
-
|
|
115
|
-
|
|
229
|
+
# Adds an alias if any invalid characters exist
|
|
230
|
+
if packet_name.count(INVALID_CHARS) > 0
|
|
231
|
+
xml['xtce'].AliasSet do
|
|
232
|
+
xml['xtce'].Alias(:nameSpace => ALIAS_NAMESPACE, :alias => packet_name)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
if packet.short_buffer_allowed
|
|
236
|
+
xml['xtce'].AncillaryDataSet do
|
|
237
|
+
xml['xtce'].AncillaryData("true", :name => "ALLOW_SHORT")
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
process_entry_list(xml, packet, :TELEMETRY)
|
|
241
|
+
xml['xtce'].BaseContainer(:containerRef => (packet_name.tr(INVALID_CHARS, REPLACEMENT_CHAR))) do
|
|
116
242
|
if packet.id_items && packet.id_items.length > 0
|
|
117
243
|
xml['xtce'].RestrictionCriteria do
|
|
118
244
|
xml['xtce'].ComparisonList do
|
|
119
245
|
packet.id_items.each do |item|
|
|
120
|
-
xml['xtce'].Comparison(:parameterRef => item.name, :value => item.id_value)
|
|
246
|
+
xml['xtce'].Comparison(:parameterRef => item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR), :value => item.id_value)
|
|
121
247
|
end
|
|
122
248
|
end
|
|
123
249
|
end
|
|
@@ -130,57 +256,136 @@ module OpenC3
|
|
|
130
256
|
end # TelemetryMetaData
|
|
131
257
|
end
|
|
132
258
|
|
|
133
|
-
def create_commands(xml, commands, target_name)
|
|
259
|
+
def create_commands(xml, commands, target_name, unique_tlm_params = {})
|
|
134
260
|
return unless commands[target_name]
|
|
135
261
|
|
|
136
262
|
xml['xtce'].CommandMetaData do
|
|
263
|
+
unique_id_items = get_unique_id_items(commands[target_name])
|
|
264
|
+
# Create Parameters for any ID item so it can be used in a comparison.
|
|
265
|
+
if unique_id_items.size > 0
|
|
266
|
+
xml['xtce'].ParameterTypeSet do
|
|
267
|
+
unique_id_items.each do |item_name, item|
|
|
268
|
+
prefix = unique_tlm_params.include?(item_name) ? "CMD_" : ""
|
|
269
|
+
to_xtce_type(item, 'Parameter', xml, prefix: prefix)
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
xml['xtce'].ParameterSet do
|
|
273
|
+
unique_id_items.each do |item_name, item|
|
|
274
|
+
prefix = unique_tlm_params.include?(item_name) ? "CMD_" : ""
|
|
275
|
+
to_xtce_item(item, 'Parameter', xml, prefix: prefix)
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
#unique_command_args_without_ids = get_unique_without_ids(commands[target_name])
|
|
280
|
+
#if unique_command_args_without_ids.size > 0
|
|
137
281
|
xml['xtce'].ArgumentTypeSet do
|
|
138
|
-
|
|
139
|
-
|
|
282
|
+
commands[target_name].each do |packet_name, packet|
|
|
283
|
+
packet.items.each do |arg_name, arg|
|
|
284
|
+
next if arg.data_type == :DERIVED
|
|
285
|
+
next if unique_id_items.key?(arg_name.tr(INVALID_CHARS, REPLACEMENT_CHAR))
|
|
286
|
+
to_xtce_type(arg, 'Argument', xml, prefix: packet_name.tr(INVALID_CHARS, REPLACEMENT_CHAR) + "_")
|
|
287
|
+
end
|
|
140
288
|
end
|
|
141
289
|
end
|
|
142
290
|
xml['xtce'].MetaCommandSet do
|
|
143
291
|
commands[target_name].each do |packet_name, packet|
|
|
144
|
-
attrs = { :name => packet_name
|
|
145
|
-
xml['xtce'].MetaCommand(attrs) do
|
|
146
|
-
xml['xtce'].ArgumentList do
|
|
147
|
-
packet.sorted_items.each do |item|
|
|
148
|
-
next if item.data_type == :DERIVED
|
|
149
|
-
|
|
150
|
-
to_xtce_item(item, 'Argument', xml)
|
|
151
|
-
end
|
|
152
|
-
end # ArgumentList
|
|
153
|
-
xml['xtce'].CommandContainer(:name => "#{target_name}_#{packet_name}_CommandContainer") do
|
|
154
|
-
process_entry_list(xml, packet, :COMMAND)
|
|
155
|
-
end
|
|
156
|
-
end # Abstract MetaCommand
|
|
157
|
-
|
|
158
|
-
attrs = { :name => packet_name }
|
|
292
|
+
attrs = { :name => packet_name.tr(INVALID_CHARS, REPLACEMENT_CHAR) }
|
|
159
293
|
attrs['shortDescription'] = packet.description if packet.description
|
|
160
294
|
xml['xtce'].MetaCommand(attrs) do
|
|
161
|
-
|
|
295
|
+
if packet_name.count(INVALID_CHARS) > 0
|
|
296
|
+
xml['xtce'].AliasSet do
|
|
297
|
+
xml['xtce'].Alias(:nameSpace => ALIAS_NAMESPACE, :alias => packet_name)
|
|
298
|
+
end # AliasSet
|
|
299
|
+
end # If packet contains invalid chars
|
|
300
|
+
argument_list_sorted_items = get_sorted_non_id_items(packet.sorted_items)
|
|
301
|
+
if argument_list_sorted_items.size > 0
|
|
302
|
+
xml['xtce'].ArgumentList do
|
|
303
|
+
argument_list_sorted_items.each do |item|
|
|
304
|
+
to_xtce_item(item, 'Argument', xml, prefix: packet_name.tr(INVALID_CHARS, REPLACEMENT_CHAR) + "_")
|
|
305
|
+
end
|
|
306
|
+
end # ArgumentList
|
|
307
|
+
end # If Arguments List is greater than 0
|
|
308
|
+
xml['xtce'].CommandContainer(:name => "#{packet_name.tr(INVALID_CHARS, REPLACEMENT_CHAR)}_Commands") do
|
|
309
|
+
process_entry_list(xml, packet, :COMMAND, unique_tlm_params)
|
|
310
|
+
#xml['xtce'].BaseContainer(:containerRef => "#{target_name}_#{packet_name}_CommandContainer")
|
|
162
311
|
if packet.id_items && packet.id_items.length > 0
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
xml['xtce'].
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
312
|
+
packet.id_items.each do |item|
|
|
313
|
+
xml['xtce'].BaseContainer(:containerRef => "#{packet_name.tr(INVALID_CHARS, REPLACEMENT_CHAR)}_Commands") do
|
|
314
|
+
xml['xtce'].RestrictionCriteria do
|
|
315
|
+
xml['xtce'].ComparisonList do
|
|
316
|
+
item_prefix = unique_tlm_params.include?(item.name) ? "CMD_" : ""
|
|
317
|
+
xml['xtce'].Comparison(:parameterRef => item_prefix + item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR),:value => item.id_value)
|
|
318
|
+
end
|
|
319
|
+
end # Restriction Criteria
|
|
320
|
+
end # Base Container
|
|
321
|
+
end # for each packet ID item
|
|
322
|
+
end # If id items
|
|
323
|
+
end # Command Container
|
|
324
|
+
end # MetaCommand
|
|
325
|
+
end # each command packet
|
|
172
326
|
end # MetaCommandSet
|
|
173
327
|
end # CommandMetaData
|
|
174
328
|
end
|
|
175
329
|
|
|
330
|
+
def get_numerical_item_initial_value(item)
|
|
331
|
+
initial_value = nil
|
|
332
|
+
if item.states && item.default && !item.array_size
|
|
333
|
+
# Invert hash so we can get the initial value. If not found remove the initial value.
|
|
334
|
+
inverted_enum_states = item.states.invert
|
|
335
|
+
if inverted_enum_states.include?(item.default)
|
|
336
|
+
initial_value = inverted_enum_states[item.default]
|
|
337
|
+
end
|
|
338
|
+
elsif item.default && !item.array_size
|
|
339
|
+
initial_value = item.default
|
|
340
|
+
end
|
|
341
|
+
if initial_value == "1970-01-01T00:00:00Z"
|
|
342
|
+
initial_value = 0
|
|
343
|
+
end
|
|
344
|
+
initial_value
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def get_string_or_block_initial_value(item, string_or_binary)
|
|
348
|
+
initial_value = nil
|
|
349
|
+
if item.default && !item.array_size
|
|
350
|
+
unless item.default.is_printable?
|
|
351
|
+
initial_value = '0x' + item.default.simple_formatted
|
|
352
|
+
else
|
|
353
|
+
if string_or_binary == :STRING
|
|
354
|
+
initial_value = item.default.inspect
|
|
355
|
+
else
|
|
356
|
+
initial_value = item.default.inspect.unpack('H*').first
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
initial_value
|
|
361
|
+
end
|
|
362
|
+
|
|
176
363
|
def get_unique(items)
|
|
177
364
|
unique = {}
|
|
178
365
|
items.each do |packet_name, packet|
|
|
179
366
|
packet.sorted_items.each do |item|
|
|
180
|
-
|
|
367
|
+
unique[item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)] ||= []
|
|
368
|
+
unique[item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)] << item
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
unique.each do |item_name, unique_items|
|
|
372
|
+
if unique_items.length <= 1
|
|
373
|
+
unique[item_name] = unique_items[0]
|
|
374
|
+
next
|
|
375
|
+
end
|
|
376
|
+
unique[item_name] = unique_items[0]
|
|
377
|
+
end
|
|
378
|
+
unique
|
|
379
|
+
end
|
|
181
380
|
|
|
182
|
-
|
|
183
|
-
|
|
381
|
+
def get_unique_without_ids(items)
|
|
382
|
+
unique = {}
|
|
383
|
+
items.each do |packet_name, packet|
|
|
384
|
+
packet.sorted_items.each do |item|
|
|
385
|
+
next if item.data_type == :DERIVED
|
|
386
|
+
next if item.id_value
|
|
387
|
+
unique[item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)] ||= []
|
|
388
|
+
unique[item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)] << item
|
|
184
389
|
end
|
|
185
390
|
end
|
|
186
391
|
unique.each do |item_name, unique_items|
|
|
@@ -188,17 +393,45 @@ module OpenC3
|
|
|
188
393
|
unique[item_name] = unique_items[0]
|
|
189
394
|
next
|
|
190
395
|
end
|
|
191
|
-
# TODO: need to make sure all the items in the array are exactly the same
|
|
192
396
|
unique[item_name] = unique_items[0]
|
|
193
397
|
end
|
|
194
398
|
unique
|
|
195
399
|
end
|
|
196
400
|
|
|
401
|
+
def get_sorted_non_id_items(items)
|
|
402
|
+
sorted_items = []
|
|
403
|
+
items.each do |item|
|
|
404
|
+
next if item.data_type == :DERIVED
|
|
405
|
+
next if item.id_value
|
|
406
|
+
sorted_items.push(item)
|
|
407
|
+
end
|
|
408
|
+
sorted_items
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def get_unique_id_items(items)
|
|
412
|
+
unique = {}
|
|
413
|
+
items.each do |packet_name, packet|
|
|
414
|
+
packet.id_items.each do |item|
|
|
415
|
+
next if item.data_type == :DERIVED
|
|
416
|
+
unique[item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)] ||= []
|
|
417
|
+
unique[item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)] << item
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
unique.each do |item_name, unique_items|
|
|
421
|
+
if unique_items.length <= 1
|
|
422
|
+
unique[item_name.tr(INVALID_CHARS, REPLACEMENT_CHAR)] = unique_items[0]
|
|
423
|
+
next
|
|
424
|
+
end
|
|
425
|
+
unique[item_name.tr(INVALID_CHARS, REPLACEMENT_CHAR)] = unique_items[0]
|
|
426
|
+
end
|
|
427
|
+
unique
|
|
428
|
+
end
|
|
429
|
+
|
|
197
430
|
# This method is almost the same for commands and telemetry except for the
|
|
198
431
|
# XML element name: [Array]ArgumentRefEntry vs [Array]ParameterRefEntry,
|
|
199
432
|
# and XML reference: argumentRef vs parameterRef.
|
|
200
433
|
# Thus we build the name and use send to dynamically dispatch.
|
|
201
|
-
def process_entry_list(xml, packet, cmd_vs_tlm)
|
|
434
|
+
def process_entry_list(xml, packet, cmd_vs_tlm, unique_tlm_params = {})
|
|
202
435
|
if cmd_vs_tlm == :COMMAND
|
|
203
436
|
type = "Argument"
|
|
204
437
|
else # :TELEMETRY
|
|
@@ -208,11 +441,12 @@ module OpenC3
|
|
|
208
441
|
packed = packet.packed?
|
|
209
442
|
packet.sorted_items.each do |item|
|
|
210
443
|
next if item.data_type == :DERIVED
|
|
211
|
-
|
|
212
|
-
|
|
444
|
+
temp_type = item.id_value ? "Parameter" : type
|
|
445
|
+
prefix = (cmd_vs_tlm == :COMMAND && unique_tlm_params.include?(item.name)) ? "CMD_" : ""
|
|
213
446
|
if item.array_size
|
|
214
447
|
# Requiring parameterRef for argument arrays appears to be a defect in the schema
|
|
215
|
-
|
|
448
|
+
reference_symbol = temp_type == "Argument" ? :parameterRef : "#{temp_type.downcase}Ref".to_sym
|
|
449
|
+
xml['xtce'].public_send("Array#{temp_type}RefEntry".intern, reference_symbol => prefix + item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)) do
|
|
216
450
|
set_fixed_value(xml, item) if !packed
|
|
217
451
|
xml['xtce'].DimensionList do
|
|
218
452
|
xml['xtce'].Dimension do
|
|
@@ -227,9 +461,9 @@ module OpenC3
|
|
|
227
461
|
end
|
|
228
462
|
else
|
|
229
463
|
if packed
|
|
230
|
-
xml['xtce'].public_send("#{
|
|
464
|
+
xml['xtce'].public_send("#{temp_type}RefEntry".intern, "#{temp_type.downcase}Ref".intern => prefix + item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR))
|
|
231
465
|
else
|
|
232
|
-
xml['xtce'].public_send("#{
|
|
466
|
+
xml['xtce'].public_send("#{temp_type}RefEntry".intern, "#{temp_type.downcase}Ref".intern => prefix + item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)) do
|
|
233
467
|
set_fixed_value(xml, item)
|
|
234
468
|
end
|
|
235
469
|
end
|
|
@@ -250,29 +484,45 @@ module OpenC3
|
|
|
250
484
|
end
|
|
251
485
|
end
|
|
252
486
|
|
|
253
|
-
def to_xtce_type(item, param_or_arg, xml)
|
|
254
|
-
# TODO: Spline Conversions
|
|
487
|
+
def to_xtce_type(item, param_or_arg, xml, prefix: "")
|
|
255
488
|
case item.data_type
|
|
256
489
|
when :INT, :UINT
|
|
257
|
-
to_xtce_int(item, param_or_arg, xml)
|
|
490
|
+
to_xtce_int(item, param_or_arg, xml, prefix:prefix)
|
|
258
491
|
when :FLOAT
|
|
259
|
-
to_xtce_float(item, param_or_arg, xml)
|
|
492
|
+
to_xtce_float(item, param_or_arg, xml, prefix: prefix)
|
|
260
493
|
when :STRING
|
|
261
|
-
to_xtce_string(item, param_or_arg, xml, 'String')
|
|
494
|
+
to_xtce_string(item, param_or_arg, xml, 'String', prefix: prefix)
|
|
262
495
|
when :BLOCK
|
|
263
|
-
to_xtce_string(item, param_or_arg, xml, 'Binary')
|
|
264
|
-
|
|
265
|
-
|
|
496
|
+
to_xtce_string(item, param_or_arg, xml, 'Binary', prefix: prefix)
|
|
497
|
+
when :DERIVED
|
|
498
|
+
if !COSMOS_NATIVE_DERIVED_ITEMS.include?(item.name)
|
|
499
|
+
to_xtce_derived(item, param_or_arg, xml, prefix: prefix)
|
|
500
|
+
end
|
|
266
501
|
end
|
|
267
502
|
|
|
268
503
|
# Handle arrays
|
|
269
504
|
if item.array_size
|
|
270
505
|
# The above will have created the type for the array entries. Now we create the type for the actual array.
|
|
271
|
-
|
|
506
|
+
|
|
507
|
+
attrs = { :name => (prefix + item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR) + '_ArrayType') }
|
|
272
508
|
attrs[:shortDescription] = item.description if item.description
|
|
273
|
-
attrs[:arrayTypeRef] = (item.name + '_Type')
|
|
274
|
-
|
|
275
|
-
|
|
509
|
+
attrs[:arrayTypeRef] = (prefix + item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR) + '_Type')
|
|
510
|
+
xml['xtce'].public_send('Array' + param_or_arg + 'Type', attrs) do
|
|
511
|
+
xml['xtce'].DimensionList do
|
|
512
|
+
xml['xtce'].Dimension do
|
|
513
|
+
xml['xtce'].StartingIndex do
|
|
514
|
+
xml['xtce'].FixedValue do
|
|
515
|
+
xml['xtce'].text 0
|
|
516
|
+
end # FixedValue
|
|
517
|
+
end # StartingIndex
|
|
518
|
+
xml['xtce'].EndingIndex do
|
|
519
|
+
xml['xtce'].FixedValue do
|
|
520
|
+
xml['xtce'].text 0 # OpenC3 Only supports one-dimensional arrays
|
|
521
|
+
end # FixedValue
|
|
522
|
+
end # EndingIndex
|
|
523
|
+
end # Dimension
|
|
524
|
+
end # DimensionList
|
|
525
|
+
end # Array<param_or_arg>Type
|
|
276
526
|
end
|
|
277
527
|
end
|
|
278
528
|
|
|
@@ -291,25 +541,38 @@ module OpenC3
|
|
|
291
541
|
end
|
|
292
542
|
end
|
|
293
543
|
|
|
294
|
-
def to_xtce_int(item, param_or_arg, xml)
|
|
295
|
-
attrs = { :name => (item.name + '_Type') }
|
|
544
|
+
def to_xtce_int(item, param_or_arg, xml, prefix: "")
|
|
545
|
+
attrs = { :name => (prefix + item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR) + '_Type') }
|
|
296
546
|
attrs[:initialValue] = item.default if item.default and !item.array_size
|
|
297
547
|
attrs[:shortDescription] = item.description if item.description
|
|
548
|
+
if attrs[:initialValue] == "1970-01-01T00:00:00Z"
|
|
549
|
+
attrs[:initialValue] = "0"
|
|
550
|
+
end
|
|
298
551
|
if item.states and item.default and item.states.key(item.default)
|
|
299
552
|
attrs[:initialValue] = item.states.key(item.default) and !item.array_size
|
|
300
553
|
end
|
|
301
554
|
if item.data_type == :INT
|
|
302
555
|
signed = 'true'
|
|
303
|
-
encoding = '
|
|
556
|
+
encoding = 'twosComplement'
|
|
304
557
|
else
|
|
305
558
|
signed = 'false'
|
|
306
559
|
encoding = 'unsigned'
|
|
307
560
|
end
|
|
308
561
|
if item.states
|
|
562
|
+
# Invert hash so we can get the initial value. If not found remove the initial value.
|
|
563
|
+
inverted_enum_states = item.states.invert
|
|
564
|
+
if inverted_enum_states.include?(item.default)
|
|
565
|
+
attrs[:initialValue] = inverted_enum_states[item.default]
|
|
566
|
+
else
|
|
567
|
+
attrs.delete(:initialValue)
|
|
568
|
+
end
|
|
309
569
|
xml['xtce'].public_send('Enumerated' + param_or_arg + 'Type', attrs) do
|
|
310
|
-
to_xtce_endianness(item, xml)
|
|
311
570
|
to_xtce_units(item, xml)
|
|
312
|
-
|
|
571
|
+
if item.endianness == :LITTLE_ENDIAN and item.bit_size > 8
|
|
572
|
+
xml['xtce'].IntegerDataEncoding(:sizeInBits => item.bit_size, :encoding => encoding, :byteOrder => "leastSignificantByteFirst")
|
|
573
|
+
else
|
|
574
|
+
xml['xtce'].IntegerDataEncoding(:sizeInBits => item.bit_size, :encoding => encoding)
|
|
575
|
+
end
|
|
313
576
|
xml['xtce'].EnumerationList do
|
|
314
577
|
item.states.each do |state_name, state_value|
|
|
315
578
|
# Skip the special OpenC3 'ANY' enumerated state
|
|
@@ -327,53 +590,86 @@ module OpenC3
|
|
|
327
590
|
attrs[:signed] = signed
|
|
328
591
|
end
|
|
329
592
|
xml['xtce'].public_send(type_string, attrs) do
|
|
330
|
-
to_xtce_endianness(item, xml)
|
|
331
593
|
to_xtce_units(item, xml)
|
|
332
594
|
if (item.read_conversion and item.read_conversion.class == PolynomialConversion) or (item.write_conversion and item.write_conversion.class == PolynomialConversion)
|
|
333
|
-
|
|
334
|
-
|
|
595
|
+
if item.endianness == :LITTLE_ENDIAN and item.bit_size >= 8
|
|
596
|
+
xml['xtce'].IntegerDataEncoding(:sizeInBits => item.bit_size, :encoding => encoding, :byteOrder => "leastSignificantByteFirst") do
|
|
597
|
+
to_xtce_conversion(item, xml)
|
|
598
|
+
end
|
|
599
|
+
else
|
|
600
|
+
xml['xtce'].IntegerDataEncoding(:sizeInBits => item.bit_size, :encoding => encoding) do
|
|
601
|
+
to_xtce_conversion(item, xml)
|
|
602
|
+
end
|
|
335
603
|
end
|
|
336
604
|
else
|
|
337
|
-
|
|
605
|
+
if item.endianness == :LITTLE_ENDIAN and item.bit_size >= 8
|
|
606
|
+
xml['xtce'].IntegerDataEncoding(:sizeInBits => item.bit_size, :encoding => encoding, :byteOrder => "leastSignificantByteFirst")
|
|
607
|
+
else
|
|
608
|
+
xml['xtce'].IntegerDataEncoding(:sizeInBits => item.bit_size, :encoding => encoding)
|
|
609
|
+
end
|
|
338
610
|
end
|
|
339
611
|
to_xtce_limits(item, xml)
|
|
340
|
-
if item.range
|
|
341
|
-
|
|
612
|
+
if item.range and item.range.last <= MAX_64_BIT_INT
|
|
613
|
+
if param_or_arg == "Parameter"
|
|
614
|
+
xml['xtce'].ValidRange(:minInclusive => item.range.first, :maxInclusive => item.range.last)
|
|
615
|
+
else
|
|
616
|
+
xml['xtce'].ValidRangeSet do
|
|
617
|
+
xml['xtce'].ValidRange(:minInclusive => item.range.first, :maxInclusive => item.range.last)
|
|
618
|
+
end
|
|
619
|
+
end
|
|
342
620
|
end
|
|
343
621
|
end # Type
|
|
344
622
|
end # if item.states
|
|
345
623
|
end
|
|
346
624
|
|
|
347
|
-
def to_xtce_float(item, param_or_arg, xml)
|
|
348
|
-
attrs = { :name => (item.name + '_Type'), :sizeInBits => item.bit_size }
|
|
625
|
+
def to_xtce_float(item, param_or_arg, xml, prefix: "")
|
|
626
|
+
attrs = { :name => (prefix + item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR) + '_Type'), :sizeInBits => item.bit_size }
|
|
349
627
|
attrs[:initialValue] = item.default if item.default and !item.array_size
|
|
350
628
|
attrs[:shortDescription] = item.description if item.description
|
|
351
629
|
xml['xtce'].public_send('Float' + param_or_arg + 'Type', attrs) do
|
|
352
|
-
to_xtce_endianness(item, xml)
|
|
353
630
|
to_xtce_units(item, xml)
|
|
354
631
|
if (item.read_conversion and item.read_conversion.class == PolynomialConversion) or (item.write_conversion and item.write_conversion.class == PolynomialConversion)
|
|
355
|
-
|
|
632
|
+
if item.endianness == :LITTLE_ENDIAN and item.bit_size >= 8
|
|
633
|
+
xml['xtce'].FloatDataEncoding(:sizeInBits => item.bit_size, :encoding => 'IEEE754_1985', :byteOrder => "leastSignificantByteFirst") do
|
|
356
634
|
to_xtce_conversion(item, xml)
|
|
357
635
|
end
|
|
636
|
+
else
|
|
637
|
+
xml['xtce'].FloatDataEncoding(:sizeInBits => item.bit_size, :encoding => 'IEEE754_1985') do
|
|
638
|
+
to_xtce_conversion(item, xml)
|
|
639
|
+
end
|
|
640
|
+
end
|
|
358
641
|
else
|
|
359
|
-
|
|
642
|
+
if item.endianness == :LITTLE_ENDIAN and item.bit_size >= 8
|
|
643
|
+
xml['xtce'].FloatDataEncoding(:sizeInBits => item.bit_size, :encoding => 'IEEE754_1985', :byteOrder => "leastSignificantByteFirst")
|
|
644
|
+
else
|
|
645
|
+
xml['xtce'].FloatDataEncoding(:sizeInBits => item.bit_size, :encoding => 'IEEE754_1985')
|
|
646
|
+
end
|
|
360
647
|
end
|
|
361
648
|
to_xtce_limits(item, xml)
|
|
362
|
-
if item.range
|
|
363
|
-
|
|
649
|
+
if item.range and item.range.last < MAX_64_BIT_INT
|
|
650
|
+
if param_or_arg == "Parameter"
|
|
651
|
+
xml['xtce'].ValidRange(:minInclusive => item.range.first, :maxInclusive => item.range.last)
|
|
652
|
+
else
|
|
653
|
+
xml['xtce'].ValidRangeSet do
|
|
654
|
+
xml['xtce'].ValidRange(:minInclusive => item.range.first, :maxInclusive => item.range.last)
|
|
655
|
+
end
|
|
656
|
+
end
|
|
364
657
|
end
|
|
365
658
|
end
|
|
366
659
|
end
|
|
367
660
|
|
|
368
|
-
def to_xtce_string(item, param_or_arg, xml, string_or_binary)
|
|
369
|
-
|
|
370
|
-
attrs = { :name => (item.name + '_Type') }
|
|
661
|
+
def to_xtce_string(item, param_or_arg, xml, string_or_binary, prefix: "")
|
|
662
|
+
attrs = { :name => (prefix + item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR) + '_Type') }
|
|
371
663
|
attrs[:characterWidth] = 8 if string_or_binary == 'String'
|
|
372
664
|
if item.default && !item.array_size
|
|
373
665
|
unless item.default.is_printable?
|
|
374
666
|
attrs[:initialValue] = '0x' + item.default.simple_formatted
|
|
375
667
|
else
|
|
376
|
-
|
|
668
|
+
if string_or_binary == 'String'
|
|
669
|
+
attrs[:initialValue] = item.default.inspect
|
|
670
|
+
else
|
|
671
|
+
attrs[:initialValue] = item.default.inspect.unpack('H*').first
|
|
672
|
+
end
|
|
377
673
|
end
|
|
378
674
|
end
|
|
379
675
|
attrs[:shortDescription] = item.description if item.description
|
|
@@ -384,10 +680,15 @@ module OpenC3
|
|
|
384
680
|
xml['xtce'].StringDataEncoding(:encoding => 'UTF-8') do
|
|
385
681
|
xml['xtce'].SizeInBits do
|
|
386
682
|
xml['xtce'].Fixed do
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
683
|
+
if item.bit_size != 0
|
|
684
|
+
xml['xtce'].FixedValue(item.bit_size.to_s)
|
|
685
|
+
else
|
|
686
|
+
xml['xtce'].FixedValue(DYNAMIC_STRING_LEN)
|
|
687
|
+
end # if statement
|
|
688
|
+
end # </Fixed>
|
|
689
|
+
xml['xtce'].TerminationChar("00")
|
|
690
|
+
end # </SizeInBits>
|
|
691
|
+
end # </StringDataEncoding>
|
|
391
692
|
else
|
|
392
693
|
xml['xtce'].BinaryDataEncoding do
|
|
393
694
|
xml['xtce'].SizeInBits do
|
|
@@ -398,11 +699,74 @@ module OpenC3
|
|
|
398
699
|
end
|
|
399
700
|
end
|
|
400
701
|
|
|
401
|
-
def
|
|
702
|
+
def to_xtce_derived(item, param_or_arg, xml, prefix: "")
|
|
703
|
+
if item.name == @packet_time_string
|
|
704
|
+
xml << "\n<!--TODO: \n" \
|
|
705
|
+
"\t<xtce:AbsoluteTime#{param_or_arg}Type name=\"#{item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)}_Type\">\n" \
|
|
706
|
+
"\t\t<TODO/>\n" \
|
|
707
|
+
"\t</xtce:AbsoluteTime#{param_or_arg}Type>"
|
|
708
|
+
"-->\n"
|
|
709
|
+
else
|
|
710
|
+
description_string = item.description ? "shortDescription=\"#{item.description}\"" : ""
|
|
711
|
+
xml << "\n<!--TODO: \n" \
|
|
712
|
+
"\t<xtce:TODO#{param_or_arg}Type name=\"#{item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)}_Type\" #{description_string} />\n" \
|
|
713
|
+
"-->\n"
|
|
714
|
+
end
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
def to_xtce_item(item, param_or_arg, xml, prefix: "", has_packet_time: false)
|
|
718
|
+
replaced_item_name = prefix + item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)
|
|
402
719
|
if item.array_size
|
|
403
|
-
|
|
720
|
+
type_suffix = "_ArrayType"
|
|
721
|
+
else
|
|
722
|
+
type_suffix = "_Type"
|
|
723
|
+
end
|
|
724
|
+
attrs = {:name => replaced_item_name, "#{param_or_arg.downcase}TypeRef" => replaced_item_name + type_suffix}
|
|
725
|
+
needs_alias = item.name.count(INVALID_CHARS) > 0 || !prefix.empty?
|
|
726
|
+
if param_or_arg.downcase == "argument"
|
|
727
|
+
# Set the name to just be the item name since ArgumentsTypes
|
|
728
|
+
# will use the packet name as a prefix but not in the actual argument name.
|
|
729
|
+
# Maintains the individual type between arguments with a shared name.
|
|
730
|
+
needs_alias = item.name.count(INVALID_CHARS) > 0
|
|
731
|
+
attrs[:name] = item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)
|
|
732
|
+
initial_value = case item.data_type
|
|
733
|
+
when :INT, :UINT, :FLOAT
|
|
734
|
+
get_numerical_item_initial_value(item)
|
|
735
|
+
when :STRING, :BLOCK
|
|
736
|
+
get_string_or_block_initial_value(item, item.data_type)
|
|
737
|
+
when :DERIVED
|
|
738
|
+
nil
|
|
739
|
+
end
|
|
740
|
+
attrs[:initialValue] = initial_value
|
|
741
|
+
end
|
|
742
|
+
if item.data_type == :DERIVED
|
|
743
|
+
if COSMOS_NATIVE_DERIVED_ITEMS.include?(item.name)
|
|
744
|
+
return
|
|
745
|
+
end
|
|
746
|
+
parameter_comment = "\n<!-- TODO: \n" \
|
|
747
|
+
"\t<xtce:#{param_or_arg} name=\"#{item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)}\" #{param_or_arg.downcase}TypeRef=\"#{item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)}_Type\">\n" \
|
|
748
|
+
"\t\t<xtce:ParameterProperties dataSource=\"derived\"/>\n"
|
|
749
|
+
|
|
750
|
+
if needs_alias
|
|
751
|
+
parameter_comment += "\t\t<xtce:AliasSet>\n" \
|
|
752
|
+
"\t\t\t<xtce:Alias nameSpace=\"COSMOS\" alias=\"#{item.name.tr(INVALID_CHARS, REPLACEMENT_CHAR)}\"/>\n" \
|
|
753
|
+
"\t\t</xtce:AliasSet>\n"
|
|
754
|
+
end
|
|
755
|
+
parameter_comment += "\t</xtce:#{param_or_arg}>\n-->\n"
|
|
756
|
+
xml << parameter_comment
|
|
404
757
|
else
|
|
405
|
-
xml['xtce'].public_send(param_or_arg,
|
|
758
|
+
xml['xtce'].public_send(param_or_arg, attrs) do
|
|
759
|
+
if needs_alias
|
|
760
|
+
xml['xtce'].AliasSet do
|
|
761
|
+
xml['xtce'].Alias(:nameSpace => ALIAS_NAMESPACE, :alias => item.name)
|
|
762
|
+
end
|
|
763
|
+
end
|
|
764
|
+
if has_packet_time
|
|
765
|
+
xml['xtce'].ParameterProperties do
|
|
766
|
+
xml['xtce'].TimeAssociation(:parameterRef => @packet_time_string)
|
|
767
|
+
end
|
|
768
|
+
end
|
|
769
|
+
end
|
|
406
770
|
end
|
|
407
771
|
end
|
|
408
772
|
|
|
@@ -437,10 +801,10 @@ module OpenC3
|
|
|
437
801
|
xml['xtce'].PolynomialCalibrator do
|
|
438
802
|
conversion.coeffs.each_with_index do |coeff, index|
|
|
439
803
|
xml['xtce'].Term(:coefficient => coeff, :exponent => index)
|
|
440
|
-
end
|
|
441
|
-
end
|
|
442
|
-
end
|
|
443
|
-
end
|
|
804
|
+
end # for each loop
|
|
805
|
+
end # </PolynomialCalibrator>
|
|
806
|
+
end # </DefaultCalibrator>
|
|
807
|
+
end # if PolynomialConversion
|
|
444
808
|
end
|
|
445
809
|
end
|
|
446
810
|
end
|