Class: Syskit::Composition

Inherits:
Component show all
Extended by:
Models::Composition
Includes:
PortAccess
Defined in:
lib/syskit/composition.rb

Overview

Compositions, i.e. grouping of components and/or other compositions that perform a given function.

Compositions are used to regroup components and/or other compositions in functional groups.

See the Models::Composition for class-level methods

Defined Under Namespace

Classes: CompositionChildInstance

Constant Summary

Constants included from Models::Component

Models::Component::PROVIDES_ARGUMENTS

Instance Attribute Summary collapse

Attributes included from Models::Component

#concrete_model

Attributes inherited from Component

#dynamics, #required_host, #requirements

Instance Method Summary collapse

Methods included from Models::Composition

add, add_child, add_main, add_main_task, add_optional, add_specialization_constraint, all_child, all_child_constraint, all_configuration, all_explicit_connection, all_exported_input, all_exported_output, applied_specializations, child, child_constraints, child_port?, children, children_names, clear_model, compute_child_dependency_options, configuration, configurations, connect, connections, constraints_for, create_dynamic_instantiation_context, create_private_specialization, dependency_injection_names, each_child, each_child_constraint, each_configuration, each_explicit_connection, each_exported_input, each_exported_output, each_input_port, each_output_port, explicit_connection, export, exported_input, exported_inputs, exported_output, exported_outputs, exported_port?, extract_grandchild_selections_by_child_name, find_applicable_specialization_from_selection, find_child, find_child_constraint, find_child_model_and_task, find_children_models_and_tasks, find_configuration, find_exported_input, find_exported_output, find_input_port, find_output_port, find_through_method_missing, fullfills?, has_child?, has_child_constraint?, has_configuration?, has_dynamic_input_port?, has_dynamic_output_port?, has_exported_input?, has_exported_output?, has_through_method_missing?, inherited, instanciate, instanciate_child, instanciate_connections, is_specialization?, main_task, merge, narrow, new_specialized_submodel, overload, parent_model_of?, prefer_specializations, pretty_print, promote_child, promote_explicit_connection, promote_exported_input, promote_exported_output, promote_exported_port, root_model, setup_submodel, specializations, specialize, specialized_children, specialized_on?, to_dot, try_resolve_child_references_in_use_flags, use

Methods included from Models::Component

#all_data_service, #all_dynamic_service, #all_stub_module, #apply_missing_dynamic_services_from, #as, #as_plan, #bind, #can_merge?, #clear_model, #component_model?, #compute_port_mappings, #concrete_model?, #connected?, #create_dynamic_instantiation_context, #create_private_specialization, #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_input_port, #each_master_driver_service, #each_output_port, #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_input_port, #find_matching_service, #find_output_port, #find_placeholder_model, #find_port, #find_through_method_missing, #fullfills?, #has_through_method_missing?, #if_already_present, #implicit_fullfilled_model, #instanciate, #instanciate_dynamic_input_port, #instanciate_dynamic_output_port, #merge, #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, #specialize, #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 Models::Base

#dependency_injection_names, #pretty_print, #short_name, #to_s

Methods included from Models::PortAccess

#each_input_port, #each_output_port, #each_port, #find_input_port, #find_output_port, #find_port, #find_through_method_missing, #has_dynamic_input_port?, #has_dynamic_output_port?, #has_input_port?, #has_output_port?, #has_port?, #has_through_method_missing?

Methods included from PortAccess

#each_input_port, #each_output_port, #each_port, #find_input_port, #find_output_port, #find_port, #find_through_method_missing, #has_input_port?, #has_output_port?, #has_port?, #has_through_method_missing?

Methods inherited from Component

#added_dynamic_service, #added_input_port_connection, #added_output_port_connection, #adding_input_port_connection, #adding_output_port_connection, #as, #bind, #can_be_deployed_by?, #can_finalize?, #can_merge?, #concrete_model, #configure, #connect_to, #create_fresh_copy, #data_accessor, #data_reader, #data_writer, #dependency_context, #deployment_hints, #duplicate_missing_services_from, #each_data_service, #each_dynamic_service, #each_fullfilled_model, #each_required_dynamic_service, #find_data_service, #find_data_service_from_type, #find_through_method_missing, #has_data_service?, #has_through_method_missing?, #initialize_copy, #meets_configurationg_precedence_constraints?, #merge, #perform_setup, #placeholder?, #ready_for_setup?, #removed_input_port_connection, #removed_output_port_connection, #removing_input_port_connection, #removing_output_port_connection, #require_dynamic_service, #self_port_to_component_port, #setting_up!, #setting_up?, #setup, #setup=, #setup?, #setup_failed!, #setup_successful!, #should_configure_after, #specialize, #specialized_model?, #start_only_when_connected?, #will_never_setup?

