Module: Syskit::Models::Composition

Includes:
Base, Component, PortAccess
Included in:
Composition
Defined in:
lib/syskit/models/composition.rb

Overview

Model-level instances and attributes for compositions

See the documentation of Model for an explanation of the *Model modules.

Defined Under Namespace

Classes: DynamicServiceInstantiationContext

Constant Summary

Constants included from Component

Syskit::Models::Component::PROVIDES_ARGUMENTS

Instance Attribute Summary

Attributes included from Component

#concrete_model

Instance Method Summary collapse

Methods included from Component

#all_data_service, #all_dynamic_service, #all_stub_module, #apply_missing_dynamic_services_from, #as, #as_plan, #bind, #can_merge?, #component_model?, #compute_port_mappings, #concrete_model?, #connected?, #create_proxy_task, #create_proxy_task_model, #data_service, #data_services, #deregister_placeholder_model, #deregister_submodels, #driver_for, #dynamic_service, #dynamic_services, #each_com_bus_driver_service, #each_data_service, #each_dynamic_service, #each_master_driver_service, #each_port, #each_required_dynamic_service, #each_required_model, #each_root_data_service, #each_slave_data_service, #each_stub_module, #ensure_model_is_specialized, #find_all_data_services_from_type, #find_data_service_from_type, #find_directional_port_mapping, #find_matching_service, #find_placeholder_model, #find_port, #if_already_present, #implicit_fullfilled_model, #instanciate_dynamic_input_port, #instanciate_dynamic_output_port, #merge_service_model, #method_missing, #needs_stub?, #placeholder?, #port_mappings_for, #port_mappings_for_task, #prefer_deployed_tasks, #prepare_stub, #private_specialization=, #private_specialization?, #promote_data_service, #promote_dynamic_service, #provides, #provides_dynamic, #proxy_task_model, #register_placeholder_model, #require_dynamic_service, #resolve, #selected_for, #self_port?, #self_port_to_component_port, #specialization_counter, #stub, #stub_module, #supermodel, #to_component_model, #try_bind, #try_resolve, #use_conf, #use_deployments, #with_arguments, #with_conf, #with_dynamic_service

Methods included from DataService

#using_data_service?

Methods included from Base

#short_name, #to_instance_requirements, #to_s

Methods included from PortAccess

#each_port, #find_port, #has_input_port?, #has_output_port?, #has_port?, #port_by_name

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Syskit::Models::Component

Instance Method Details

#add(models, options = Hash.new) ⇒ CompositionChild

Add an element in this composition.

Subclassing

If the composition model is a subclass of another composition model, then add can be used to override a child definition. In if it the case, if model is a component model, then it has to be a subclass of any component model that has been used in the parent composition. Otherwise, #add raises ArgumentError

Examples:

data_service_type 'OrientationSrv'
class OrientationFilter < Syskit::Composition
  # Add a child called 'orientation'
  add OrientationSrv, :as => 'orientation'
end
# Returns the object that defines the new child
Orientation.orientation_child

overloading a child with an unrelated data service


data_service_type "RawImuReadings"
class Foo < Orientation
  # This is fine as +raw_imu_readings+ and +orientation_provider+
  # can be combined. +submodel+ will require 'imu' to provide both
  # a RawImuReadings data service and a Orientation data service.
  add RawImuReadings, :as => 'imu' 
end

overloading a child with an incompatible task model


class Foo < Syskit::Composition
  add XsensImu::Task, :as => 'imu'
end
class Bar < Foo
  # This overload is invalid if the two tasks are unrelated
  # (i.e. if DfkiImu::Task is not a subclass of XsensImu::Task)
  add DfkiImu::Task, :as => 'imu'
end

Parameters:

  • models (Array<Model>, Model)

    the child's model. Can be a set of models to provide e.g. multiple unrelated data services, or a task context and a data service type that would be provided by a dynamic data service on the task context.

  • options (Hash) (defaults to: Hash.new)

    set of options for the new child, as well as any option that is valid to be passed to Roby::Task#depends_on

Options Hash (options):

  • :as (String)

    this is actually mandatory, but is keps as an option for backward compatibility reasons. It is the name of the child. The new child can be accessed by calling #childname_child on the new model (see example below)

Returns:



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/syskit/models/composition.rb', line 287

def add(models, options = Hash.new)
    if models.respond_to?(:to_instance_requirements)
        models = models.to_instance_requirements
    else
        models = InstanceRequirements.new(Array(models))
    end

    options, dependency_options = Kernel.filter_options options,
        :as => nil
    if !options[:as]
        raise ArgumentError, "you must provide an explicit name with the :as option"
    end

    add_child(options[:as], models, dependency_options)
end

#add_child(name, child_models, dependency_options) ⇒ Object

Internal helper to add a child to the composition



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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/syskit/models/composition.rb', line 155

def add_child(name, child_models, dependency_options)
    name = name.to_str
    dependency_options = Roby::TaskStructure::Dependency.
        validate_options(dependency_options)

    # We do NOT check for an already existing definition. The reason
    # is that specialization (among other) will add a default child,
    # that may be overriden by the composition's owner. Either to
    # set arguments, or to have a specialization over an aspect of a
    # data service use a more specific task model in the specialized
    # composition.
    #
    # Anyway, the remainder checks that the new definition is a
    # valid overloading of the previous one.
    
    child_model = find_child(name) || CompositionChild.new(self, name)
    # The user might have called e.g.
    #
    #   overload 'bla', bla_child.with_arguments(bla: 10)
    if child_models.object_id != child_model.object_id
        child_model.merge(child_models)
    end
    dependency_options = Roby::TaskStructure::Dependency.
        merge_dependency_options(child_model.dependency_options, dependency_options)
    child_model.dependency_options.clear
    child_model.dependency_options.merge!(dependency_options)

    Models.debug do
        Models.debug "added child #{name} to #{short_name}"
        Models.debug "  with models #{child_model.model}"
        if parent_model = superclass.find_child(name)
            Models.debug "  updated from #{parent_model.model}"
        end
        if !child_model.port_mappings.empty?
            Models.debug "  port mappings"
            Models.log_nest(4) do
                child_model.port_mappings.each_value do |mappings|
                    Models.log_pp(:debug, mappings)
                end
            end
        end
        break
    end
    children[name] = child_model

    @exported_outputs = exported_outputs.map_value do |_, port|
        if port.component_model.child_name == name
            child_model.find_port(port.name)
        else port
        end
    end
    @exported_inputs = exported_inputs.map_value do |_, port|
        if port.component_model.child_name == name
            child_model.find_port(port.name)
        else port
        end
    end

    child_model
end

#add_main(models, options = Hash.new) ⇒ Object

Adds the given child, and marks it as the task that provides the main composition's functionality.

What is means in practice is that the composition will terminate successfully when this child terminates successfully



334
335
336
337
338
339
# File 'lib/syskit/models/composition.rb', line 334

def add_main(models, options = Hash.new)
    if main_task
        raise ArgumentError, "this composition already has a main task child"
    end
    @main_task = add(models, options)
