Class: Syskit::Models::BoundDataService

Inherits:
Object
  • Object
show all
Extended by:
InstanceRequirements::Auto
Includes:
MetaRuby::DSLs::FindThroughMethodMissing, Base, PortAccess
Defined in:
lib/syskit/models/bound_data_service.rb

Overview

Representation of a data service as provided by a component model

Instances of this class are usually created by Component#provides. Note that bound dynamic services are instances of BoundDynamicDataService instead.

At the component instance level, each BoundDataService is represented by a corresponding BoundDataService, whose BoundDataService#model method returns this object. This instance-level object is created with #bind

Bound data services are promoted from one component model to its submodels, which means that when one does

bound_m = task_m.test_srv
sub_m = task_m.mew_submodel
sub_bound_m = bound_m.test_srv

Then sub_bound_m != bound_m as sub_bound_m is bound to sub_m and bound_m is bound to task_m. Use #same_service? to check whether two services are different promoted version of the same original service

Direct Known Subclasses

BoundDynamicDataService

Defined Under Namespace

Classes: DRoby

Constant Summary

Constants included from InstanceRequirements::Auto

InstanceRequirements::Auto::METHODS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from PortAccess

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

Constructor Details

#initialize(name, component_model, master, model, port_mappings) ⇒ BoundDataService

Returns a new instance of BoundDataService



74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/syskit/models/bound_data_service.rb', line 74

def initialize(name, component_model, master, model, port_mappings)
    @name, @component_model, @master, @model, @port_mappings =
        name, component_model, master, model, port_mappings

    @full_name =
        if master
            "#{master.name}.#{name}"
        else
            name
        end

    @demoted = self
end

Instance Attribute Details

#component_modelObject (readonly)

The task model which provides this service



31
32
33
# File 'lib/syskit/models/bound_data_service.rb', line 31

def component_model
  @component_model
end

#demotedObject (readonly)

The original service from which self has been promoted

See Syskit::Models::BoundDataService for more details on promotion



72
73
74
# File 'lib/syskit/models/bound_data_service.rb', line 72

def demoted
  @demoted
end

#full_nameObject (readonly)

The service's full name, i.e. the name with which it is referred to in the task model



44
45
46
# File 'lib/syskit/models/bound_data_service.rb', line 44

def full_name
  @full_name
end

#masterObject (readonly)

The master service (if there is one)



35
36
37
# File 'lib/syskit/models/bound_data_service.rb', line 35

def master
  @master
end

#modelObject (readonly)

The service model



37
38
39
# File 'lib/syskit/models/bound_data_service.rb', line 37

def model
  @model
end

#nameObject (readonly)

The service name



33
34
35
# File 'lib/syskit/models/bound_data_service.rb', line 33

def name
  @name
end

#port_mappingsObject (readonly)

The mappings needed between the ports in the service interface and the actual ports on the component



40
41
42
# File 'lib/syskit/models/bound_data_service.rb', line 40

def port_mappings
  @port_mappings
end

Instance Method Details

#==(other) ⇒ Object



63
64
65
# File 'lib/syskit/models/bound_data_service.rb', line 63

def ==(other)
    eql?(other)
end

#as(service_model) ⇒ BoundDataService

Returns a view of this service as a provider of service_model

It allows to transparently apply port mappings as if self was a service of type service_model

The original state of self (before as was called) can be retrieved by calling #as_real_model on the returned value

Returns:



184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/syskit/models/bound_data_service.rb', line 184

def as(service_model)
    result = dup
    result.instance_variable_set(:@model, service_model)

    mappings = port_mappings.dup
    mappings.delete_if do |srv, _|
        !service_model.fullfills?(srv)
    end
    result.instance_variable_set(:@port_mappings, mappings)
    result.ports.clear
    result
end

#as_real_modelBoundDataService

Returns the actual bound data service when the receiver is the return value of #as

Returns:



201
202
203
# File 'lib/syskit/models/bound_data_service.rb', line 201

def as_real_model
    component_model.find_data_service(full_name)
end

#attach(new_component_model, verify: true) ⇒ Object

Returns the bound data service object that represents self being attached to a new component model



