Class: Syskit::Models::DataServiceModel

Inherits:
Roby::Models::TaskServiceModel
  • Object
show all
Includes:
MetaRuby::DSLs::FindThroughMethodMissing, Base, PortAccess
Defined in:
lib/syskit/models/data_service.rb

Overview

Base type for data service models (DataService, Devices, ComBus). Methods defined in this class are available on said models (for instance Device.new_submodel)

Direct Known Subclasses

DeviceModel

Defined Under Namespace

Classes: BlockInstanciator

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

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_dynamic_input_port?, #has_dynamic_output_port?, #has_input_port?, #has_output_port?, #has_port?, #has_through_method_missing?, #port_by_name

Methods included from Base

#dependency_injection_names, #short_name, #to_instance_requirements, #to_s

Constructor Details

#initialize(project: Roby.app.default_orogen_project) ⇒ DataServiceModel

Returns a new instance of DataServiceModel



27
28
29
30
# File 'lib/syskit/models/data_service.rb', line 27

def initialize(project: Roby.app.default_orogen_project)
    @orogen_model = OroGen::Spec::TaskContext.new(project)
    super()
end

Instance Attribute Details

#orogen_modelObject (readonly)

Orocos::Spec::TaskContext

the object describing the data

service's interface



239
240
241
# File 'lib/syskit/models/data_service.rb', line 239

def orogen_model
  @orogen_model
end

#rw port_mappings(port_mappings) ⇒ Hash<DataServiceModel,Hash<String,String>>

Port mappings from this service's parent models to the service itself

Whenever a data service provides another one, it is possible to specify that some ports of the provided service are mapped onto th ports of the new service. This hash keeps track of these port mappings.

The mapping is of the form

provided_service_model => [provided_service_model_port, port]

Returns:



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

attribute(:port_mappings) { Hash.new }

Class Method Details

.new_permanent_root(parent: nil) ⇒ Object

Create a model that is root for a model hierarchy

This is used to create e.g. Syskit::DataService

The result of this method must be assigned to a constant. It is marked as permanent w.r.t. MetaRuby's model management.



17
18
19
20
21
22
23
24
25
# File 'lib/syskit/models/data_service.rb', line 17

def self.new_permanent_root(parent: nil)
    model = new(project: OroGen::Spec::Project.blank)
    model.root = true
    model.permanent_model = true
    if parent
        model.provides parent
    end
    model
end

Instance Method Details

#apply_block(name = nil, &block) ⇒ Object

Applies a setup block on a service model

If name is given, that string will be reported as the service name in the block, instead of the actual service name



126
127
128
129
130
131
132
133
134
# File 'lib/syskit/models/data_service.rb', line 126

def apply_block(name = nil, &block)
    BlockInstanciator.new(self, name).instance_eval(&block)

    # Now initialize the port_mappings hash. We register our own
    # ports as identity (from => from)
    self_mappings = (port_mappings[self] ||= Hash.new)
    each_input_port  { |port| self_mappings[port.name] = port.name }
    each_output_port { |port| self_mappings[port.name] = port.name }
end

#as_planObject



359
360
361
# File 'lib/syskit/models/data_service.rb', line 359

def as_plan
    to_instance_requirements.as_plan
end

#bind(component) ⇒ nil, BoundDataService

Binds the data service model on the given task

Parameters:

Returns:

Raises:

  • (ArgumentError)

    if the given component has no such data service, or if it has more than one



322
323
324
325
326
327
328
# File 'lib/syskit/models/data_service.rb', line 322

def bind(component)
    if bound = try_bind(component)
        bound
    else
        raise ArgumentError, "cannot bind #{self} to #{component}"
    end
end

#clear_modelObject



32
33
34
35
36
# File 'lib/syskit/models/data_service.rb', line 32

def clear_model
    super()
    @orogen_model = OroGen::Spec::TaskContext.new(@orogen_model.project)
    port_mappings.clear
end

#connected?(out_port, in_port) ⇒ Boolean

Delegated call from Port#connected?

Always returns false as “plain” data service ports cannot be connected

Returns:

  • (Boolean)


287
288
289
# File 'lib/syskit/models/data_service.rb', line 287

def connected?(out_port, in_port)
    false
end

#create_placeholder_taskComponent