end

#add_main_task(models, options = Hash.new) ⇒ Object

DEPRECATED. Use #add_main instead.



325
326
327
# File 'lib/syskit/models/composition.rb', line 325

def add_main_task(models, options = Hash.new) # :nodoc:
    add_main(models, options)
end

#add_optional(models, options = Hash.new) ⇒ Object

Add a child that may not be provided

(see #add)



306
307
308
309
310
# File 'lib/syskit/models/composition.rb', line 306

def add_optional(models, options = Hash.new)
    child = add(models, options)
    child.optional
    child
end

#add_specialization_constraint(explicit = nil) {|spec0, spec1| ... } ⇒ void

This method returns an undefined value.

Registers a block that will be able to tell the system that two specializations are not compatible (i.e. should never be applied at the same time).

Parameters:

  • a (#[])

    proc object given explicitly if the block form is not desired

Yield Parameters:

Yield Returns:

  • (Boolean)

    true if the two specializations are compatible, and false otherwise



649
650
651
# File 'lib/syskit/models/composition.rb', line 649

def add_specialization_constraint(explicit = nil, &block)
    specializations.add_specialization_constraint(explicit, &block)
end

#all_childArray<CompositionChild>

The union, along the class hierarchy, of all the values stored in child

Returns:



26
# File 'lib/syskit/models/composition.rb', line 26

inherited_attribute(:child, :children, :map => true) { Hash.new }

#all_child_constraintArray<Object>

The union, along the class hierarchy, of all the values stored in child_constraint

Returns:



28
# File 'lib/syskit/models/composition.rb', line 28

inherited_attribute(:child_constraint, :child_constraints, :map => true) { Hash.new { |h, k| h[k] = Array.new } }

#all_configurationArray<Hash<String>]

The union, along the class hierarchy, of all the values stored in configuration

Returns:

  • (Array<Hash<String>])

    Array<Hash<String>]



1206
# File 'lib/syskit/models/composition.rb', line 1206

inherited_attribute(:configuration, :configurations, :map => true)  { Hash.new }

#all_explicit_connectionArray<Object>

The union, along the class hierarchy, of all the values stored in explicit_connection

Returns:



78
# File 'lib/syskit/models/composition.rb', line 78

inherited_attribute(:explicit_connection, :explicit_connections) { Hash.new { |h, k| h[k] = Hash.new } }

#all_exported_inputArray<Port>

The union, along the class hierarchy, of all the values stored in exported_input

Returns:



1197
# File 'lib/syskit/models/composition.rb', line 1197

inherited_attribute(:exported_input, :exported_inputs, :map => true)  { Hash.new }

#all_exported_outputArray<Port>

The union, along the class hierarchy, of all the values stored in exported_output

Returns:



1182
# File 'lib/syskit/models/composition.rb', line 1182

inherited_attribute(:exported_output, :exported_outputs, :map => true)  { Hash.new }

#applied_specializationsObject

:attr: specialized_children

The set of specializations that are applied from the root of the model graph up to this model



100
# File 'lib/syskit/models/composition.rb', line 100

attribute(:applied_specializations) { Set.new }

#childHash<String,CompositionChild>

The composition children

Returns:



26
# File 'lib/syskit/models/composition.rb', line 26

inherited_attribute(:child, :children, :map => true) { Hash.new }

#child_constraintsObject



28
# File 'lib/syskit/models/composition.rb', line 28

inherited_attribute(:child_constraint, :child_constraints, :map => true) { Hash.new { |h, k| h[k] = Array.new } }

#child_port?(port) ⇒ Boolean

Tests whether the given port is a port of one of this composition's children

Returns:

  • (Boolean)


408
409
410
411
412
# File 'lib/syskit/models/composition.rb', line 408

def child_port?(port)
    port_component_model = port.to_component_port.component_model
    port_component_model.respond_to?(:composition_model) &&
            port_component_model.composition_model == self
end

#childrenHash<String,CompositionChild>

The composition children

Returns:



26
# File 'lib/syskit/models/composition.rb', line 26

inherited_attribute(:child, :children, :map => true) { Hash.new }

#children_namesObject



666
667
668
# File 'lib/syskit/models/composition.rb', line 666

def children_names
    each_child.map { |name, _| name }
end

#clear_modelObject



30
31
32
33
34
35
36
37
38
39
# File 'lib/syskit/models/composition.rb', line 30

def clear_model
    super
    child_constraints.clear
    children.clear
    configurations.clear
    exported_inputs.clear
    exported_outputs.clear
    @specializations = SpecializationManager.new(self)
    @main_task = nil
end

#compute_child_dependency_options(child_name, child_task) ⇒ Hash

Computes the options for depends_on needed to add child_task as the child_name's composition child

Parameters:

Returns:

  • (Hash)


890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
# File 'lib/syskit/models/composition.rb', line 890

def compute_child_dependency_options(child_name, child_task)
    child_m = find_child(child_name)
    dependent_models    = child_m.each_required_model.to_a
    dependent_arguments = dependent_models.inject(Hash.new) do |result, m|
        result.merge(m.meaningful_arguments(child_task.arguments))
    end
    if child_task.has_argument?(:conf)
        dependent_arguments[:conf] = child_task.arguments[:conf]
    end

    dependency_options = Roby::TaskStructure::Dependency.
        validate_options(child_m.dependency_options)
    default_options = Roby::TaskStructure::Dependency.
        validate_options(:model => [dependent_models, dependent_arguments], :roles => [child_name].to_set)
    dependency_options = Roby::TaskStructure::Dependency.merge_dependency_options(
        dependency_options, default_options)
    if !dependency_options[:success]
        dependency_options = { :success => [], :failure => [:stop] }.
            merge(dependency_options)
    end
    dependency_options
end

#conf(name, mappings = Hash.new) ⇒ Object

Declares a composition configuration

Composition configurations are named selections of configurations.

For instance, if

conf 'narrow',
    'monitoring' => ['default', 'narrow_window'],
    'sonar' => ['default', 'narrow_window']

is declared, and the composition is instanciated with

Cmp::SonarMonitoring.use_conf('narrow')

Then the composition children called 'monitoring' and 'sonar' will be both instanciated with ['default', 'narrow_window']



1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
# File 'lib/syskit/models/composition.rb', line 1224

def conf(name, mappings = Hash.new)
    mappings = mappings.map_key do |child, conf|
        if child.respond_to?(:to_str)
            Roby.warn_deprecated "providing the child as string in #conf is deprecated, use the _child accessors instead (here #{child}_child => [#{conf.join(", ")}])"
            child
        else
            child.child_name
        end
    end
    configurations[name] = mappings
end

#configurationHash<String,Hash<String,String>>

Configurations defined on this composition model

Returns:

  • (Hash<String,Hash<String,String>>)

    the mapping from a composition configuration name to the corresponding configurations that should be applied to its children

