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
# 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_vendor = root_component.xpath("./spirit:component/spirit:vendor").text
  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

  component = IPXACT::Component.new(instance_name, component_name, component_vendor, component_version)
  component.interconnections = interconnections
  component.hierconnections = hierconnections
  component.subcomponents = instances
  component.ports = ports
  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.
    


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

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::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.



152
153
154
155
156
157
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
# File 'lib/ipxact/parser/component_data_parser.rb', line 152

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::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]
      
      constraint = constraints.select{|constraint| constraint[0] == component_reference[:name]}.first
      if constraint.nil?
        acc[local_instance_name] = parse_component([component_reference[:name], component_reference[:version]], local_instance_name, local_variables, component_docs, design_docs, constraints)
      else
        acc[local_instance_name] = parse_component(constraint, 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.
    


242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/ipxact/parser/component_data_parser.rb', line 242

def self.parse_hierconnections(design_doc, component_instances)
  design_doc.xpath("//spirit:hierConnection").inject({}) do |acc, hier|
    interface = hier.xpath("./spirit:interface").first
    port = component_instances[interface['componentRef']].ports.select{|port| port[:name] == interface['busRef']}.first
    acc[hier['interfaceRef']] = {
      :port_name => interface['busRef'],
      :port_type => port[:type],
      :component_instance => interface['componentRef']
    }
    acc
  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.
    


204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/ipxact/parser/component_data_parser.rb', line 204

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']]
      port = component_instance.ports.select{|port| port[:name] == active_interface['busRef']}.first
      {
        :port_name => port[:name],
        :port_type => port[:type],
        :component_instance => active_interface['componentRef']
      }
    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.



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/ipxact/parser/component_data_parser.rb', line 294

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.



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/ipxact/parser/component_data_parser.rb', line 109

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 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::is_most_recent_version?(a,b)
                                              0 if IPXACT::is_same_version?(a,b)
                                              -1
                                           }.last
  end
  [component_name, component_version]
end