class Syskit::Component

Base class for models that represent components (TaskContext, Composition)

The model-level methods (a.k.a. singleton methods) are defined on Models::Component). See the documentation of Model for an explanation of this.

Components may be data service providers. Two types of data sources exist:

Data services are referred to by name. In the case of a main service, its name is the name used during the declaration. In the case of slave services, it is main_data_service_name.slave_name. I.e. the name of the slave service depends on the selected

Attributes

dynamics[RW]

The PortDynamics object that holds the dynamics information computed for this task (not its ports) @return [NetworkGeneration::PortDynamics]

required_host[RW]

The name of the process server that should run this component

On regular task contexts, it is the host on which the task is required to run. On compositions, it affects the composition's children

requirements[R]

The InstanceRequirements object for which this component has been instanciated.

Public Class Methods

new(**arguments) click to toggle source
Calls superclass method
# File lib/syskit/component.rb, line 48
def initialize(**arguments)
    super
    @requirements = InstanceRequirements.new
end

Public Instance Methods

added_dynamic_service(srv) click to toggle source

Hook called on an already-configured task when a new service got added.

Note that it will only happen for services whose 'dynamic' flag is set (not the default)

@param [BoundDynamicDataService] srv the newly created service @return [void] @see {#require_dynamic_service}

Calls superclass method
# File lib/syskit/component.rb, line 556
def added_dynamic_service(srv)
    super if defined? super
end
added_input_port_connection(source_port, sink_port, policy) click to toggle source

Hook called when a connection has been created to an input port

This is called after the connection has been established on the underlying ports

@param [Port] source_port @param [Port] sink_port @param [Hash] policy

Calls superclass method
# File lib/syskit/component.rb, line 652
def added_input_port_connection(source_port, sink_port, policy)
    super if defined? super
end
added_output_port_connection(source_port, sink_port, policy) click to toggle source

Hook called when a connection has been created to an output port

This is called after the connection has been established on the underlying ports

@param [Port] source_port @param [Port] sink_port @param [Hash] policy

Calls superclass method
# File lib/syskit/component.rb, line 676
def added_output_port_connection(source_port, sink_port, policy)
    super if defined? super
end
adding_input_port_connection(source_port, sink_port, policy) click to toggle source

Hook called when a connection will be created on an input port

This is called before the connection gets established on the underlying ports

@param [Port] source_port @param [Port] sink_port @param [Hash] policy

Calls superclass method
# File lib/syskit/component.rb, line 640
def adding_input_port_connection(source_port, sink_port, policy)
    super if defined? super
end
adding_output_port_connection(source_port, sink_port, policy) click to toggle source

Hook called when a connection will be created on an output port

This is called before the connection gets established on the underlying ports

@param [Port] source_port @param [Port] sink_port @param [Hash] policy

Calls superclass method
# File lib/syskit/component.rb, line 664
def adding_output_port_connection(source_port, sink_port, policy)
    super if defined? super
end
as(service_model) click to toggle source

Returns a view of this component as a provider of the given service model. It can for instance be used to connect ports while transparently applying port mappings

It works only if there is only one service providing the requested type on self. Otherwise, one will have to select the service first and only then call as on the DataServiceInstance object

The same can be done at the model level with Syskit::Models::Component#as

# File lib/syskit/component.rb, line 480
def as(service_model)
    return model.as(service_model).bind(self)
end
bind(task) click to toggle source
# File lib/syskit/component.rb, line 516
def bind(task)
    if !task.kind_of?(self)
        raise TypeError, "cannot bind #{self} to #{task}"
    end
    task
end
can_be_deployed_by?(task) click to toggle source

Tests whether a task can be used as-is to deploy this

It is mostly the same as {#can_merge?}, while taking into account e.g. that some operations done during merging will require the component to do a reconfiguration cycle

@param [Component] task @return [Boolean]

# File lib/syskit/component.rb, line 304
def can_be_deployed_by?(task)
    task.can_merge?(self)
end
can_finalize?() click to toggle source

Controls whether the task can be removed from the plan

Task context objects are kept while they're being set up, for the sake of not breaking the setup process in an uncontrollable way.

# File lib/syskit/component.rb, line 256
def can_finalize?
    !setting_up?
end
can_merge?(task) click to toggle source

Test if the given task could be merged in self

This method should only consider intrinsic criteria for the merge, as e.g. compatibility of models or the value abstract? It should never look into the task's neighborhood

Calls superclass method
# File lib/syskit/component.rb, line 276
def can_merge?(task)
    if !super
        NetworkGeneration::MergeSolver.info "rejected: Component#can_merge? super returned false"
        return
    end

    # Cannot merge if we are not reusable
    if !reusable?
        NetworkGeneration::MergeSolver.info "rejected: receiver is not reusable"
        return
    end
    # We can not replace a non-abstract task with an
    # abstract one
    if !task.abstract? && abstract?
        NetworkGeneration::MergeSolver.info "rejected: cannot merge a non-abstract task into an abstract one"
        return
    end
    return true
end
concrete_model() click to toggle source

Returns the most-derived model that is not a private specialization

# File lib/syskit/component.rb, line 602
def concrete_model
    self.class.concrete_model
end
configure() click to toggle source

User-provided part of the component configuration

Calls superclass method
# File lib/syskit/component.rb, line 261
def configure
    super if defined? super
end
connect_to(port_or_component, policy = Hash.new) click to toggle source

Automatically computes connections from the output ports of self to the given port or to the input ports of the given component

(see Syskit.connect)

# File lib/syskit/component.rb, line 512
def connect_to(port_or_component, policy = Hash.new)
    Syskit.connect(self, port_or_component, policy)
end
create_fresh_copy() click to toggle source
Calls superclass method
# File lib/syskit/component.rb, line 62
def create_fresh_copy
    new_task = super
    new_task.robot = robot
    new_task
end
data_reader 'port_name'[, policy] click to toggle source
data_reader 'role_name', 'port_name'[, policy]

Returns a data reader that allows to read the specified port

In the first case, the returned reader is applied to a port on self. In the second case, it is a port of the specified child. In both cases, an optional connection policy can be specified as

data_reader('pose', 'pose_samples', :type => :buffer, :size => 1)

A pull policy is taken by default, as to avoid impacting the components.

The reader is automatically disconnected when the task quits

# File lib/syskit/component.rb, line 433
def data_reader(*args)
    task, port_name, policy = data_accessor(*args)
    policy, other_policy = Kernel.filter_options policy, :pull => true
    policy.merge!(other_policy)

    port = task.find_output_port(port_name)
    if !port
        raise ArgumentError, "#{task} has no output port #{port_name}"
    end

    result = port.reader(policy)
    data_readers << result
    result
end
data_writer 'port_name'[, policy] click to toggle source
data_writer 'role_name', 'port_name'[, policy]

Returns a data writer that allows to read the specified port

In the first case, the returned writer is applied to a port on self. In the second case, it is a port of the specified child. In both cases, an optional connection policy can be specified as

data_writer('pose', 'pose_samples', :type => :buffer, :size => 1)

A pull policy is taken by default, as to avoid impacting the components.

The writer is automatically disconnected when the task quits

# File lib/syskit/component.rb, line 404
def data_writer(*args)
    task, port_name, policy = data_accessor(*args)

    port = task.find_input_port(port_name)
    if !port
        raise ArgumentError, "#{task} has no input port #{port_name}"
    end

    result = port.writer(policy)
    data_writers << result
    result
end
dependency_context() click to toggle source

Returns a description of this task based on the dependency information

# File lib/syskit/component.rb, line 623
def dependency_context
    enum_for(:each_parent_object, Roby::TaskStructure::Dependency).
        map do |parent_task|
            options = parent_task[self,
                                  Roby::TaskStructure::Dependency]
            [options[:roles].to_a.first, parent_task]
        end
end
deployment_hints() click to toggle source

Returns a set of hints that should be used to disambiguate the deployment of this task.

It looks for deployment_hints in the requirements. If there are none, it then looks in the parents.

# File lib/syskit/component.rb, line 80
def deployment_hints
    hints = requirements.deployment_hints
    return hints if !hints.empty?

    result = Set.new
    each_parent_task do |p|
        result |= p.deployment_hints
    end
    result
end
duplicate_missing_services_from(task) click to toggle source
# File lib/syskit/component.rb, line 342
def duplicate_missing_services_from(task)
    missing_services = task.model.each_data_service.find_all do |_, srv|
        !model.find_data_service(srv.full_name)
    end

    missing_services.each do |_, srv|
        if !srv.respond_to?(:dynamic_service)
            raise InternalError, "attempting to duplicate static service #{srv.name} from #{task} to #{self}"
        end
        dynamic_service_options = Hash[as: srv.name].
            merge(srv.dynamic_service_options)
        require_dynamic_service srv.dynamic_service.name, **dynamic_service_options
    end
end
each_data_service() { |bind| ... } click to toggle source

Yields the data services that are defined on this task

# File lib/syskit/component.rb, line 97
def each_data_service
    return enum_for(:each_data_service) if !block_given?
    model.each_data_service do |name, srv|
        yield(srv.bind(self))
    end
    self
end
each_dynamic_service(&block) click to toggle source

@deprecated has been renamed to {#each_required_dynamic_service}

for consistency with the model-level method
# File lib/syskit/component.rb, line 562
def each_dynamic_service(&block)
    each_required_dynamic_service(&block)
end
each_fullfilled_model(&block) click to toggle source

Returns the set of models this task fullfills

# File lib/syskit/component.rb, line 92
def each_fullfilled_model(&block)
    model.each_fullfilled_model(&block)
end
each_required_dynamic_service() { |srv| ... } click to toggle source

Yields the data services that have been created through the dynamic data service mechanism

@yieldparam [BoundDataService] srv the data service generated

using a dynamic data service. srv.model is an instance of
{Models::BoundDynamicDataService} and srv.model.dynamic_service
is the original dynamic data service (ouch !)

@return [void]

# File lib/syskit/component.rb, line 574
def each_required_dynamic_service
    return enum_for(:each_dynamic_service) if !block_given?
    each_data_service do |srv|
        if srv.model.respond_to?(:dynamic_service)
            yield(srv)
        end
    end
end
find_data_service(service_name) click to toggle source

Finds a data service by its name

@param [String] service_name the data service name @return [BoundDataService,nil] the found data service, or nil if

there are no services with that name in self
# File lib/syskit/component.rb, line 114
def find_data_service(service_name)
    if service_model = model.find_data_service(service_name)
        return service_model.bind(self)
    end
end
find_data_service_from_type(service_type) click to toggle source

Finds a data service by its data service model

@param [Model<DataService>] service_type the data service model we want to find

in self

@return [BoundDataService,nil] the found data service, or nil if there

are no services of that type in self

@raise (see Models::DataService#find_data_service_from_type)

# File lib/syskit/component.rb, line 127
def find_data_service_from_type(service_type)
    if service_model = model.find_data_service_from_type(service_type)
        return service_model.bind(self)
    end
end
find_through_method_missing(m, args) click to toggle source
# File lib/syskit/component.rb, line 466
def find_through_method_missing(m, args)
    MetaRuby::DSLs.find_through_method_missing(
        self, m, args, '_srv' => :find_data_service) || super
end
has_data_service?(service_name) click to toggle source
# File lib/syskit/component.rb, line 105
def has_data_service?(service_name)
    !!model.find_data_service(service_name)
end
has_through_method_missing?(m) click to toggle source
# File lib/syskit/component.rb, line 461
def has_through_method_missing?(m)
    MetaRuby::DSLs.has_through_method_missing?(
        self, m, '_srv' => :has_data_service?) || super
end
initialize_copy(source) click to toggle source
Calls superclass method
# File lib/syskit/component.rb, line 53
def initialize_copy(source)
    super
    @requirements = @requirements.dup
    if source.specialized_model?
        specialize
    end
    duplicate_missing_services_from(source)
end
meets_configurationg_precedence_constraints?() click to toggle source
# File lib/syskit/component.rb, line 147
def meets_configurationg_precedence_constraints?
    waiting_precedence_relation = start_event.
        parent_objects(Roby::EventStructure::SyskitConfigurationPrecedence).
        find do |event|
            !event.emitted? && !event.unreachable?
        end

    if waiting_precedence_relation
        debug { "#{self} not ready for setup: waiting on #{waiting_precedence_relation}" }
        false
    else
        true
    end
end
merge(merged_task) click to toggle source

Updates self so that it is a valid replacement for merged_task

This method assumes that can_merge?(task) has already been called and returned true

# File lib/syskit/component.rb, line 312
def merge(merged_task)
    # Copy arguments of +merged_task+ that are not yet assigned in
    # +self+
    arguments.semantic_merge!(merged_task.arguments)

    # Merge the fullfilled model if set explicitely
    explicit_merged_fullfilled_model = merged_task.explicit_fullfilled_model
    explicit_this_fullfilled_model   = explicit_fullfilled_model
    if explicit_this_fullfilled_model && explicit_merged_fullfilled_model
        self.fullfilled_model = Roby::TaskStructure::Dependency.merge_fullfilled_model(
            explicit_merged_fullfilled_model,
            [explicit_this_fullfilled_model[0]] + explicit_this_fullfilled_model[1],
            explicit_this_fullfilled_model[2])

    elsif explicit_merged_fullfilled_model
        self.fullfilled_model = explicit_merged_fullfilled_model.dup
    end

    # Merge the InstanceRequirements objects
    update_requirements(merged_task.requirements)

    # If merged_task has instantiated dynamic services, instantiate
    # them on self
    if merged_task.model.private_specialization?
        duplicate_missing_services_from(merged_task)
    end

    nil
end
perform_setup(promise) click to toggle source

@api private

The actual setup operations. {#setup} is the user-facing part of the setup API, which creates the promise and sets up the setup-related bookkeeping operations

# File lib/syskit/component.rb, line 206
def perform_setup(promise)
    promise.on_success(description: "#{self}#perform_setup#configure") do
        freeze_delayed_arguments
        if self.model.needs_stub?(self)
            self.model.prepare_stub(self)
        end
        configure
    end
end
placeholder_task?() click to toggle source

Wether this component instance is a placeholder for data services

@see Models::PlaceholderTask

# File lib/syskit/component.rb, line 71
def placeholder_task?
    false
end
removed_input_port_connection(source_task, source_port, sink_port) click to toggle source

Hook called when a connection has been removed from an input port

This is called after the connection has been removed on the underlying ports. It will be called only if this task is set up

Unlike the add hooks, this does not deal with the syskit-level representation of tasks, but with the underlying component handler. The root cause of it is that disconnection can be performed after a Roby task got finalized.

@param [Orocos::TaskContext] source_task @param [String] source_port @param [String] sink_port

Calls superclass method
# File lib/syskit/component.rb, line 710
def removed_input_port_connection(source_task, source_port, sink_port)
    super if defined? super
end
removed_output_port_connection(source_port, sink_task, sink_port) click to toggle source

Hook called when a connection has been removed from an output port

This is called after the connection has been removed on the underlying ports. It will be called only if this task is set up

Unlike the add hooks, this does not deal with the syskit-level representation of tasks, but with the underlying component handler. The root cause of it is that disconnection can be performed after a Roby task got finalized.

@param [String] source_port @param [Orocos::TaskContext] sink_task @param [String] sink_port

Calls superclass method
# File lib/syskit/component.rb, line 744
def removed_output_port_connection(source_port, sink_task, sink_port)
    super if defined? super
end
removing_input_port_connection(source_task, source_port, sink_port) click to toggle source

Hook called when a connection will be removed from an input port

This is called before the connection gets removed on the underlying ports. It will be called only if this task is set up

Unlike the add hooks, this does not deal with the syskit-level representation of tasks, but with the underlying component handler. The root cause of it is that disconnection can be performed after a Roby task got finalized.

@param [Orocos::TaskContext] source_task @param [String] source_port @param [String] sink_port

Calls superclass method
# File lib/syskit/component.rb, line 693
def removing_input_port_connection(source_task, source_port, sink_port)
    super if defined? super
end
removing_output_port_connection(source_port, sink_task, sink_port) click to toggle source

Hook called when a connection will be removed from an output port

This is called before the connection gets removed on the underlying ports. It will be called only if this task is set up

Unlike the add hooks, this does not deal with the syskit-level representation of tasks, but with the underlying component handler. The root cause of it is that disconnection can be performed after a Roby task got finalized.

@param [String] source_port @param [Orocos::TaskContext] sink_task @param [String] sink_port

Calls superclass method
# File lib/syskit/component.rb, line 727
def removing_output_port_connection(source_port, sink_task, sink_port)
    super if defined? super
end
require_dynamic_service(dynamic_service_name, as: nil, **dyn_options) click to toggle source

Requires a new dynamic service on this task context

As {Models::Component#dynamic_service} already stated, the new dynamic service is a description of what the task should provide. One needs to reimplement the model's configure method to actually configure the task properly.

@param (see Syskit::Models::Component#require_dynamic_service) @return [BoundDataService] the newly created service

# File lib/syskit/component.rb, line 536
def require_dynamic_service(dynamic_service_name, as: nil, **dyn_options)
    specialize
    bound_service = self.model.require_dynamic_service(
        dynamic_service_name, as: as, **dyn_options)
    srv = bound_service.bind(self)
    if plan && plan.executable? && setup?
        added_dynamic_service(srv)
    end
    srv
end
self_port_to_actual_port(port) click to toggle source

Resolves the given Syskit::Port object into a port object that points to the real task context port

It should not be used directly. One should usually use Syskit::Port#to_actual_port instead

@return [Syskit::Port]

# File lib/syskit/component.rb, line 491
def self_port_to_actual_port(port)
    port
end
self_port_to_component_port(port) click to toggle source

Resolves the given Syskit::Port object into a Port object where component is guaranteed to be a proper component instance

It should not be used directly. One should usually use Syskit::Port#to_component_port

@param [Syskit::Port] port @return [Syskit::Port] a port in which Syskit::Port#component is

guaranteed to be a proper component (e.g. not BoundDataService)
# File lib/syskit/component.rb, line 504
def self_port_to_component_port(port)
    model.self_port_to_component_port(port.model).bind(self)
end
setting_up!(promise) click to toggle source

@api private

Called once at the beginning of a setup promise

# File lib/syskit/component.rb, line 219
def setting_up!(promise)
    if @setting_up
        raise InvalidState, "#{self} is already setting up"
    end
    @setting_up = promise
end
setting_up?() click to toggle source

Whether the task is being set up

# File lib/syskit/component.rb, line 248
def setting_up?
    !!@setting_up
end
setup() click to toggle source

Create a {Roby::Promise} object that configures the component

Never overload this method. Overload {#perform_setup} instead.

@return [Promise]

# File lib/syskit/component.rb, line 185
def setup
    if setup?
        raise ArgumentError, "#{self} is already set up"
    end
    promise = self.promise(description: "promise:#{self}#setup")
    perform_setup(promise)
    promise.on_error(description: "#{self}#setup#setup_failed!") do |e|
        setup_failed!(e)
    end
    promise.on_success(description: "#{self}#setup#setup_successful!") do
        setup_successful!
    end
    setting_up!(promise)
    promise
end
setup_failed!(exception) click to toggle source

@api private

Called when the setup process failed

# File lib/syskit/component.rb, line 238
def setup_failed!(exception)
    if start_event.plan
        start_event.emit_failed(exception)
    else
        Roby.execution_engine.add_framework_error(e, "#{self} got finalized before the setting_up! error handler was called")
    end
    @setting_up = nil
end
setup_successful!() click to toggle source

@api private

Called once the setup process is finished to mark the task as set up

# File lib/syskit/component.rb, line 230
def setup_successful!
    @setting_up = nil
    self.setup = true
end
should_configure_after(object) click to toggle source

Declare that this component should not be configured until event has been emitted. This is used to sequence configurations with other system events, but should not be required in most cases

# File lib/syskit/component.rb, line 136
def should_configure_after(object)
    # To make the scheduler happy
    should_start_after object
    object.add_syskit_configuration_precedence(start_event)
end
specialize() click to toggle source

Sets up this task to use its singleton class as model instead of the plain class. It is useful in particular for dynamic services

# File lib/syskit/component.rb, line 589
def specialize
    if model != singleton_class
        @model = singleton_class
        model.name = self.class.name
        model.concrete_model = self.class.concrete_model
        model.private_specialization = true
        model.private_model
        self.class.setup_submodel(model)
        true
    end
end
specialized_model?() click to toggle source
# File lib/syskit/component.rb, line 583
def specialized_model?
    concrete_model != model
end
start_only_when_connected?() click to toggle source

Whether this task should be started only after all its inputs have been connected

# File lib/syskit/component.rb, line 267
def start_only_when_connected?
    true
end
to_instance_requirements() click to toggle source

Generates the InstanceRequirements object that represents self best

@return [Syskit::InstanceRequirements]

# File lib/syskit/component.rb, line 610
def to_instance_requirements
    # Do not use #model here as we don't want a requirement that
    # uses a specialized model
    req = self.class.to_instance_requirements
    req.with_arguments(arguments.assigned_arguments)
    if required_host
        req.on_server(required_host)
    end
    req
end
update_requirements(new_requirements, keep_abstract: false) click to toggle source
# File lib/syskit/component.rb, line 523
def update_requirements(new_requirements, keep_abstract: false)
    requirements.merge(new_requirements, keep_abstract: keep_abstract)
end
will_never_setup?() click to toggle source

Whether this task context will ever configurable

# File lib/syskit/component.rb, line 143
def will_never_setup?
    false
end