See Also:

  • Syskit::Models::Composition.{{#conf}


1206
# File 'lib/syskit/models/composition.rb', line 1206

inherited_attribute(:configuration, :configurations, :map => true)  { Hash.new }

#configurationsHash<String,Hash<String,String>>

Configurations defined on this composition model

Returns:

  • (Hash<String,Hash<String,String>>)

    the mapping from a composition configuration name to the corresponding configurations that should be applied to its children

See Also:

  • Syskit::Models::Composition.{{#conf}


1206
# File 'lib/syskit/models/composition.rb', line 1206

inherited_attribute(:configuration, :configurations, :map => true)  { Hash.new }

#connect(mappings) ⇒ Object

Explicitly create the given connections between children of this composition.

Example:

composition 'Test' do
    source = add 'Source'
    sink   = add 'Sink'
    connect source.output => sink.input, :type => :buffer
end

Explicit connections always have precedence on automatic connections. See #autoconnect for automatic connection handling



633
634
635
636
637
638
639
640
641
642
643
644
645
646
# File 'lib/syskit/models/composition.rb', line 633

def connect(mappings)
    options = Hash.new
    mappings.delete_if do |a, b|
        if a.respond_to?(:to_str)
            options[a] = b
        end
    end
    if !options.empty?
        options = Kernel.validate_options options, Orocos::Port::CONNECTION_POLICY_OPTIONS
    end
    mappings.each do |out_p, in_p|
        out_p.connect_to in_p
    end
end

#connectionsObject

Returns the set of connections that should be created during the instanciation of this composition model.

The returned value is a mapping:

[source_name, sink_name] =>
    {
        [source_port_name0, sink_port_name1] => connection_policy,
        [source_port_name0, sink_port_name1] => connection_policy
    }


395
396
397
398
399
400
401
402
403
404
# File 'lib/syskit/models/composition.rb', line 395

def connections
    result = Hash.new { |h, k| h[k] = Hash.new }

    # In the following, 'key' is [child_source, child_dest] and
    # 'mappings' is [port_source, port_sink] => connection_policy
    each_explicit_connection do |key, mappings|
        result[key].merge!(mappings)
    end
    result
end

#constraints_for(child_name) ⇒ Object

Returns the set of constraints that exist for the given child. I.e. the set of types that, at instanciation time, the chosen child must provide.

See #constrain



658
659
660
661
662
663
664
# File 'lib/syskit/models/composition.rb', line 658

def constraints_for(child_name)
    result = Set.new
    each_child_constraint(child_name, false) do |constraint_set|
        result |= constraint_set.to_set
    end
    result
end

#create_dynamic_instantiation_context(name, dynamic_service, **options) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

(see Component#create_dynamic_instantiation_context)



617
618
619
# File 'lib/syskit/models/composition.rb', line 617

def create_dynamic_instantiation_context(name, dynamic_service, **options)
    DynamicServiceInstantiationContext.new(self, name, dynamic_service, **options)
end

#create_private_specializationObject

Called by Syskit::Models::Component#specialize to create the composition model that will be used for a private specialization



104
105
106
# File 'lib/syskit/models/composition.rb', line 104

def create_private_specialization
    new_submodel(:register_specializations => false)
end

#dependency_injection_namesObject

The list of names that will be used by this model as keys in a DependencyInjection object,

For compositions, this is the list of children names



674
675
676
# File 'lib/syskit/models/composition.rb', line 674

def dependency_injection_names
    children_names
end

#each_child(child_name, uniq = true) {|element| ... } ⇒ Object #each_child(nil, uniq = true) {|child_name, element| ... } ⇒ Object

Overloads:

  • #each_child(child_name, uniq = true) {|element| ... } ⇒ Object

    Enumerates all objects registered in child under the given key

    Yields:

    • (element)

    Yield Parameters:

  • #each_child(nil, uniq = true) {|child_name, element| ... } ⇒ Object

    Enumerates all objects registered in child

    Yields:

    • (child_name, element)

    Yield Parameters:



26
# File 'lib/syskit/models/composition.rb', line 26

inherited_attribute(:child, :children, :map => true) { Hash.new }

#each_child_constraint(key, uniq = true) {|element| ... } ⇒ Object #each_child_constraint(nil, uniq = true) {|key, element| ... } ⇒ Object

Overloads:

  • #each_child_constraint(key, uniq = true) {|element| ... } ⇒ Object

    Enumerates all objects registered in child_constraint under the given key

    Yields:

    • (element)

    Yield Parameters:

  • #each_child_constraint(nil, uniq = true) {|key, element| ... } ⇒ Object

    Enumerates all objects registered in child_constraint

    Yields:

    • (key, element)

    Yield Parameters:



28
# File 'lib/syskit/models/composition.rb', line 28

inherited_attribute(:child_constraint, :child_constraints, :map => true) { Hash.new { |h, k| h[k] = Array.new } }

#each_configuration(conf_name, uniq = true) {|element| ... } ⇒ Object #each_configuration(nil, uniq = true) {|conf_name, element| ... } ⇒ Object

Overloads:

  • #each_configuration(conf_name, uniq = true) {|element| ... } ⇒ Object

    Enumerates all objects registered in configuration under the given key

    Yields:

    • (element)

    Yield Parameters:

    • Hash<String] (Hash<String] element)

      element

  • #each_configuration(nil, uniq = true) {|conf_name, element| ... } ⇒ Object

    Enumerates all objects registered in configuration

    Yields:

    • (conf_name, element)

    Yield Parameters:

    • conf_name (String)
    • Hash<String] (Hash<String] element)

      element



1206
# File 'lib/syskit/models/composition.rb', line 1206

inherited_attribute(:configuration, :configurations, :map => true)  { Hash.new }

#each_explicit_connection {|element| ... } ⇒ Object

Enumerates all objects registered in explicit_connection

Yields:

  • (element)

Yield Parameters:

Returns:



78
# File 'lib/syskit/models/composition.rb', line 78

inherited_attribute(:explicit_connection, :explicit_connections) { Hash.new { |h, k| h[k] = Hash.new } }

#each_exported_input(exported_port_name, uniq = true) {|element| ... } ⇒ Object #each_exported_input(nil, uniq = true) {|exported_port_name, element| ... } ⇒ Object

Overloads:

  • #each_exported_input(exported_port_name, uniq = true) {|element| ... } ⇒ Object

    Enumerates all objects registered in exported_input under the given key

    Yields:

    • (element)

    Yield Parameters:

  • #each_exported_input(nil, uniq = true) {|exported_port_name, element| ... } ⇒ Object

    Enumerates all objects registered in exported_input

    Yields:

    • (exported_port_name, element)

    Yield Parameters:

    • exported_port_name (String)
    • element (Port)


1197
# File 'lib/syskit/models/composition.rb', line 1197

inherited_attribute(:exported_input, :exported_inputs, :map => true)  { Hash.new }

#each_exported_output(exported_port_name, uniq = true) {|element| ... } ⇒ Object #each_exported_output(nil, uniq = true) {|exported_port_name, element| ... } ⇒ Object

Overloads:

  • #each_exported_output(exported_port_name, uniq = true) {|element| ... } ⇒ Object

    Enumerates all objects registered in exported_output under the given key

    Yields:

    • (element)

    Yield Parameters:

  • #each_exported_output(nil, uniq = true) {|exported_port_name, element| ... } ⇒ Object

    Enumerates all objects registered in exported_output

    Yields:

    • (exported_port_name, element)

    Yield Parameters:

    • exported_port_name (String)
    • element (Port)


1182
# File 'lib/syskit/models/composition.rb', line 1182

inherited_attribute(:exported_output, :exported_outputs, :map => true)  { Hash.new }

#each_input_portObject

Enumerates this component's input ports



495
496
497
498
499
500
# File 'lib/syskit/models/composition.rb', line 495

def each_input_port
    return enum_for(:each_input_port) if !block_given?
    each_exported_input do |name, p|
        yield(find_input_port(name))
    end
end

#each_output_portObject

Enumerates this component's output ports



487
488
489
490
491
492
# File 'lib/syskit/models/composition.rb', line 487

def each_output_port
    return enum_for(:each_output_port) if !block_given?
    each_exported_output do |name, p|
        yield(find_output_port(name))
    end
end

#explicit_connectionHash{(String,String)=>{(String,String)=>Hash}}

The set of connections specified by the user for this composition

of connections defined on this composition model. The first level is a mapping from the (output child name, input child name) to a set of connections. The set of connections is specified as a mapping from the output port name (on the output child) and the input port name (on the input child) to the desired connection policy.

Empty connection policies means “autodetect policy”

Returns:

  • (Hash{(String,String)=>{(String,String)=>Hash}})

    the set



78
# File 'lib/syskit/models/composition.rb', line 78

inherited_attribute(:explicit_connection, :explicit_connections) { Hash.new { |h, k| h[k] = Hash.new } }

#export(port, as: port.name) ⇒ Object

Export the given port to the boundary of the composition (it becomes a composition port). By default, the composition port has the same name than the exported port. This name can be overriden by the :as option

For example, if one does:

composition 'Test' do
   source = add 'Source'
   export source.output
   export source.output, :as => 'output2'
end

Then the resulting composition gets 'output' and 'output2' output ports that can further be used in other connections (or autoconnections):

composition 'Global' do
   test = add 'Test'
   c = add 'Component'
   connect test.output2 => c.input
end


437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
# File 'lib/syskit/models/composition.rb', line 437

def export(port, as: port.name)
    name = as.to_str
    if existing = (self.find_exported_input(name) || self.find_exported_output(name))
        if port.to_component_port != existing
            raise ArgumentError, "#{port} is already exported as #{name} on #{short_name}, cannot override with #{port}."
        end
        return
    end

    if !child_port?(port)
        raise ArgumentError, "#{port} is not a port of one of #{self}'s children"
    end

    case port
    when InputPort
        exported_inputs[name] = port.to_component_port
    when OutputPort
        exported_outputs[name] = port.to_component_port
    else
        raise TypeError, "invalid attempt to export port #{port} of type #{port.class}"
    end
    find_port(name)
end

#exported_inputHash<String,Port>

Inputs exported from components in this composition to this composition's interface

Returns:

  • (Hash<String,Port>)


1197
# File 'lib/syskit/models/composition.rb', line 1197

inherited_attribute(:exported_input, :exported_inputs, :map => true)  { Hash.new }

#exported_inputsHash<String,Port>

Inputs exported from components in this composition to this composition's interface

Returns:

  • (Hash<String,Port>)


1197
# File 'lib/syskit/models/composition.rb', line 1197

inherited_attribute(:exported_input, :exported_inputs, :map => true)  { Hash.new }

#exported_outputHash<String,Port>

Outputs exported from components in this composition to this composition's interface

Returns:

  • (Hash<String,Port>)


1182
# File 'lib/syskit/models/composition.rb', line 1182

inherited_attribute(:exported_output, :exported_outputs, :map => true)  { Hash.new }

#exported_outputsHash<String,Port>

Outputs exported from components in this composition to this composition's interface

Returns:

  • (Hash<String,Port>)


1182
# File 'lib/syskit/models/composition.rb', line 1182

inherited_attribute(:exported_output, :exported_outputs, :map => true)  { Hash.new }

#exported_port?(port) ⇒ Boolean

Returns true if port_model, which has to be a child's port, is exported in this composition

Examples:


class C < Syskit::Composition
  add srv, :as => 'srv'
  export srv.output_port
end

C.exported_port?(C.srv_child.output_port) => true

Returns:

  • (Boolean)

See Also:



476
477
478
479
480
481
482
483
484
# File 'lib/syskit/models/composition.rb', line 476

def exported_port?(port)
    each_exported_output do |name, p|
        return true if p == port
    end
    each_exported_input do |name, p|
        return true if p == port
    end
    false
end

#extract_grandchild_selections_by_child_name(child_name, selections) ⇒ Object

Extracts the selections for grandchildren out of a selection for this

It matches the child_name.granchild_name pattern and returns a hash with the matching selections

Parameters:

  • child_name (String)

    the child name

  • selections (Hash)

    the selection hash



873
874
875
876
877
878
879
880
881
882
# File 'lib/syskit/models/composition.rb', line 873

def extract_grandchild_selections_by_child_name(child_name, selections)
    child_user_selection = Hash.new
    match = /^#{child_name}\.(.*)$/
    selections.each do |name, sel|
        if name =~ match
            child_user_selection[$1] = sel
        end
    end
    child_user_selection
end

#find_applicable_specialization_from_selection(explicit_selections, selections, options = Hash.new) ⇒ Object



829
830
831
832
833
834
835
# File 'lib/syskit/models/composition.rb', line 829

def find_applicable_specialization_from_selection(explicit_selections, selections, options = Hash.new)
    specialized_model = specializations.matching_specialized_model(explicit_selections, options)
    if specialized_model != self
        return specialized_model
    end
    return specializations.matching_specialized_model(selections, options)
end

#find_child(child_name) ⇒ CompositionChild?

Looks for objects registered in child under the given key, and returns the first one in the ancestor chain (i.e. the one tha thas been registered in the most specialized class)

Returns:

  • (CompositionChild, nil)

    the found object, or nil if none is registered under that key



26
# File 'lib/syskit/models/composition.rb', line 26

inherited_attribute(:child, :children, :map => true) { Hash.new }

#find_child_constraint(key) ⇒ Object?

Looks for objects registered in child_constraint under the given key, and returns the first one in the ancestor chain (i.e. the one tha thas been registered in the most specialized class)

Returns:

  • (Object, nil)

    the found object, or nil if none is registered under that key



28
# File 'lib/syskit/models/composition.rb', line 28

inherited_attribute(:child_constraint, :child_constraints, :map => true) { Hash.new { |h, k| h[k] = Array.new } }

#find_child_model_and_task(child_name, context) ⇒ Object



678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
# File 'lib/syskit/models/composition.rb', line 678

def find_child_model_and_task(child_name, context)
    Models.debug do
        Models.debug "selecting #{child_name}:"
        Models.log_nest(2) do
            Models.debug "on the basis of"
            Models.log_nest(2) do
                Models.log_pp(:debug, context)
            end
        end
        break
    end
    child_requirements = find_child(child_name)
    selected_child, used_keys =
        context.instance_selection_for(child_name, child_requirements)
    Models.debug do
        Models.debug "selected"
        Models.log_nest(2) do
            Models.log_pp(:debug, selected_child)
        end
        break
    end
    explicit = context.has_selection_for?(child_name)
    return selected_child, explicit, used_keys
end

#find_children_models_and_tasks(context) ⇒ (Hash<String,InstanceSelection>,Hash<String,InstanceSelection>,Hash<String,Set>)

Given a dependency injection context, it computes the models and task instances for each of the composition's children

Returns:

  • ((Hash<String,InstanceSelection>,Hash<String,InstanceSelection>,Hash<String,Set>))

    the resolved selections. The first hash is the set of explicitly selected children (i.e. selected by name) and the second is all the selections. The last returned value is the set keys in context that have been used to perform the resolution



711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
# File 'lib/syskit/models/composition.rb', line 711

def find_children_models_and_tasks(context)
    explicit = Hash.new
    result   = Hash.new
    used_keys = Hash.new
    each_child do |child_name, child_requirements|
        result[child_name], child_is_explicit, used_keys[child_name] =
            find_child_model_and_task(child_name, context)

        if child_is_explicit
            explicit[child_name] = result[child_name]
        end
    end

    return explicit, result, used_keys
end

#find_configuration(conf_name) ⇒ Hash<String,nil] the found object, or nil if none is registered under that key

Looks for objects registered in configuration under the given key, and returns the first one in the ancestor chain (i.e. the one tha thas been registered in the most specialized class)

Returns:

  • (Hash<String,nil] the found object, or nil if none is registered under that key)

    Hash<String,nil] the found object, or nil if none is registered under that key