Create a task that can be used as a placeholder for #self in the plan

Returns:

See Also:



258
259
260
# File 'lib/syskit/models/data_service.rb', line 258

def create_placeholder_task
    placeholder_model.new
end

#create_proxy_taskObject

Deprecated.


248
249
250
251
# File 'lib/syskit/models/data_service.rb', line 248

def create_proxy_task
    Roby.warn_deprecated "DataService.create_proxy_task is deprecated, use .create_placeholder_task instead"
    create_placeholder_task
end

#each_fullfilled_modelObject

The set of services that this service provides



70
71
72
73
74
75
76
77
# File 'lib/syskit/models/data_service.rb', line 70

def each_fullfilled_model
    return enum_for(:each_fullfilled_model) if !block_given?
    ancestors.each do |m|
        if m.kind_of?(DataServiceModel)
            yield(m)
        end
    end
end

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

Yields:

  • (_self)

Yield Parameters:



79
80
81
82
# File 'lib/syskit/models/data_service.rb', line 79

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

#if_already_presentInstanceRequirements

Optional dependency injection

Returns an InstanceRequirements that you can use to inject optional dependencies that will be fullfilled only if there is already a matching task deployed in the plan

This can only be meaningfully used when injected for a composition's optional child



48
49
50
# File 'lib/syskit/models/data_service.rb', line 48

def if_already_present
    to_instance_requirements.if_already_present
end

#instanciate(plan, context = DependencyInjectionContext.new, options = Hash.new, &block) ⇒ TaskContext

Create a task instance that can be used in a plan to represent this service

The returned task instance is obviously an abstract one

Returns:



297
298
299
# File 'lib/syskit/models/data_service.rb', line 297

def instanciate(plan, context = DependencyInjectionContext.new, options = Hash.new, &block)
    placeholder_model.instanciate(plan, context, options, &block)
end

#placeholder?Boolean

Wether this model represents a placeholder for data services

Returns:

  • (Boolean)

See Also:



277
278
279
# File 'lib/syskit/models/data_service.rb', line 277

def placeholder?
    false
end

#placeholder_modelComponent

A component model that can be used to represent an instance of this data service in a Roby plan

Returns:

See Also:



267
268
269
270
271
272
# File 'lib/syskit/models/data_service.rb', line 267

def placeholder_model
    if @placeholder_model
        return @placeholder_model
    end
    @placeholder_model = Placeholder.for([self])
end

#port_mappings_for(service_type) ⇒ Object

Returns the set of port mappings needed between service_type and self

In other words, if one wants to link port A from service_type on a task that provides a service of type self, it should actually link to

actual_port_name = (port_mappings_for(service_type)['A'] || 'A')

Raises:

  • (ArgumentError)

    if self does not provide service_type



146
147
148
149
150
151
152
# File 'lib/syskit/models/data_service.rb', line 146

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

#pretty_print(pp) ⇒ Object



301
302
303
# File 'lib/syskit/models/data_service.rb', line 301

def pretty_print(pp)
    pp.text short_name
end

#provides(service_model, new_port_mappings = Hash.new) ⇒ Object

Declares that this data service model provides the given service model

If no port mappings are given, it will mean that the ports defined by service_model should be added to this service's interface. If port mappings are given, they define the mapping between ports in service_model and existing ports in self

Note that if both service_model and self have a port with the same name, this port needs also to be mapped explicitely by providing the 'name' => 'name' mapping in new_port_mappings



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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/syskit/models/data_service.rb', line 172