134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/syskit/models/bound_data_service.rb', line 134

def attach(new_component_model, verify: true)
    if new_component_model == self
        return self
    elsif verify && !new_component_model.fullfills?(component_model)
        raise ArgumentError, "cannot attach #{self} on #{new_component_model}: does not fullfill #{component_model}"
    end

    # NOTE: do NOT use #find_data_service here ! find_data_service
    # NOTE: might require some promotion from parent models, which
    # NOTE: is done using #attach !
    result = dup
    result.instance_variable_set :@component_model, new_component_model
    result
end

#bind(task) ⇒ Syskit::BoundDataService

Returns the BoundDataService object that binds this provided service to an actual task

Parameters:

Returns:



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/syskit/models/bound_data_service.rb', line 285

def bind(task)
    if task.model == self
        # !!! task is a BoundDataService
        return task
    elsif task.model <= component_model # This is stronger than #fullfills?
        Syskit::BoundDataService.new(task, self)
    elsif task.fullfills?(component_model)
        # Fullfills, but does not inherit ? component_model may be
        # a data service proxies
        if component_model.placeholder?
            base_model = component_model.superclass
            if base_model_srv = base_model.find_data_service(name)
                # The data service is from a concrete task model
                Syskit::BoundDataService.new(task, base_model_srv)
            else
                task.find_data_service_from_type(model)
            end
        # Or maybe we're dealing with dynamic service instanciation
        else
            resolved = task.find_data_service(name)
            if resolved && resolved.model.same_service?(self)
                return resolved
            else
                raise InternalError, "#{component_model} is fullfilled by #{task}, but is not inherited by its model #{task.model}. I didn't manage to resolve this, either as a task-to-placeholder mapping, or as a dynamic service"
            end
        end
    else
        raise ArgumentError, "cannot bind #{self} on #{task}: does not fullfill #{component_model}"
    end
end

#connect_to(other, policy = Hash.new) ⇒ Object



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

def connect_to(other, policy = Hash.new)
    Syskit.connect(self, other, policy)
end

#dependency_injection_namesObject



54
# File 'lib/syskit/models/bound_data_service.rb', line 54

def dependency_injection_names; Array.new end

#droby_dump(peer) ⇒ Object



417
418
419
# File 'lib/syskit/models/bound_data_service.rb', line 417

def droby_dump(peer)
    DRoby.new(name, peer.dump(component_model), peer.dump(master), peer.dump(model))
end

#dynamic?Boolean

True if this is a dynamic service

Returns:

  • (Boolean)


50
# File 'lib/syskit/models/bound_data_service.rb', line 50

def dynamic?; false end

#each_data_service(&block) ⇒ Object



261
262
263
# File 'lib/syskit/models/bound_data_service.rb', line 261

def each_data_service(&block)
    self
end

#each_fullfilled_model(&block) ⇒ Object



270
271
272
# File 'lib/syskit/models/bound_data_service.rb', line 270

def each_fullfilled_model(&block)
    model.each_fullfilled_model(&block)
end

#each_input_portObject



125
126
127
128
129
130
# File 'lib/syskit/models/bound_data_service.rb', line 125

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

#each_output_portObject



118
119
120
121
122
123
# File 'lib/syskit/models/bound_data_service.rb', line 118

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

#each_required_model {|model| ... } ⇒ Object

Yields:



341
342
343
344
# File 'lib/syskit/models/bound_data_service.rb', line 341

def each_required_model
    return enum_for(:each_required_model) if !block_given?
    yield(model)
end

#each_slave_data_service(&block) ⇒ Object

Enumerates the data services that are slave of this one



266
267
268
# File 'lib/syskit/models/bound_data_service.rb', line 266

def each_slave_data_service(&block)
    component_model.each_slave_data_service(self, &block)
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


56
57
58
59
60
61
# File 'lib/syskit/models/bound_data_service.rb', line 56

def eql?(other)
    other.kind_of?(self.class) &&
        other.full_name == full_name &&
        other.model == model &&
        other.component_model == component_model
end

#find_data_service(name) ⇒ Object