1206
# File 'lib/syskit/models/composition.rb', line 1206

inherited_attribute(:configuration, :configurations, :map => true)  { Hash.new }

#find_exported_input(exported_port_name) ⇒ Port?

Looks for objects registered in exported_input under the given key, and returns the first one in the ancestor chain (i.e. the one tha thas been registered in the most specialized class)

Returns:

  • (Port, nil)

    the found object, or nil if none is registered under that key



1197
# File 'lib/syskit/models/composition.rb', line 1197

inherited_attribute(:exported_input, :exported_inputs, :map => true)  { Hash.new }

#find_exported_output(exported_port_name) ⇒ Port?

Looks for objects registered in exported_output under the given key, and returns the first one in the ancestor chain (i.e. the one tha thas been registered in the most specialized class)

Returns:

  • (Port, nil)

    the found object, or nil if none is registered under that key



1182
# File 'lib/syskit/models/composition.rb', line 1182

inherited_attribute(:exported_output, :exported_outputs, :map => true)  { Hash.new }

#find_input_port(name) ⇒ Object

Returns the composition's input port named 'name'

See #port, and #export to create ports on a composition



515
516
517
518
519
520
# File 'lib/syskit/models/composition.rb', line 515

def find_input_port(name)
    name = name.to_str
    if p = find_exported_input(name.to_str)
        return InputPort.new(self, p.orogen_model, name)
    end
