Module: IPXACT::Parser::ComponentData

Defined in:
lib/ipxact/parser/component_data_parser.rb

Overview

Module for parsing IPXACT XML data and creating component objects.

Author:

  • Guillaume Godet-Bar

Constant Summary collapse

AUTHORIZED_CONNECTIONS =

The list of authorized connections (between two component instances in an interconnection).

[
  {
    :from => :mirrored_slave,
    :to   => :slave
  },
  {
    :from => :master,
    :to   => :mirrored_master
  },
  {
    :from => :master,
    :to   => :slave
  }
]

Class Method Summary collapse

Class Method Details

.parse_component(component_id, instance_name, variables, component_docs, design_docs, constraints) ⇒ Component

Parses the component identified by component_id, assigns the variables associated to instance_name, and produces a Component object.

Parameters:

  • component_id (Array<String>, String)

    the component identifier, which should appear in one of the component_docs. This identifier can either be a String (in which circumstance the most recent component with the given name will be returned, if any) or an Array of 2 elements, composed as follows:

    [<component_identifier>, <component_version>]
    
  • instance_name (String)

    the name of the component instance (if any) that is being parsed.

  • variables (Array<Hash>)

    the variables associated with the instance_name.

  • component_docs (Hash<String, Nokogiri::Node>)

    the Hash of Nokogiri component documents that was extracted from the platform’s IPXACT specification.

  • design_docs (Hash<String, Nokogiri::Node>)

    the Hash of Nokogiri design documents that was extracted from the platform’s IPXACT specification.

  • contraints (Array<String>)

    an Array of constraints imposed on components. A constraint is an Array of 2 elements, composed as follows:

    [<component_identifier>, <component_version>]
    

    For each component instanciated during the parsing of the platform, the parser will verify if any of the constraints may apply and then select the appropriate component.

Returns:

  • (Component)

    the corresponding component



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/ipxact/parser/component_data_parser.rb', line 72

def self.parse_component(component_id, instance_name, variables, component_docs, design_docs, constraints)
  component_name, component_version = select_component(component_id, component_docs)

  root_component = component_docs[[component_name, component_version]]
  component_id = IPXACT::Parser::IdentifierData.parse_identifier(root_component.xpath("./spirit:component"))
  views = root_component.xpath("//spirit:model/spirit:views/spirit:view")

  instances, interconnections, hierconnections = parse_design_views(views, component_docs, design_docs, constraints)

  ports = root_component.xpath(".//spirit:busInterface").collect do |a_bus_interface|
    IPXACT::Parser::BusData.parse_bus(a_bus_interface, root_component, variables)
  end

  cpus = root_component.xpath(".//spirit:cpu").collect do |a_cpu|
    { :name => a_cpu.xpath("./spirit:name").first.text }
  end

  component = IPXACT::Component.new(instance_name, component_id)
  component.interconnections = interconnections
  component.hierconnections = hierconnections
  component.subcomponents = instances
  component.ports = ports
  component.cpus = cpus
  component
end

.parse_component_instances(design_doc) ⇒ Hash

Extracts relevant data from an IPXACT design document (i.e., variable configurations)

Parameters:

  • design_doc (Nokogiri::Node)

    a valid IPXACT design document.

Returns:

  • (Hash)

    a Hash that associates the component instance names with variable configurations. A variable configuration is composed of:

    :variables  - The list of variables (name - value associations)
    :reference  - The component type of the instance to which the variables
                should be affected.
    


290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/ipxact/parser/component_data_parser.rb', line 290

def self.parse_component_instances(design_doc)
  design_doc.xpath("//spirit:componentInstance").inject({}) do |acc, instance|

    variables = instance.xpath(".//spirit:configurableElementValue").inject({}) do |acc, value|
        acc[value['referenceId']] = value.text
        acc
    end
    acc[instance.xpath("./spirit:instanceName").text] = 
      {
        :reference => IPXACT::Parser::IdentifierData::parse_reference(instance.xpath("./spirit:componentRef").first),
        :variables => variables
      }
    acc
  end
end

.parse_design_views(views, component_docs, design_docs, constraints) ⇒ Array<Hash>