Constructor Details

#initialize(options = Hash.new) ⇒ Composition

Returns a new instance of Composition



20
21
22
23
# File 'lib/syskit/composition.rb', line 20

def initialize(options = Hash.new)
    @child_selection = Hash.new
    super
end

Instance Attribute Details

#child_selectionObject (readonly)

A name => SelectedChild mapping of the selection result during #instanciate



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

def child_selection
  @child_selection
end

Instance Method Details

#added_sink(child, mappings) ⇒ Object

Called when a new child is added to this composition.

It updates Flows::DataFlow#modified_tasks so that the engine can update the underlying task's connections



258
259
260
261
# File 'lib/syskit/composition.rb', line 258

def added_sink(child, mappings) # :nodoc:
    super
    dataflow_change_handler(false, child, mappings)
end

#conf(names) ⇒ Object

Returns the configuration definition for the given configuration(s)

Note that unlike ConfigurationManager, only one configuration can be selected (they cannot be overlaid on top of each other)

The returned value is a mapping from child names to the configurations that should be applied to them, i.e. for the 'narrow' configuration used as an example in Composition.conf,

conf(['narrow'])

would return

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


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

def conf(names)
    if names.size != 1
        raise ArgumentError, "unlike with ConfigurationManager, only one  configuration can be selected on compositions"
    end

    result = Hash.new
    found_something = false
    model.each_configuration(names.first.to_s, false) do |values|
        found_something = true
        result = values.merge(result)
    end
    if !found_something
        if names == ['default']
            ConfigurationManager.info "required default configuration on composition #{task}, but #{task.model.short_name} has no registered default configurations"
            return result
        else
            raise ArgumentError, "#{self} has no declared configuration called #{names.join(", ")}"
        end
    end
    result
end

#dataflow_change_handler(ignore_missing_child, child, mappings) ⇒ 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.

Helper for #added_child_object and #removing_child_object

It adds the task to Flows::DataFlow#modified_tasks whenever the DataFlow relations is changed in a way that could require changing the underlying Orocos components connections.



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/syskit/composition.rb', line 207

def dataflow_change_handler(ignore_missing_child, child, mappings) # :nodoc:
    # The case where 'child' is already a task context is already
    # taken care of by
    mappings.each_key do |source_port, sink_port|
        component =
            begin find_port(source_port).to_actual_port.component
            rescue Roby::NoSuchChild
                raise if !ignore_missing_child
            end

        if component
            relation_graph_for(Flows::DataFlow).modified_tasks << component
        end
    end
end

#executable?Boolean

Overriden from Roby::Task

will return false if any of the children is not executable.

Returns:

  • (Boolean)


179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/syskit/composition.rb', line 179

def executable? # :nodoc:
    if !super
        return false
    elsif @executable
        return true
    end

    each_child do |child_task, _|
        if child_task.kind_of?(TaskContext)
            if !child_task.orocos_task
                return false
            end
        elsif child_task.kind_of?(Component) && child_task.start_event.root?
            if !child_task.executable?
                return false
            end
        end
    end
    return true
end

#find_required_composition_child_from_role(role, from_model = self.model) ⇒ Component, ...

Returns a child from its role, as the composition model tells we should see it

Generally speaking, if the composition model requires a data service, this service is going to be returned instead of the whole task

Returns:



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/syskit/composition.rb', line 134

def find_required_composition_child_from_role(role, from_model = self.model)
    selected = child_selection[role]
    return if !selected
    # Check what the child is made of ... We might not have to
    # return a service
    task = find_child_from_role(role)
    return if !task

    target_child_model = from_model.find_child(role)
    if target_srv = target_child_model.service
        service_selections = [selected.service_selection]
        child_model = self.model.find_child(role)
        while !from_model.fullfills?(child_model.composition_model)
            service_selections.unshift child_model.overload_info.service_selection
            child_model = child_model.parent_model
        end
        selected_service_m = service_selections.inject(target_srv) do |srv, selections|
            if selected_srv = selections[srv.model]
                if task.model <= selected_srv.component_model
                    selected_srv
                else selected_srv.as_real_model
                end
            else break srv
            end
        end
        return selected_service_m.bind(task).as(target_srv.model)
    else return task
    end
end

#port_by_name(name) ⇒ Syskit::Port

Finds the corresponding syskit port

Parameters:

  • the (String)

    name of the port that should be found

Returns:

  • (Syskit::Port)

    the actuar orocos port with the given name



119
120
121
122
123
124
# File 'lib/syskit/composition.rb', line 119

def port_by_name(name)
    if p = find_input_port(name) || find_output_port(name)
        p
    else raise ArgumentError, "#{self} has no port called #{name}, known ports are: #{each_port.map(&:name).sort.join(", ")}"
    end