355
356
357
358
359
360
361
362
# File 'lib/syskit/models/bound_data_service.rb', line 355

def find_data_service(name)
    component_model.each_slave_data_service(self) do |slave_m|
        if slave_m.name == name
            return slave_m
        end
    end
    nil
end

#find_input_port(name) ⇒ Object



111
112
113
114
115
116
# File 'lib/syskit/models/bound_data_service.rb', line 111

def find_input_port(name)
    name = name.to_str
    if (mapped = port_mappings_for_task[name]) && (port = component_model.find_input_port(mapped))
        ports[name] ||= InputPort.new(self, port.orogen_model, name)
    end
end

#find_output_port(name) ⇒ Object



104
105
106
107
108
109
# File 'lib/syskit/models/bound_data_service.rb', line 104

def find_output_port(name)
    name = name.to_str
    if (mapped = port_mappings_for_task[name]) && (port = component_model.find_output_port(mapped))
        ports[name] ||= OutputPort.new(self, port.orogen_model, name)
    end
end

#find_port_for_task_port(task_port) ⇒ nil, Port

Returns the service port that maps to a task port

Returns:



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

def find_port_for_task_port(task_port)
    task_port_name = task_port.name
    port_mappings_for_task.each do |service_port_name, port_name|
        if port_name == task_port_name
            return find_port(service_port_name)
        end
    end
    nil
end

#find_through_method_missing(m, args) ⇒ Object



368
369
370
371
372
# File 'lib/syskit/models/bound_data_service.rb', line 368

def find_through_method_missing(m, args)
    MetaRuby::DSLs.find_through_method_missing(
        self, m, args,
        '_srv'.freeze => :find_data_service) || super
end

#fullfilled_modelObject



205
206
207
# File 'lib/syskit/models/bound_data_service.rb', line 205

def fullfilled_model
    [model]
end

#fullfills?(models) ⇒ Boolean

Returns true if self provides all models in models

Returns:

  • (Boolean)


210
211
212
213
214
215
216
217
218
219
220
# File 'lib/syskit/models/bound_data_service.rb', line 210

def fullfills?(models)
    if !models.respond_to?(:each)
        models = [models]
    end
    models.each do |required_m|
        required_m.each_fullfilled_model do |m|
            return false if !self.model.fullfills?(m)
        end
    end
    true
end

#has_data_service?(name) ⇒ Boolean

Returns:

  • (Boolean)


348
349
350
351
352
353
# File 'lib/syskit/models/bound_data_service.rb', line 348

def has_data_service?(name)
    component_model.each_slave_data_service(self) do |slave_m|
        return true if slave_m.name == name
    end
    false
end

#has_through_method_missing?(m) ⇒ Boolean

Returns:

  • (Boolean)


364
365
366
367
# File 'lib/syskit/models/bound_data_service.rb', line 364

def has_through_method_missing?(m)
    MetaRuby::DSLs.has_through_method_missing?(
        self, m, '_srv'.freeze => :has_data_service?) || super
end

#hashObject



67
# File 'lib/syskit/models/bound_data_service.rb', line 67

def hash; [self.class, full_name, component_model].hash end

#initialize_copy(original) ⇒ Object



88
89
90
91
# File 'lib/syskit/models/bound_data_service.rb', line 88

def initialize_copy(original)
    super
    @ports = Hash.new
end

#inspectObject



161
# File 'lib/syskit/models/bound_data_service.rb', line 161

def inspect; to_s end

#instanciate(plan, context = DependencyInjectionContext.new, options = Hash.new) ⇒ Syskit::BoundDataService

Creates, in the given plan, a new task matching this service in the given context, and returns the instanciated data service



327
328
329
# File 'lib/syskit/models/bound_data_service.rb', line 327

def instanciate(plan, context = DependencyInjectionContext.new, options = Hash.new)
    bind(component_model.instanciate(plan, context, options))
end

#master?Boolean

True if this service is not a slave service

Returns:

  • (Boolean)


47
# File 'lib/syskit/models/bound_data_service.rb', line 47

def master?; !@master end

#merge(other_model) ⇒ Object



274
275
276
277
# File 'lib/syskit/models/bound_data_service.rb', line 274