end

#find_output_port(name) ⇒ Object

Returns the composition's output port named 'name'

See #port, and #export to create ports on a composition



505
506
507
508
509
510
# File 'lib/syskit/models/composition.rb', line 505

def find_output_port(name)
    name = name.to_str
    if p = find_exported_output(name.to_str)
        return OutputPort.new(self, p.orogen_model, name)
    end
end

#find_through_method_missing(m, args) ⇒ Object



1150
1151
1152
1153
# File 'lib/syskit/models/composition.rb', line 1150

def find_through_method_missing(m, args)
    MetaRuby::DSLs.find_through_method_missing(
        self, m, args, '_child' => :find_child) || super
end

#fullfills?(models) ⇒ Boolean

Reimplemented from Roby::Task to take into account the multiple inheritance mechanisms that is the composition specializations

Returns:

  • (Boolean)


1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
# File 'lib/syskit/models/composition.rb', line 1268

def fullfills?(models)
    models = [models] if !models.respond_to?(:map)
    models = models.map do |other_model|
        if other_model.respond_to?(:applied_specializations)
            if !(other_model.applied_specializations - applied_specializations).empty?
                return false
            end
            other_model.root_model
        else
            other_model
        end
    end
    return super(models)
end

#has_child?(child_name) ⇒ Boolean

Returns true if an object is registered in child anywhere in the class hierarchy

Returns:

  • (Boolean)


26
# File 'lib/syskit/models/composition.rb', line 26

inherited_attribute(:child, :children, :map => true) { Hash.new }

#has_child_constraint?(key) ⇒ Boolean

Returns true if an object is registered in child_constraint anywhere in the class hierarchy

Returns:

  • (Boolean)


28
# File 'lib/syskit/models/composition.rb', line 28

inherited_attribute(:child_constraint, :child_constraints, :map => true) { Hash.new { |h, k| h[k] = Array.new } }

#has_configuration?(conf_name) ⇒ Boolean

Returns true if an object is registered in configuration anywhere in the class hierarchy

Returns:

  • (Boolean)


1206
# File 'lib/syskit/models/composition.rb', line 1206

inherited_attribute(:configuration, :configurations, :map => true)  { Hash.new }

#has_dynamic_input_port?(name, type = nil) ⇒ Boolean

Returns true if name is a valid dynamic input port.

On a composition, it always returns false. This method is defined for consistency for the other kinds of Component objects.

Returns:

  • (Boolean)


526
# File 'lib/syskit/models/composition.rb', line 526

def has_dynamic_input_port?(name, type = nil); false end

#has_dynamic_output_port?(name, type = nil) ⇒ Boolean

Returns true if name is a valid dynamic output port.

On a composition, it always returns false. This method is defined for consistency for the other kinds of Component objects.

Returns:

  • (Boolean)


532
# File 'lib/syskit/models/composition.rb', line 532

def has_dynamic_output_port?(name, type = nil); false end

#has_exported_input?(exported_port_name) ⇒ Boolean

Returns true if an object is registered in exported_input anywhere in the class hierarchy

Returns:

  • (Boolean)


1197
# File 'lib/syskit/models/composition.rb', line 1197

inherited_attribute(:exported_input, :exported_inputs, :map => true)  { Hash.new }

#has_exported_output?(exported_port_name) ⇒ Boolean

Returns true if an object is registered in exported_output anywhere in the class hierarchy

Returns:

  • (Boolean)


1182
# File 'lib/syskit/models/composition.rb', line 1182

inherited_attribute(:exported_output, :exported_outputs, :map => true)  { Hash.new }

#has_through_method_missing?(m) ⇒ Boolean

Returns:

  • (Boolean)


1145
1146
1147
1148
# File 'lib/syskit/models/composition.rb', line 1145