end

#removing_child(child) ⇒ Object

Hook called when one of the compositions' child has been removed

If self has exported ports, this broke some connections (the exported ports are not “internally connected” anymore) and as such the corresponding tasks should be added to modified_tasks



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/syskit/composition.rb', line 228

def removing_child(child)
    super
    dataflow_graph = relation_graph_for(Flows::DataFlow)
    if dataflow_graph.has_edge?(child, self)
        # output ports, we only need to make sure that the dataflow
        # handlers are called
        dataflow_graph.remove_relation(child, self)
    end
    if dataflow_graph.has_edge?(self, child)
        # This one is harder, we need to explicitely add the sources
        # because none of the other triggers will work
        #
        # Note that merging and dependency injection can cause a child
        # to have a non-forwarding connection to the composition. We
        # can't assume that the edges from self to child are all forwarding
        # (input-to-input)
        dataflow_graph.edge_info(self, child).each_key do |self_port_name, child_port_name|
            if (self_port = find_input_port(self_port_name))
                self_port.each_concrete_connection do |source_port|
                    dataflow_graph.modified_tasks << source_port.component
                end
            end
        end
    end
end

#removing_sink(child) ⇒ Object

Called when a child is removed from this composition.

It updates Flows::DataFlow#modified_tasks so that the engine can update the underlying task's connections



272
273
274
275
# File 'lib/syskit/composition.rb', line 272

def removing_sink(child) # :nodoc:
    super
    dataflow_change_handler(true, child, self[child, Flows::DataFlow])
end

#required_composition_child_from_role(role) ⇒ Component, ...

Returns a child from its role, as the composition model tells we should see it

Generally speaking, if the composition model requires a data service, this service is going to be returned instead of the whole task

Returns:

Raises:

  • ArgumentError if the requested child does not exist



168
169
170
171
172
173
174
# File 'lib/syskit/composition.rb', line 168

def required_composition_child_from_role(role)
    selected = find_required_composition_child_from_role(role)
    if !selected
        raise ArgumentError, "#{role} does not seem to be a proper child of this composition"
    end
    selected
end

#resolve_port(port_name) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/syskit/composition.rb', line 67

def resolve_port(port_name)
    export = model.find_exported_input(port_name) ||
        model.find_exported_output(port_name)
    child_name = export.component_model.child_name
    if child = find_child_from_role(child_name)
        actual_port_name = child_selection[child_name].port_mappings[export.name]
        if child.respond_to?(:resolve_port)
            child.resolve_port(actual_port_name)
        else child.find_port(actual_port_name)
        end
    end
end

#self_port_to_actual_port(exported_port) ⇒ Object

attribute is guaranteed to be an instance of TaskContext



86
87
88
89
90
91
92
93
94
95
96
# File 'lib/syskit/composition.rb', line 86

def self_port_to_actual_port(exported_port)
    port_name = exported_port.name

    export = model.find_exported_input(port_name) ||
        model.find_exported_output(port_name)

    child_name = export.component_model.child_name
    child = child_from_role(child_name)
    actual_port_name = child_selection[child_name].port_mappings[export.name]
    child.find_port(actual_port_name).to_actual_port
end

#self_port_to_orocos_port(exported_port) ⇒ Orocos::Port

Maps a port exported on this composition to the actual orocos port that it represents

Parameters:

Returns:

  • (Orocos::Port)

    the actual port



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

def self_port_to_orocos_port(exported_port)
		self_port_to_actual_port(exported_port).to_orocos_port
end

#to_instance_requirementsSyskit::InstanceRequirements

Generates the InstanceRequirements object that represents self best



281
282
283
284
285
286
287
288
289
# File 'lib/syskit/composition.rb', line 281

def to_instance_requirements
    req = super
    use_flags = Hash.new
    model.each_child do |child_name, _|
        use_flags[child_name] = required_composition_child_from_role(child_name).to_instance_requirements
    end
    req.use(use_flags)
    req
end

#update_requirements(new_requirements, name: nil, keep_abstract: false) ⇒ Object



107
108
109
110
111
112
113
# File 'lib/syskit/composition.rb', line 107

def update_requirements(new_requirements, name: nil, keep_abstract: false)
    super
    new_requirements.dynamics.ports.each do |port_name, info|
        find_port(port_name).to_actual_port.component.
            requirements.dynamics.add_port_info(port_name, info)
    end
end

#updated_sink(child, mappings) ⇒ Object



263
264
265
266
# File 'lib/syskit/composition.rb', line 263

def updated_sink(child, mappings)
    super
    dataflow_change_handler(false, child, mappings)
end