Scans the given views for a hierarchical view. If such a view is found, the method loads the referenced design document, and parses all component instances, interconnections and hierconnections that are described by the view.

Parameters:

  • views (Array<Nokogiri::Node>)

    the views from an IPXACT XML that should be scanned for a hierarchical view.

  • component_docs (Hash<String, Nokogiri::Node>)

    the Nokogiri component documents that were extracted from the platform’s IPXACT specification.

  • design_docs (Hash<String, Nokogiri::Node>)

    the design documents that were extracted from the platform’s IPXACT specification.

  • constraints (Array<Array>)

    an Array of constraints imposed on components. A constraint is an Array of 2 elements, composed as follows:

    [<component_identifier>, <component_version>]
    

    For each component instanciated during the parsing of the platform, the parser will verify if any of the constraints may apply and then select the appropriate component.

Returns:

  • (Array<Hash>)

    an Array containing 3 Hashes, respectively: the component instances Hash, the interconnections Hash and the hierconnections Hash. Empty hashes are returned by default.

Raises:

  • an exception if any duplicate interconnection is found.



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/ipxact/parser/component_data_parser.rb', line 158

def self.parse_design_views(views, component_docs, design_docs, constraints)
  design_view = views.select{|view| view.xpath("./spirit:name").text == 'spirit-design' && view.xpath("./spirit:envIdentifier").text == '::Hierarchy'}
  if design_view.size == 1
    design_ref = IPXACT::Parser::IdentifierData.parse_reference(design_view.first.xpath("./spirit:hierarchyRef").first)

    design_doc = design_docs[[design_ref.name, design_ref.version]]

    instances_map = parse_component_instances(design_doc)
    instances = instances_map.inject({}) do |acc, entry|
      local_instance_name = entry[0]
      component_reference = entry[1][:reference]
      local_variables =  entry[1][:variables]
      
      if constraint = constraints.find{|constraint| constraint[0] == component_reference.name}
        acc[local_instance_name] = parse_component(constraint,
                                                   local_instance_name,
                                                   local_variables,
                                                   component_docs,
                                                   design_docs, constraints)
      else
        acc[local_instance_name] = parse_component([
                                                    component_reference.name,
                                                    component_reference.version
                                                   ],
                                                   local_instance_name,
                                                   local_variables,
                                                   component_docs,
                                                   design_docs, constraints)
      end
      acc
    end

    interconnections = parse_interconnections(design_doc, instances)
    interconnection_sources = interconnections.values.collect{|v| v[0]}
    interconnection_targets = interconnections.values.collect{|v| v[1]}
    raise 'Duplicate connections!' \
      if interconnection_sources.size != interconnection_sources.uniq.size ||
         interconnection_targets.size != interconnection_targets.uniq.size

    hierconnections = parse_hierconnections(design_doc, instances)
  end

  [(instances || {}), (interconnections || {}), (hierconnections || {})]

end

.parse_hierconnections(design_doc, component_instances) ⇒ Hash

Parses a design document’s hierconnections. A hierconnection corresponds to a connection between a port from the outside interface of the component instanciated by the design document with one of the component’s subcomponents’ ports.

Parameters:

  • design_doc (Nokogiri::Node)

    the IPXACT design document that should be parsed for extracting the hierconnections.

  • component_instances (Array<Component>)

    the list of Component instances (i.e. parsed components) that correspond to the design_doc design document’s root component’s subcomponents.

Returns:

  • (Hash)

    a Hash that associates the outside port’s name with the the following Hash:

    :port_name          - The corresponding subcomponent's port name.
    :port_type          - The corresponding subcomponent's port type.
    :component_instance - The subcomponent's instance.
    

Raises:

  • an exception if any port connection could not be found.



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/ipxact/parser/component_data_parser.rb', line 263

def self.parse_hierconnections(design_doc, component_instances)
  design_doc.xpath("//spirit:hierConnection").inject({}) do |acc, hier|
    interface = hier.xpath("./spirit:interface").first
    if port = component_instances[interface['componentRef']].ports.find{|port| port[:name] == interface['busRef']}
      acc[hier['interfaceRef']] = {
        :port_name => interface['busRef'],
        :port_type => port[:type],
        :component_instance => interface['componentRef']
      }
      acc
    else
      raise "Could not find port with name \"#{interface['busRef']}\" in component list."
    end
  end