def provides(service_model, new_port_mappings = Hash.new)
    # A device can provide either a device or a data service, but
    # not a combus. Idem for data service: only other data services
    # can be provided
    if !kind_of?(service_model.class)
        raise ArgumentError, "a #{self.class.name} cannot provide a #{service_model.class.name}. If this is really what you mean, declare #{self.name} as a #{service_model.class.name} first"
    end

    if parent_models.include?(service_model)
        return
    elsif !service_model.kind_of?(DataServiceModel) # this is probably just a task service
        return super(service_model)
    end

    service_model.each_port do |p|
        if find_port(p.name) && !new_port_mappings[p.name]
            raise SpecError, "port collision: #{self} and #{service_model} both have a port named #{p.name}. If you mean to tell syskit that this is the same port, you must provide the mapping explicitely by adding '#{p.name}' => '#{p.name}' to the provides statement"
            new_port_mappings[p.name] ||= p.name
        end
    end

    new_port_mappings.each do |service_name, self_name|
        if !(source_port = service_model.find_port(service_name))
            raise SpecError, "#{service_name} is not a port of #{service_model.short_name}"
        end
        if !(target_port = find_port(self_name))
            raise SpecError, "#{self_name} is not a port of #{short_name}"
        end
        if target_port.type != source_port.type
            raise SpecError, "invalid port mapping #{service_name} => #{self_name} in #{self.short_name}.provides(#{service_model.short_name}): port #{source_port.name} on #{self.short_name} is of type #{source_port.type.name} and #{target_port.name} on #{service_model.short_name} is of type #{target_port.type.name}"
        elsif source_port.class != target_port.class
            raise SpecError, "invalid port mapping #{service_name} => #{self_name} in #{self.short_name}.provides(#{service_model.short_name}): port #{source_port.name} on #{self.short_name} is a #{target_port.class.name} and #{target_port.name} on #{service_model.short_name} is of a #{source_port.class.name}"
        end
    end

    service_model.port_mappings.each do |original_service, mappings|
        updated_mappings = Hash.new
        mappings.each do |from, to|
            updated_mappings[from] = new_port_mappings[to] || to
        end
        port_mappings[original_service] =
            Models.merge_port_mappings(port_mappings[original_service] || Hash.new, updated_mappings)
    end

    # Now, add the ports that are going to be created because of the
    # addition of the service
    service_model.each_port do |p|
        new_port_mappings[p.name] ||= p.name
    end
    port_mappings[service_model] =
        Models.merge_port_mappings(port_mappings[service_model] || Hash.new, new_port_mappings)

    # Merging the interface should never raise at this stage. It
    # should have been validated above.
    Models.merge_orogen_task_context_models(orogen_model, [service_model.orogen_model], new_port_mappings)

    # For completeness, add port mappings for ourselves
    port_mappings[self] = Hash.new
    each_port do |p|
        port_mappings[self][p.name] = p.name
    end

    super(service_model)
end

#provides?(srv) ⇒ Boolean

Tests whether self already provides another service

Parameters:

Returns:

  • (Boolean)


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

def provides?(srv)
    parent_models.include?(srv)
end

#proxy_task_modelObject

Deprecated.

use #placeholder_model instead



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

def proxy_task_model
    Roby.warn_deprecated "DataService.proxy_task_model is deprecated, use .placeholder_model instead"
    placeholder_model
end

#resolve(task) ⇒ Object

Deprecated.

use #bind instead



337
338
339
340
# File 'lib/syskit/models/data_service.rb', line 337

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

#to_component_modelObject



281
# File 'lib/syskit/models/data_service.rb', line 281

def to_component_model; self end

#to_dot(io) ⇒ Object



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/syskit/models/data_service.rb', line 342

def to_dot(io)
    id = object_id.abs
    inputs = orogen_model.all_input_ports.map(&:name)
    outputs = orogen_model.all_output_ports.map(&:name)
    label = Graphviz.dot_iolabel(constant_name, inputs, outputs)
    io << "  C#{id} [label=\"#{label}\",fontsize=15];"

    parent_models.each do |parent_m|
        parent_id = parent_m.object_id.abs
        (parent_m.each_input_port.to_a + parent_m.each_output_port.to_a).
            each do |parent_p|
            io << "  C#{parent_id}:#{parent_p.name} -> C#{id}:#{port_mappings_for(parent_m)[parent_p.name]};"
        end

    end
end

#try_bind(component) ⇒ nil, BoundDataService

Try to bind the data service model on the given task

Parameters:

Returns:



309
310
311
312
313
314
# File 'lib/syskit/models/data_service.rb', line 309

def try_bind(component)
    begin
        component.find_data_service_from_type(self)
    rescue AmbiguousServiceSelection
    end
end

#try_resolve(task) ⇒ Object

Deprecated.

use #try_bind instead



331
332
333
334
# File 'lib/syskit/models/data_service.rb', line 331

def try_resolve(task)
    Roby.warn_deprecated "#{__method__} is deprecated, use #try_bind instead"
    try_bind(task)
end