def has_through_method_missing?(m)
    MetaRuby::DSLs.has_through_method_missing?(
        self, m, '_child' => :find_child) || super
end

#inherited(submodel) ⇒ Object

Overloaded to set the model documentation



1121
1122
1123
1124
# File 'lib/syskit/models/composition.rb', line 1121

def inherited(submodel)
    super
    submodel.doc MetaRuby::DSLs.parse_documentation_block(/.*/, /^inherited/)
end

#instanciate(plan, context = DependencyInjectionContext.new, task_arguments: Hash.new, specialize: true, specialization_hints: Array.new) ⇒ Object

Creates the required task and children for this composition model.

It selects the relevant specialization and instantiates it instead of self when relevant.

Parameters:

  • the (Roby::Plan)

    plan in which the composition should be instantiated

  • context (DependencyInjectionContext) (defaults to: DependencyInjectionContext.new)

    the dependency injection used to select the actual models for the children (and therefore the specializations as well). The last element in this DIContext stack is interpreted as DI setup only for the composition (not for the instantiation of its children).

  • arguments (Hash)

    a customizable set of options



930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
# File 'lib/syskit/models/composition.rb', line 930

def instanciate(plan, context = DependencyInjectionContext.new,
                task_arguments: Hash.new,
                specialize: true,
                specialization_hints: Array.new)

    Models.debug do
        Models.debug "instanciating #{short_name} with"
        Models.log_nest(2)
        Models.log_pp(:debug, context)
        break
    end

    # Find what we should use for our children. +explicit_selection+
    # is the set of children for which a selection existed and
    # +selected_models+ all the models we should use
    explicit_selections, selected_models, used_keys =
        find_children_models_and_tasks(context.current_state)

    if specialize
        specialized_model = find_applicable_specialization_from_selection(
            explicit_selections,
            selected_models,
            specialization_hints: specialization_hints)
        if specialized_model != self
            return specialized_model.instanciate(plan, context,
                                                 task_arguments: task_arguments,
                                                 specialize: true,
                                                 specialization_hints: specialization_hints)
        end
    end

    # First of all, add the task for +self+
    plan.add(self_task = new(task_arguments))
    conf = if self_task.has_argument?(:conf)
               self_task.conf(self_task.arguments[:conf])
           else Hash.new
           end

    # This is the part of the context that is directly associated
    # with the composition. We use it later to extract by-name
    # selections for the children of the form
    # child_name.child_of_child_name
    composition_use_flags = context.top

    # Finally, instanciate the missing tasks and add them to our
    # children
    children_tasks = Hash.new
    remaining_children_models = selected_models.dup
    while !remaining_children_models.empty?
        current_size = remaining_children_models.size
        remaining_children_models.delete_if do |child_name, selected_child|
            selected_child = selected_child.dup

            resolved_selected_child = selected_child
            if selected_child.selected.fullfills?(Syskit::Composition)
                resolved_selected_child = selected_child.dup
                if !try_resolve_child_references_in_use_flags(self_task, resolved_selected_child.selected)
                    next
                end

                child_user_selection = extract_grandchild_selections_by_child_name(child_name, composition_use_flags.added_info.explicit)
                resolved_selected_child.selected.use(child_user_selection)
            end

            child_task = context.save do
                context.push_mask(used_keys[child_name])
                instanciate_child(plan, context, self_task, child_name, resolved_selected_child)
            end
            child_task = child_task.to_task

            if child_conf = conf[child_name]
                child_task.arguments[:conf] ||= child_conf
            end

            children_tasks[child_name] = child_task

            dependency_options = compute_child_dependency_options(child_name, child_task)
            Models.info do
                Models.info "adding dependency #{self_task}"
                Models.info "    => #{child_task}"
                Models.info "   options; #{dependency_options}"
                break
            end

            self_task.depends_on(child_task, dependency_options)
            self_task.child_selection[child_name] = selected_child
            if (main = main_task) && (main.child_name == child_name)
                child_task.each_event do |ev|
                    if !ev.terminal? && ev.symbol != :start && self_task.has_event?(ev.symbol)
                        child_task.event(ev.symbol).forward_to self_task.event(ev.symbol)
                    end
                end
                child_task.success_event.forward_to self_task.success_event
            end
            true # it has been processed, delete from remaining_children_models
        end
        if remaining_children_models.size == current_size
            raise InternalError, "cannot resolve children #{remaining_children_models.map(&:first).sort.join(", ")}"
        end
    end

    instanciate_connections(self_task, selected_models, children_tasks)
    self_task
ensure
    Models.debug do
        Models.log_nest -2
        break
    end
end

#instanciate_child(plan, context, self_task, child_name, selected_child) ⇒ Object

Instanciates a task for the required child



766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
# File 'lib/syskit/models/composition.rb', line 766

def instanciate_child(plan, context, self_task, child_name, selected_child) # :nodoc:
    Models.debug do
        Models.debug "instanciating child #{child_name}"
        Models.log_nest 2
        break
    end

    child_arguments = selected_child.selected.arguments
    child_arguments.each_key do |key|
 value = child_arguments[key]
        if value.respond_to?(:resolve_child)
            child_arguments[key] = value.resolve_child(self)
        end
    end

    selected_child.instanciate(plan, context, :task_arguments => child_arguments)
ensure
    Models.debug do
        Models.log_nest -2
        break
    end
end

#instanciate_connections(self_task, selected_children, children_tasks) ⇒ Object



789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
# File 'lib/syskit/models/composition.rb', line 789

def instanciate_connections(self_task, selected_children, children_tasks)
    # The set of connections we must create on our children. This is
    # self.connections on which we will apply port mappings for the
    # instanciated children
    each_explicit_connection do |(out_name, in_name), conn|
        if (out_task = children_tasks[out_name]) && (in_task = children_tasks[in_name])
            child_out    = selected_children[out_name]
            child_in     = selected_children[in_name]
            mappings_out = child_out.port_mappings
            mappings_in  = child_in.port_mappings

            mapped = Hash.new
            conn.each do |(port_out, port_in), policy|
                mapped_port_out = mappings_out[port_out] || port_out
                mapped_port_in  = mappings_in[port_in] || port_in
                mapped[[mapped_port_out, mapped_port_in]] = policy
            end
                
            out_task.connect_ports(in_task, mapped)
        end
    end

    each_exported_input do |export_name, port|
        child_name = port.component_model.child_name
        if child_task = children_tasks[child_name]
            child = selected_children[child_name]
            self_task.forward_input_ports(
                child_task, [export_name, child.port_mappings[port.name]] => Hash.new)
        end
    end
    each_exported_output do |export_name, port|
        child_name = port.component_model.child_name
        if child_task = children_tasks[child_name]
            child = selected_children[child_name]
            child_task.forward_output_ports(
                self_task, [child.port_mappings[port.name], export_name] => Hash.new)
        end
    end
end

#is_specialization?Boolean

Returns true if this composition model is a specialized version of its superclass, and false otherwise

Returns:

  • (Boolean)


