class Syskit::Actions::Profile
A representation of a set of dependency injections and definition of pre-instanciated models
Attributes
Set of known profiles
The call trace at the time of the profile definition
The definitions @return [Hash<String,InstanceRequirements>]
The DependencyInjection object that is being defined in this profile @return [DependencyInjection]
The profile name @return [String]
The set of profiles that have been used in this profile with {use_profile} @return [Array<Profile>]
Public Class Methods
# File lib/syskit/actions/profile.rb, line 538 def self.clear_model end
# File lib/syskit/actions/profile.rb, line 532 def self.deregister_profile(profile) filter_submodels do |pr| pr == profile end end
Defined here to make profiles look like models w.r.t. Roby's #clear_model implementation
It enumerates the profiles created so far
# File lib/syskit/actions/profile.rb, line 520 def self.each_submodel return enum_for(__method__) if !block_given? filter_submodels do |profile| yield(profile) false end end
Helper method that allows to iterate over the registered profiles and possibly delete some of them
@yieldparam [Profile] profile @yieldreturn [Boolean] true if this profile should be deleted,
false otherwise
# File lib/syskit/actions/profile.rb, line 505 def self.filter_submodels profiles.delete_if do |weakref| begin obj = weakref.__getobj__ yield(obj) rescue WeakRef::RefError true end end end
# File lib/syskit/actions/profile.rb, line 190 def initialize(name = nil, register: false) @name = name @permanent_model = false @definitions = Hash.new @tags = Hash.new @used_profiles = Array.new @dependency_injection = DependencyInjection.new @robot = RobotDefinition.new(self) @definition_location = caller_locations super() if register Profile.profiles << WeakRef.new(self) end end
# File lib/syskit/actions/profile.rb, line 528 def self.register_profile(profile) profiles << WeakRef.new(profile) end
Public Instance Methods
# File lib/syskit/actions/profile.rb, line 434 def all_used_profiles resolve_used_profiles(Array.new, Set.new) end
The profile's basename
# File lib/syskit/actions/profile.rb, line 115 def basename; name.gsub(/.*::/, '') end
Clears this profile of all data, leaving it blank
This is mostly used in Roby's model-reloading procedures
# File lib/syskit/actions/profile.rb, line 485 def clear_model @robot = Robot::RobotDefinition.new definitions.clear @dependency_injection = DependencyInjection.new used_profiles.clear super if defined? super if MetaRuby::Registration.accessible_by_name?(self) MetaRuby::Registration.deregister_constant(self) end Profile.deregister_profile(self) end
Create a new definition based on a given instance requirement object
@param [String] the definition name @param [#to_instance_requirements] requirements the IR object @return [Definition] the added instance requirement
# File lib/syskit/actions/profile.rb, line 345 def define(name, requirements) resolved = resolved_dependency_injection. direct_selection_for(requirements) || requirements doc = MetaRuby::DSLs.parse_documentation_block( ->(file) { Roby.app.app_file?(file) }, /^define$/) register_definition(name, resolved.to_instance_requirements, doc: doc) end
Returns the instance requirement object that represents the given definition
@param (see find_definition_by_name) @raise [ArgumentError] if the definition does not exist @see #resolved_definition
# File lib/syskit/actions/profile.rb, line 391 def definition(name) if req = find_definition_by_name(name) req else raise ArgumentError, "profile #{self.name} has no definition called #{name}" end end
Yield all actions that can be used to access this profile's definitions and devices
@yieldparam [Models::Action] action_model
# File lib/syskit/actions/profile.rb, line 545 def each_action return enum_for(__method__) if !block_given? robot.each_master_device do |dev| action_model = dev.to_action_model yield(action_model) end definitions.each do |name, req| action_model = req.to_action_model action_model.name = "#{name}_def" yield(action_model) end end
Enumerate the definitions
@yieldparam [Definition] definition @return [void]
# File lib/syskit/actions/profile.rb, line 375 def each_definition return enum_for(__method__) if !block_given? definitions.each do |name, definition| definition = definition.dup definition.name = name yield(definition) end end
Defined here to make profiles look like models w.r.t. Roby's #clear_model implementation
It does nothing
# File lib/syskit/actions/profile.rb, line 106 def each_submodel end
Enumerate the tags declared on this profile
It never enumerates tags from used profiles
@yieldparam [Models::Tag]
# File lib/syskit/actions/profile.rb, line 220 def each_tag(&block) tags.each_value(&block) end
Returns the instance requirement object that represents the given definition, if there is one
@param [String] name the definition name @return [nil,InstanceRequirements] the object matching the given
name, or nil if there is none
@see definition #resolved_definition
# File lib/syskit/actions/profile.rb, line 406 def find_definition_by_name(name) if req = definitions[name] req = req.dup req.name = name req end end
Returns the instance requirements that represent a certain device
@param [String] name @return [InstanceRequirements,nil]
# File lib/syskit/actions/profile.rb, line 581 def find_device_requirements_by_name(name) if dev = robot.devices[name] dev.to_instance_requirements.dup end end
Returns a tag by its name
@param [String] name @return [Tag,nil]
# File lib/syskit/actions/profile.rb, line 569 def find_tag(name) tags[name] end
# File lib/syskit/actions/profile.rb, line 595 def find_through_method_missing(m, args) MetaRuby::DSLs.find_through_method_missing( self, m, args, '_tag'.freeze => :find_tag, '_def'.freeze => :find_definition_by_name, '_dev'.freeze => :find_device_requirements_by_name) || super end
Get an argument from a task accessor
The common usage for this is to forward an argument from the task's parent:
define 'test', Composition.use( 'some_child' => Model.with_arguments(test: from(:parent_task).test))
# File lib/syskit/actions/profile.rb, line 152 def from(accessor) Roby::Task.from(accessor) end
Get an argument from a state object
The common usage for this is to access a state variable
define 'test', Composition.use( 'some_child' => Model.with_arguments(enabled: from_state.enabled?))
# File lib/syskit/actions/profile.rb, line 162 def from_state(state_object = State) Roby::Task.from_state(state_object) end
Test if this profile has a definition with the given name
# File lib/syskit/actions/profile.rb, line 367 def has_definition?(name) definitions.has_key?(name) end
# File lib/syskit/actions/profile.rb, line 573 def has_device?(name) !!robot.devices[name] end
Whether a tag with this name exists
# File lib/syskit/actions/profile.rb, line 561 def has_tag?(name) !!tags[name] end
# File lib/syskit/actions/profile.rb, line 587 def has_through_method_missing?(m) MetaRuby::DSLs.has_through_method_missing?( self, m, '_tag'.freeze => :has_tag?, '_def'.freeze => :has_definition?, '_dev'.freeze => :has_device?) || super end
# File lib/syskit/actions/profile.rb, line 461 def initialize_copy(old) super old.definitions.each do |name, req| definitions[name] = req.dup end end
Injects the DI information registered in this profile in the given instance requirements
@param [InstanceRequirements] req the instance requirement object @return [void]
# File lib/syskit/actions/profile.rb, line 455 def inject_di_context(req) req.push_dependency_injection(resolved_dependency_injection) super if defined? super nil end
Invalidate the cached dependency inject object
@see #resolved_dependency_injection
# File lib/syskit/actions/profile.rb, line 234 def invalidate_dependency_injection @di = nil end
Dependency injection object that signifies “select nothing for this”
This is used to override more generic selections, or to make sure that a compositions' optional child is not present
@example disable the optional 'pose' child of Camera composition
Compositions::Camera.use('pose' => nothing)
# File lib/syskit/actions/profile.rb, line 142 def nothing DependencyInjection.nothing end
Promote requirements taken from another profile to this profile
@param [Profile] profile the profile the requirements are
originating from
@param [InstanceRequirements] req the instance requirement object @param [{String=>Object}] tags selections for tags in profile,
from the tag name to the selected object
@return [InstanceRequirements] the promoted requirement object. It
might be the same than the req parameter (i.e. it is not guaranteed to be a copy)
# File lib/syskit/actions/profile.rb, line 272 def promote_requirements(profile, req, tags = Hash.new) if req.composition_model? req = req.dup tags = resolve_tag_selection(profile, tags) req.push_selections req.use(tags) end req end
@api private
Register requirements to a definition name
@return [Definition] the definition object
# File lib/syskit/actions/profile.rb, line 358 def register_definition(name, requirements, doc: nil) definition = Definition.new(self, name) definition.doc(doc) if doc definition.advanced = false definition.merge(requirements) definitions[name] = definition end
@api private
Resolves the names in the tags argument given to {#use_profile}
# File lib/syskit/actions/profile.rb, line 285 def resolve_tag_selection(profile, tags) tags.map_key do |key, _| if key.respond_to?(:to_str) profile.send("#{key.gsub(/_tag$/, '')}_tag") else key end end end
# File lib/syskit/actions/profile.rb, line 438 def resolve_used_profiles(list, set) new_profiles = used_profiles.find_all do |p, _| !set.include?(p) end list.concat(new_profiles) set |= new_profiles.map(&:first).to_set new_profiles.each do |p, _| p.resolve_used_profiles(list, set) end list end
Returns the instance requirement object that represents the given definition, with all the dependency injection information contained in this profile applied
@param [String] name the definition name @return [InstanceRequirements] the instance requirement
representing the definition
@raise [ArgumentError] if the definition does not exist @see definition
# File lib/syskit/actions/profile.rb, line 423 def resolved_definition(name) req = definition(name) result = Definition.new(self, name, resolved: true) result.merge(req) inject_di_context(result) result.name = req.name result.doc(req.doc) result end
@api private
Resolve the profile's global dependency injection object
This is an internal cache, and is updated as-needed
@see #invalidate_dependency_injection
# File lib/syskit/actions/profile.rb, line 245 def resolved_dependency_injection if !@di di = DependencyInjectionContext.new di.push(robot.to_dependency_injection) all_used_profiles.each do |prof, _| di.push(prof.dependency_injection) end di.push(dependency_injection) @di = di.current_state end @di end
@overload robot @overload robot { … }
Gets and/or modifies the robot definition of this profile
@return [Syskit::Robot::RobotDefinition] the robot definition
object
# File lib/syskit/actions/profile.rb, line 475 def robot(&block) if block_given? @robot.instance_eval(&block) end @robot end
The profile's namespace
# File lib/syskit/actions/profile.rb, line 117 def spacename; name.gsub(/::[^:]*$/, '') end
# File lib/syskit/actions/profile.rb, line 206 def tag(name, *models) tags[name] = Syskit.create_proxy_task_model_for(models, :extension => Tag, :as => "#{self}.#{name}_tag") tags[name].tag_name = name tags[name].profile = self tags[name] end
# File lib/syskit/actions/profile.rb, line 258 def to_s "profile:#{name}" end
Add some dependency injections for the definitions in this profile
# File lib/syskit/actions/profile.rb, line 225 def use(*args) invalidate_dependency_injection dependency_injection.add(*args) self end
Adds the given profile DI information and registered definitions to this one.
If a definitions has the same name in self than in the given profile, the local definition takes precedence
@param [Profile] profile @return [void]
# File lib/syskit/actions/profile.rb, line 307 def use_profile(profile, tags = Hash.new, transform_names: ->(k) { k }) invalidate_dependency_injection tags = resolve_tag_selection(profile, tags) used_profiles.push([profile, tags]) # Register the definitions, but let the user override # definitions of the given profile locally new_definitions = Array.new profile.definitions.each do |name, req| name = transform_names.call(name) req = promote_requirements(profile, req, tags) definition = register_definition(name, req, doc: req.doc) new_definitions << definition end new_definitions.concat(robot.use_robot(profile.robot)) # Now, map possible IR objects or IR-derived Action objects that # are present within the arguments new_definitions.each do |req| rebound_arguments = Hash.new req.arguments.each do |name, value| if value.respond_to?(:rebind_requirements) rebound_arguments[name] = value.rebind_requirements(self) end end req.with_arguments(**rebound_arguments) end super if defined? super new_definitions end
Whether self uses the given profile
# File lib/syskit/actions/profile.rb, line 295 def uses_profile?(profile) used_profiles.any? { |used_profile, _| used_profile == profile } end