end

.parse_interconnections(design_doc, component_instances) ⇒ Hash

Parses the IPXACT’s design document for constructing the corresponding component instance interconnection.

Parameters:

  • design_doc (Nokogiri::Node)

    the IPXACT design document that should be parsed for extracting the interconnection’s data.

  • component_instances (Array<Component>)

    the list of Component instances (i.e. parsed components) that correspond to the design_doc‘s root component’s subcomponents.

Returns:

  • (Hash)

    a Hash that associates the interconnection’s name with an ordered Array (see reorder_connection) of Hashes, composed as follows:

    :port_name          - The subcomponent's port name.
    :port_type          - The subcomponent's port type.
    :component_instance - The subcomponent instance.
    

Raises:

  • an exception if any port connection cannot be found.



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/ipxact/parser/component_data_parser.rb', line 221

def self.parse_interconnections(design_doc, component_instances)
  design_doc.xpath("//spirit:interconnection").inject({}) do |acc, inter|
    unordered_connection = inter.xpath("./spirit:activeInterface").collect do |active_interface|
      component_instance = component_instances[active_interface['componentRef']]
      if port = component_instance.ports.find{|port| port[:name] == active_interface['busRef']}
        {
          :port_name => port[:name],
          :port_type => port[:type],
          :component_instance => active_interface['componentRef']
        }
      else
        raise "Could not find port named \"#{active_interface['busRef']}\" for component \"#{component_instance.instance_name}\""
      end
    end

    ordered_connection = reorder_connection(unordered_connection)

    acc[inter.xpath("./spirit:name").first.text] = ordered_connection
    acc
  end
end

.reorder_connection(connection) ⇒ Array

Reorders IPXACT connections (which are not oriented in IPXACT specifications), following the list of authorized connections described in AUTHORIZED_CONNECTIONS.

Parameters:

  • connection (Array<Hash>)

    a connection Array, that should contain 2 elements. Each element must be a Hash with at least one :port_type key.

Returns:

  • (Array)

    the connection Array, reversed if necessary.

Raises:

  • an exception if no permutation of the connection elements corresponds to an authorized connection.



318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/ipxact/parser/component_data_parser.rb', line 318

def self.reorder_connection(connection)
  if AUTHORIZED_CONNECTIONS.include?({
    :from => connection[0][:port_type],
    :to => connection[1][:port_type]
  })
    connection
  elsif AUTHORIZED_CONNECTIONS.include?({
    :from => connection[1][:port_type],
    :to => connection[0][:port_type]
  })
    connection.reverse
  else
    raise 'Invalid interconnect list!'
  end
end

.select_component(component_id, component_docs) ⇒ Component

Selects from the component_docs the component that corresponds to the component_id.

Parameters:

  • component_id (String, Array<String>)

    the component identifier, which should appear in one of the component_docs. This identifier can either be a String (in which circumstance the most recent component with the given name will be returned, if any) or an Array of 2 elements, composed as follows:

    [<component_identifier>, <component_version>]
    
  • component_docs (Hash<String, Nokogiri::Node>)

    the Hash of Nokogiri component documents that was extracted from the platform’s IPXACT specification.

Returns:

  • (Component)

    the component identified by component_id.

Raises:

  • (ArgumentError)

    if no component could be found.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/ipxact/parser/component_data_parser.rb', line 114

def self.select_component(component_id, component_docs)
  if component_id.kind_of?(Array) && component_id.size == 2
    # A version must have been specified
    raise ArgumentError, "Component '#{component_id}' could not be found!" \
      unless component_docs.has_key? component_id 
    component_name, component_version = component_id
  else
    component_name = component_id
    component_version = component_docs.keys.select{|name, version| name == component_name} \
                                           .collect{|name, version| version} \
                                           .sort{|a, b| 
                                              1 if IPXACT::Identifier::is_most_recent_version?(a,b)
                                              0 if IPXACT::Identifier::is_same_version?(a,b)
                                              -1
                                           }.last
  end
  [component_name, component_version]
end