343
# File 'lib/syskit/models/composition.rb', line 343

def is_specialization?; false end

#main_taskObject

Returns this composition's main task

The main task is the task that performs the composition's main goal (if there is one). The composition will terminate successfully whenever the main task finishes successfully.



317
318
319
320
321
322
# File 'lib/syskit/models/composition.rb', line 317

def main_task
    if @main_task then @main_task
    elsif superclass.respond_to?(:main_task)
        superclass.main_task
    end
end

#merge(other_model) ⇒ Object

Merge two models, making sure that specializations are properly applied on the result



1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
# File 'lib/syskit/models/composition.rb', line 1238

def merge(other_model)
    needed_specializations = self.applied_specializations
    if other_model.respond_to?(:root_model)
        needed_specializations |= other_model.applied_specializations
        other_model = other_model.root_model
    end

    if needed_specializations.empty?
        super(other_model)
    else
        base_model = root_model.merge(other_model)
        # If base_model is a placeholder model, we apply the
        # specialization on the proper composition model and then
        # re-proxy it
        services = []
        if base_model.placeholder?
            services   = base_model.proxied_data_service_models
            base_model = base_model.proxied_component_model
        end

        composite_spec = CompositionSpecialization.
            merge(*needed_specializations)
        result = base_model.specializations.specialized_model(
            composite_spec, needed_specializations)
        Placeholder.for(services.to_a, component_model: result)
    end
end

#narrow(context, options = Hash.new) ⇒ Model<Composition>

Returns the set of specializations that match the given dependency injection context

Parameters:

  • context (DependencyInjection)

    the dependency injection object that is used to determine the selected model

Returns:



733
734
735
736
737
# File 'lib/syskit/models/composition.rb', line 733

def narrow(context, options = Hash.new)
    explicit_selections, selected_models, _ =
        find_children_models_and_tasks(context)
    find_applicable_specialization_from_selection(explicit_selections, selected_models, options)
end

#new_specialized_submodel(options = Hash.new, &block) ⇒ Object

Create a new submodel of this composition model that will be used to represent a specialization



1114
1115
1116
1117
1118
# File 'lib/syskit/models/composition.rb', line 1114

def new_specialized_submodel(options = Hash.new, &block)
    submodel = new_submodel(options.merge(:register_specializations => false), &block)
    submodel.extend Models::CompositionSpecialization::Extension
    submodel
end

#overload(child, model, options = Hash.new) ⇒ Object

Overloads an existing child with a new model and/or options

This is 100% equivalent to

add model, (:as => name).merge(options)

The only (important) difference is that it checks that name is indeed an existing child, and allows people that read the composition model to understand the intent



225
226
227
228
229
230
231
232
233
# File 'lib/syskit/models/composition.rb', line 225

def overload(child, model, options = Hash.new)
    if child.respond_to?(:child_name)
        child = child.child_name
    end
    if !find_child(child)
        raise ArgumentError, "#{child} is not an existing child of #{short_name}"
    end
    add(model, options.merge(:as => child))
end

#parent_model_of?(child_model) ⇒ Boolean

Returns true if self is a parent model of child_model

Returns:

  • (Boolean)


149
150
151
152
# File 'lib/syskit/models/composition.rb', line 149

def parent_model_of?(child_model)
    (child_model < self) ||
        specializations.values.include?(child_model)
end

#prefer_specializations(*spec) ⇒ Object

Returns this composition associated with specialization disambiguation information

For instance,

CorridorServoing.
    prefer_specializations('child' => XsensImu::Task)

