Class: OpenC3::XtceConverter

Inherits:
Object
  • Object
show all
Defined in:
lib/openc3/packets/parsers/xtce_converter.rb

Constant Summary collapse

XTCE_1_2_NAMESPACE =

The namespace COSMOS exports. Files declaring an older namespace still import.

'http://www.omg.org/spec/XTCE/20180204'
SCHEMA_DIR =

The OMG schemas are vendored here and validation reads them from disk, so it works air gapped. The omg.org URL the files declare in schemaLocation is never fetched.

File.join(PATH, 'data', 'xtce_schemas')
SCHEMA_FILE =
File.join(SCHEMA_DIR, 'SpaceSystem_20180204.xsd')

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Attribute Details

#current_target_nameObject

Returns the value of attribute current_target_name.



36
37
38
# File 'lib/openc3/packets/parsers/xtce_converter.rb', line 36

def current_target_name
  @current_target_name
end

Class Method Details

.combine_output_xtce(output_dir, root_target_name = nil) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/openc3/packets/parsers/xtce_converter.rb', line 80

def self.combine_output_xtce(output_dir, root_target_name = nil)
  combined_file_directory = File.join(output_dir, 'TARGETS_COMBINED', 'cmd_tlm')
  begin
    FileUtils.rm_rf(combined_file_directory)
  rescue
    # doesn't exist
  end
  file_pattern = File.join(output_dir, "**", "*.xtce")
  xml_files = Dir.glob(file_pattern)
  if xml_files.empty?
      puts "No *.xtce files found to combine."
  elsif xml_files.length == 1
      puts "Only one *.xtce file found. No need to unify."
  else
    puts "Multiple targets found. Creating Unified XTCE representation."
    FileUtils.mkdir_p(combined_file_directory)
    file_basename = "combined"
    xml_files.each do |file_path|
      file_basename += "_#{File.basename(file_path, ".*")}"
    end
    full_file_name = File.join(combined_file_directory, file_basename.downcase + '.xtce')
    begin
      File.delete(full_file_name)
    rescue
      # Doesn't exist
    end
    xml_files.each do |file_path|
      file_basename += File.basename(file_path, ".*")
    end
    root_builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
      xml['xtce'].SpaceSystem("xmlns:xtce" => "http://www.omg.org/spec/XTCE/20180204",
                              "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
                              "name" => root_target_name ? root_target_name : "root",
                              "xsi:schemaLocation" => "http://www.omg.org/spec/XTCE/20180204 https://www.omg.org/spec/XTCE/20180204/SpaceSystem.xsd")
    end
    new_doc = root_builder.doc
    new_root = new_doc.root
    xml_files.each do |file_path|
      source_doc = Nokogiri::XML(File.open(file_path))
      target_root = source_doc.root
      target_root.attributes.each do |name, attr|
        unless name == "name"
          attr.remove
        end
      end
      if root_target_name == target_root["name"]
        nodes_to_add_reversed = target_root.children.to_a.reverse
        nodes_to_add_reversed.each do |child_node|
          new_root.prepend_child(child_node)
        end
      else
        new_root.add_child(target_root)
      end
    end
    File.open(full_file_name, 'w') do |file|
      file.puts new_doc.to_xml
    end
    full_file_name
  end
end

.convert(commands, telemetry, output_dir, time_association_name) ⇒ Object

Output a previously parsed definition file into the XTCE format

Parameters:

  • commands (Hash<String=>Packet>)

    Hash of all the command packets keyed by the packet name.

  • telemetry (Hash<String=>Packet>)

    Hash of all the telemetry packets keyed by the packet name. that were created while parsing the configuration

  • output_dir (String)

    The name of the output directory to generate the XTCE files. A file is generated for each target.



76
77
78
# File 'lib/openc3/packets/parsers/xtce_converter.rb', line 76

def self.convert(commands, telemetry, output_dir, time_association_name)
  XtceConverter.new(commands, telemetry, output_dir, time_association_name)
end

.schema_errors(filename) ⇒ Array<String>

Validate an .xtce file against the vendored OMG XTCE 1.2 schema

Only meaningful for files declaring XTCE_1_2_NAMESPACE. XTCE 1.0 / 1.1 files import fine but report errors against this schema, so check xtce_namespace first.

Parameters:

  • filename (String)

    Path to a .xtce file

Returns:

  • (Array<String>)

    Validation error messages, empty when the file is valid



60
61
62
63
64
65
# File 'lib/openc3/packets/parsers/xtce_converter.rb', line 60

def self.schema_errors(filename)
  # File.open, not File.read: the schema imports xml.xsd relative to itself, which
  # Nokogiri can only resolve from the IO's path
  @schema ||= File.open(SCHEMA_FILE) { |f| Nokogiri::XML::Schema(f) }
  @schema.validate(Nokogiri::XML(File.read(filename))).map(&:message)
end

.xtce_namespace(filename) ⇒ String?

The XTCE namespace an existing .xtce file declares on its root element

Parameters:

  • filename (String)

    Path to a .xtce file

Returns:

  • (String, nil)

    The declared namespace, or nil if the file has none



49
50
51
# File 'lib/openc3/packets/parsers/xtce_converter.rb', line 49

def self.xtce_namespace(filename)
  Nokogiri::XML(File.read(filename)).root&.namespace&.href
end