def merge(other_model)
    m = other_model.merge(component_model)
    attach(m)
end

#orogen_modelObject

Orocos::Spec::TaskContext

the oroGen model for this service's

interface



100
101
102
# File 'lib/syskit/models/bound_data_service.rb', line 100

def orogen_model
    model.orogen_model
end

#port_mappings_for(service_model) ⇒ Hash<String,String>

Returns the port mappings that should be applied from one of the service models provided by #model to #component_model

Parameters:

Returns:

  • (Hash<String,String>)

    mapping from the name of a port of service_model to the name of a port on #component_model

See Also:



254
255
256
257
258
259
# File 'lib/syskit/models/bound_data_service.rb', line 254

def port_mappings_for(service_model)
    if !(result = port_mappings[service_model])
        raise ArgumentError, "#{service_model} is not provided by #{model.short_name}"
    end
    result
end

#port_mappings_for_taskHash<String,String>

Returns the port mappings that should be applied to convert a port from this service to #component_model

Returns:

  • (Hash<String,String>)

    mapping from the name of a port of self to the name of a port on #component_model

See Also:



242
243
244
# File 'lib/syskit/models/bound_data_service.rb', line 242

def port_mappings_for_task
    port_mappings_for(model)
end

#pretty_print(pp) ⇒ Object



167
168
169
170
171
172
173
# File 'lib/syskit/models/bound_data_service.rb', line 167

def pretty_print(pp)
    pp.text "service #{name}(#{model.name}) of"
    pp.nest(2) do
        pp.breakable
        component_model.pretty_print(pp)
    end
end

#resolve(task) ⇒ Object

Deprecated.

use #bind instead



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

def resolve(task)
    Roby.warn_deprecated "#{__method__} is deprecated, use "\
        "BoundDataService#bind instead"
    bind(task)
end

#same_service?(other) ⇒ Boolean

Whether two services are the same service bound to two different interfaces

When subclassing, the services are promoted to the new component interface that is being accessed, so in effect when one does

bound_m = task_m.test_srv
sub_m = task_m.mew_submodel
sub_bound_m = bound_m.test_srv

Then sub_bound_m != bound_m as sub_bound_m is bound to sub_m and bound_m is bound to task_m. This is important, as we sometimes want to compare services including which interface they're bound to.

However, in some cases, we want to know whether two services are actually issues from the same service definition, i.e. have been promoted from the same service. This method performs that comparison

Parameters:

Returns:

  • (Boolean)


393
394
395
# File 'lib/syskit/models/bound_data_service.rb', line 393

def same_service?(other)
    other.demoted == self.demoted
end

#selected_for(requirements) ⇒ InstanceSelection

The selection object that represents self being selected for requirements

Parameters:

Returns:



403
404
405
# File 'lib/syskit/models/bound_data_service.rb', line 403

def selected_for(requirements)
    InstanceSelection.new(nil, self.to_instance_requirements, requirements.to_instance_requirements)
end

#self_port_to_component_port(port) ⇒ Models::Port

Resolves the given port model into a component model where #component_model is a proper component (e.g. not a BoundDataService)

It is not meant to be used directly. Use Port#to_component_port instead

Parameters:

Returns:



94
95
96
# File 'lib/syskit/models/bound_data_service.rb', line 94

def self_port_to_component_port(port)
    return component_model.find_port(port_mappings_for_task[port.name])
end

#short_nameObject



163
164
165
# File 'lib/syskit/models/bound_data_service.rb', line 163

def short_name
    "#{component_model.short_name}:#{full_name}"
end

#to_component_modelObject



52
# File 'lib/syskit/models/bound_data_service.rb', line 52

def to_component_model; component_model.to_component_model end

#to_instance_requirementsSyskit::InstanceRequirements

Generates the InstanceRequirements object that represents self best



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

def to_instance_requirements
    req = component_model.to_instance_requirements
    req.select_service(self)
    req
end

#to_sObject



157
158
159
# File 'lib/syskit/models/bound_data_service.rb', line 157

def to_s
    "#{component_model.short_name}.#{full_name}"
end