(see InstanceRequirements#prefer_specializations)



761
762
763
# File 'lib/syskit/models/composition.rb', line 761

def prefer_specializations(*spec)
    InstanceRequirements.new([self]).prefer_specializations(*spec)
end

#pretty_print(pp) ⇒ Object

:nodoc:



348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
# File 'lib/syskit/models/composition.rb', line 348

def pretty_print(pp) # :nodoc:
    pp.text "#{root_model.name}:"

    specializations = specialized_children.to_a
    if !specializations.empty?
        pp.text "Specialized on:"
        pp.nest(2) do
            specializations.each do |key, selected_models|
                pp.breakable
                pp.text "#{key}: "
                pp.nest(2) do
                    pp.seplist(selected_models) do |m|
                        m.pretty_print(pp)
                    end
                end
            end
        end
    end
    
    data_services = each_data_service.to_a
    if !data_services.empty?
        pp.nest(2) do
            pp.breakable
            pp.text "Data services:"
            pp.nest(2) do
                data_services.sort_by(&:first).
                    each do |name, ds|
                        pp.breakable
                        pp.text "#{name}: #{ds.model.name}"
                    end
            end
        end
    end
end

#promote_child(child_name, child) ⇒ Object



16
17
18
19
20
# File 'lib/syskit/models/composition.rb', line 16

def promote_child(child_name, child)
    promoted = child.attach(self)
    promoted.parent_model = child
    children[child_name] = promoted
end

#promote_explicit_connection(connections) ⇒ Object

Method that maps connections from this composition's parent models to this composition's own interface

It is called as needed when calling #each_explicit_connection



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/syskit/models/composition.rb', line 45

def promote_explicit_connection(connections)
    children, mappings = *connections

    mappings_out =
        if child_out = self.children[children[0]]
            child_out.port_mappings
        else Hash.new
        end
    mappings_in =
        if child_in = self.children[children[1]]
            child_in.port_mappings
        else Hash.new
        end

    mapped = Hash.new
    mappings.each do |(port_name_out, port_name_in), options|
        port_name_out = (mappings_out[port_name_out] || port_name_out)
        port_name_in  = (mappings_in[port_name_in]   || port_name_in)
        mapped[[port_name_out, port_name_in]] = options
    end
    [children, mapped]
end

#promote_exported_input(export_name, port) ⇒ Object

Method that maps exports from this composition's parent models to this composition's own interface

It is called as needed when calling #each_exported_input



1188
1189
1190
# File 'lib/syskit/models/composition.rb', line 1188

def promote_exported_input(export_name, port)
    exported_inputs[export_name] = promote_exported_port(export_name, port)
end

#promote_exported_output(export_name, port) ⇒ Object

Method that maps exports from this composition's parent models to this composition's own interface

It is called as needed when calling #each_exported_output



1173
1174
1175
# File 'lib/syskit/models/composition.rb', line 1173

def promote_exported_output(export_name, port)
    exported_outputs[export_name] = promote_exported_port(export_name, port)
end

#promote_exported_port(export_name, port) ⇒ Object



1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
# File 'lib/syskit/models/composition.rb', line 1157

def promote_exported_port(export_name, port)
    if new_child = children[port.component_model.child_name]
        if new_port_name = new_child.port_mappings[port.name]
            find_child(port.component_model.child_name).find_port(new_port_name).dup
        else
            port
        end
    else
        port
    end
end

#root_modelObject

The root composition model in the specialization hierarchy



84
# File 'lib/syskit/models/composition.rb', line 84

def root_model; self end

#setup_submodel(submodel, register_specializations: true, **submodel_options, &block) ⇒ Object

Create a new submodel of this composition model



1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
# File 'lib/syskit/models/composition.rb', line 1127

def setup_submodel(submodel, register_specializations: true, **submodel_options, &block)
    super(submodel, **submodel_options, &block)

    if register_specializations
        specializations.each_specialization do |spec|
            next if applied_specializations.include?(spec)
            spec.specialization_blocks.each do |spec_block|
                specialized_children = spec.specialized_children.map_key do |child_name, child_model|
                    submodel.find_child(child_name)
                end
                submodel.specialize(specialized_children, &spec_block)
            end
        end
    end
    submodel.applied_specializations |= applied_specializations.to_set
    submodel
end

#specializationsSpecializationManager

Returns the object that manages all specializations defined on this composition model

Returns:

  • (SpecializationManager)

    the object that manages all specializations defined on this composition model



14
# File 'lib/syskit/models/composition.rb', line 14

attribute(:specializations) { SpecializationManager.new(self) }

#specialize(options = Hash.new, &block) ⇒ Object

Specifies a modification that should be applied on #composition_model when select children fullfill some specific models.

Options Hash (options):

  • not (Boolean)

    If it is known that a specialization is in conflict with another, the :not option can be used. For instance, in the following code, only two specialization will exist: the one in which the Control child is a SimpleController and the one in which it is a FourWheelController.

    In the example below, if the :not option had not been used, three specializations would have been added: the same two than above, and the one case where 'Control' fullfills both the SimpleController and FourWheelController data services.

    @example

    specialize 'Control', SimpleController, :not => FourWheelController do
    end
    specialize 'Control', FourWheelController, :not => SimpleController do
    end
    


109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/syskit/models/composition.rb', line 109

def specialize(options = Hash.new, &block)
    if options.respond_to?(:to_str)
        return super(options)
    elsif options.empty?
        return super()
    end

    options = options.map_key do |key, value|
        if key.respond_to?(:to_str) || key.respond_to?(:to_sym)
            Roby.warn_deprecated "calling #specialize with child names is deprecated, use _child accessors instead (i.e. #{key}_child here)", 5
            key
        elsif key.respond_to?(:child_name)
            key.child_name
        end
    end

    specializations.specialize(options, &block)
end

#specialized_childrenObject

:attr: specialized_children

The set of specializations that are applied from the root of the model graph up to this model

It is empty for composition models that are not specializations



93
# File 'lib/syskit/models/composition.rb', line 93

attribute(:specialized_children) { Hash.new }

#specialized_on?(child_name, child_model) ⇒ Boolean

See CompositionSpecialization#specialized_on?

Returns:

  • (Boolean)


143
144
145
146
# File 'lib/syskit/models/composition.rb', line 143

def specialized_on?(child_name, child_model)
    specialized_children.has_key?(child_name) &&
        specialized_children[child_name].include?(child_model)
end

#to_dot(io) ⇒ Object



1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
# File 'lib/syskit/models/composition.rb', line 1040

def to_dot(io)
    id = object_id.abs

    connections.each do |(source, sink), mappings|
        mappings.each do |(source_port, sink_port), policy|
            io << "C#{id}#{source}:#{source_port} -> C#{id}#{sink}:#{sink_port};"
        end
    end

    if !is_specialization?
        specializations = each_specialization.to_a
        specializations.each do |spec, specialized_model|
            specialized_model.to_dot(io)

            specialized_model.parent_models.each do |parent_compositions|
                parent_id = parent_compositions.object_id
                specialized_id = specialized_model.object_id
                io << "C#{parent_id} -> C#{specialized_id} [ltail=cluster_#{parent_id} lhead=cluster_#{specialized_id} weight=2];"
            end
        end
    end

    io << "subgraph cluster_#{id} {"
    io << "  fontsize=18;"
    io << "  C#{id} [style=invisible];"

    if !exported_inputs.empty? || !exported_outputs.empty?
        inputs = exported_inputs.keys
        outputs = exported_outputs.keys
        label = Graphviz.dot_iolabel("Composition Interface", inputs, outputs)
        io << "  Cinterface#{id} [label=\"#{label}\",color=blue,fontsize=15];"
        
        exported_outputs.each do |exported_name, port|
            io << "C#{id}#{port.component_model.child_name}:#{port.port.name} -> Cinterface#{id}:#{exported_name} [style=dashed];"
        end
        exported_inputs.each do |exported_name, port|
            io << "Cinterface#{id}:#{exported_name} -> C#{id}#{port.component_model.child_name}:#{port.port.name} [style=dashed];"
        end
    end
    label = [short_name.dup]
    provides = each_data_service.map do |name, type|
        "#{name}:#{type.model.short_name}"
    end
    if abstract?
        label << "Abstract"
    end
    if !provides.empty?
        label << "Provides:"
        label.concat(provides)
    end
    io << "  label=\"#{label.join("\\n")}\";"
    # io << "  label=\"#{model.name}\";"
    # io << "  C#{id} [style=invisible];"
    each_child do |child_name, child_definition|
        child_model = child_definition.each_required_model

        task_label = child_model.map(&:short_name).join(',')
        task_label = "#{child_name}[#{task_label}]"
        inputs = child_model.map { |m| m.each_input_port.map(&:name) }.
            inject(&:concat).to_a
        outputs = child_model.map { |m| m.each_output_port.map(&:name) }.
            inject(&:concat).to_a
        label = Graphviz.dot_iolabel(task_label, inputs, outputs)

        if child_model.any? { |m| !m.fullfills?(Component) || m.abstract? }
            color = ", color=\"red\""
        end
        io << "  C#{id}#{child_name} [label=\"#{label}\"#{color},fontsize=15];"
    end
    io << "}"
end

#try_resolve_child_references_in_use_flags(self_task, selected_child) ⇒ Boolean

Resolves references to other children in a child's use flags

This updates the selected_child requirements to replace CompositionChild object by the actual task instance.

otherwise

Parameters:

  • self_task (Composition)

    the task that represents the composition itself

  • selected_child (InstanceSelection)

    the requirements that are meant to be updated

Returns:

  • (Boolean)

    if all references could be updated, false



850
851
852
853
854
855
856
857
858
859
860
861
862
863
# File 'lib/syskit/models/composition.rb', line 850

def try_resolve_child_references_in_use_flags(self_task, selected_child)
    # Check if selected_child points to another child of
    # self, and if it is the case, make sure it is available
    selected_child.map_use_selections! do |sel|
        if sel.kind_of?(CompositionChild)
            if task = sel.try_resolve_and_bind_child_recursive(self_task)
                task
            else return
            end
        else sel
        end
    end
    true
end

#use(*spec) ⇒ Object

Returns this composition associated with dependency injection information

For instance,

CorridorServoing.
    use(Odometry.use(XsensImu::Task))

(see InstanceRequirements#use)



748
749
750
# File 'lib/syskit/models/composition.rb', line 748

def use(*spec)
    InstanceRequirements.new([self]).use(*spec)
end