Class: Syskit::Models::DataServiceModel
- Inherits:
-
Roby::Models::TaskServiceModel
- Object
- Roby::Models::TaskServiceModel
- Syskit::Models::DataServiceModel
- 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
Defined Under Namespace
Classes: BlockInstanciator
Instance Attribute Summary collapse
-
#orogen_model ⇒ Object
readonly
- Orocos::Spec::TaskContext
-
the object describing the data service's interface.
-
#rw port_mappings(port_mappings) ⇒ Hash<DataServiceModel,Hash<String,String>>
Port mappings from this service's parent models to the service itself.
Class Method Summary collapse
-
.new_permanent_root(parent: nil) ⇒ Object
Create a model that is root for a model hierarchy.
Instance Method Summary collapse
-
#apply_block(name = nil, &block) ⇒ Object
Applies a setup block on a service model.
- #as_plan ⇒ Object
-
#bind(component) ⇒ nil, BoundDataService
Binds the data service model on the given task.
- #clear_model ⇒ Object
-
#connected?(out_port, in_port) ⇒ Boolean
Delegated call from Port#connected?.
-
#create_placeholder_task ⇒ Component
Create a task that can be used as a placeholder for #self in the plan.
-
#create_proxy_task ⇒ Object
deprecated
Deprecated.
use #create_placeholder_task instead
-
#each_fullfilled_model ⇒ Object
The set of services that this service provides.
- #each_required_model {|_self| ... } ⇒ Object
-
#if_already_present ⇒ InstanceRequirements
Optional dependency injection.
-
#initialize(project: Roby.app.default_orogen_project) ⇒ DataServiceModel
constructor
A new instance of DataServiceModel.
-
#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.
-
#placeholder? ⇒ Boolean
Wether this model represents a placeholder for data services.
-
#placeholder_model ⇒ Component
A component model that can be used to represent an instance of this data service in a Roby plan.
-
#port_mappings_for(service_type) ⇒ Object
Returns the set of port mappings needed between
service_type
andself
. - #pretty_print(pp) ⇒ Object
-
#provides(service_model, new_port_mappings = Hash.new) ⇒ Object
Declares that this data service model provides the given service model.
-
#provides?(srv) ⇒ Boolean
Tests whether self already provides another service.
-
#proxy_task_model ⇒ Object
deprecated
Deprecated.
use #placeholder_model instead
-
#resolve(task) ⇒ Object
deprecated
Deprecated.
use #bind instead
- #to_component_model ⇒ Object
- #to_dot(io) ⇒ Object
-
#try_bind(component) ⇒ nil, BoundDataService
Try to bind the data service model on the given task.
-
#try_resolve(task) ⇒ Object
deprecated
Deprecated.
use #try_bind instead
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_model ⇒ Object (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]
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_plan ⇒ Object
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
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_model ⇒ Object
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
287 288 289 |
# File 'lib/syskit/models/data_service.rb', line 287 def connected?(out_port, in_port) false end |
#create_placeholder_task ⇒ Component
Create a task that can be used as a placeholder for #self in the plan
258 259 260 |
# File 'lib/syskit/models/data_service.rb', line 258 def create_placeholder_task placeholder_model.new end |
#create_proxy_task ⇒ Object
use #create_placeholder_task instead
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_model ⇒ Object
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
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_present ⇒ InstanceRequirements
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
297 298 299 |
# File 'lib/syskit/models/data_service.rb', line 297 def instanciate(plan, context = DependencyInjectionContext.new, = Hash.new, &block) placeholder_model.instanciate(plan, context, , &block) end |
#placeholder? ⇒ Boolean
Wether this model represents a placeholder for data services
277 278 279 |
# File 'lib/syskit/models/data_service.rb', line 277 def placeholder? false end |
#placeholder_model ⇒ Component
A component model that can be used to represent an instance of this data service in a Roby plan
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')
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
157 158 159 |
# File 'lib/syskit/models/data_service.rb', line 157 def provides?(srv) parent_models.include?(srv) end |
#proxy_task_model ⇒ Object
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
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_model ⇒ Object
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
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 |