Class: Syskit::Actions::Profile

Inherits:
Object
  • Object
show all
Includes:
MetaRuby::DSLs::FindThroughMethodMissing, Roby::DRoby::Identifiable, Roby::DRoby::V5::DRobyConstant::Dump
Defined in:
lib/syskit/actions/profile.rb

Overview

A representation of a set of dependency injections and definition of pre-instanciated models

Defined Under Namespace

Modules: Models, Tag Classes: Definition, ProfileInstanceRequirements, RobotDefinition

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name = nil, register: false) ⇒ Profile

Returns a new instance of Profile



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/syskit/actions/profile.rb', line 191

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
    @deployment_group = Syskit::Models::DeploymentGroup.new
    @deployment_groups = Hash.new
    super()

    if register
        Profile.profiles << WeakRef.new(self)
    end
end

Class Attribute Details

.profilesObject (readonly)

Set of known profiles



10
11
12
# File 'lib/syskit/actions/profile.rb', line 10

def profiles
  @profiles
end

Instance Attribute Details

#definition_locationObject (readonly)

The call trace at the time of the profile definition



104
105
106
# File 'lib/syskit/actions/profile.rb', line 104

def definition_location
  @definition_location
end

#definitionsHash<String,InstanceRequirements> (readonly)

The definitions

Returns:



114
115
116
# File 'lib/syskit/actions/profile.rb', line 114

def definitions
  @definitions
end

#dependency_injectionDependencyInjection (readonly)

The DependencyInjection object that is being defined in this profile

Returns:



125
126
127
# File 'lib/syskit/actions/profile.rb', line 125

def dependency_injection
  @dependency_injection
end

#deployment_groupModels::DeploymentGroup (readonly)

The deployments available on this profile



129
130
131
# File 'lib/syskit/actions/profile.rb', line 129

def deployment_group
  @deployment_group
end

#deployment_groupsObject (readonly)

A set of deployment groups that can be used to narrow deployments on tasks



132
133
134
# File 'lib/syskit/actions/profile.rb', line 132

def deployment_groups
  @deployment_groups
end

#nameString (readonly)

The profile name

Returns:

  • (String)


107
108
109
# File 'lib/syskit/actions/profile.rb', line 107

def name
  @name
end

#tagsHash<String,InstanceRequirements> (readonly)

The tags

Returns:



117
118
119
# File 'lib/syskit/actions/profile.rb', line 117

def tags
  @tags
end

#used_profilesArray<Profile> (readonly)

The set of profiles that have been used in this profile with #use_profile

Returns:



121
122
123
# File 'lib/syskit/actions/profile.rb', line 121

def used_profiles
  @used_profiles
end

Class Method Details

.clear_modelObject



656
657
# File 'lib/syskit/actions/profile.rb', line 656

def self.clear_model
end

.deregister_profile(profile) ⇒ Object



650
651
652
653
654
# File 'lib/syskit/actions/profile.rb', line 650

def self.deregister_profile(profile)
    filter_submodels do |pr|
        pr == profile
    end
end

.each_submodelObject

Defined here to make profiles look like models w.r.t. Roby's clear_model implementation

It enumerates the profiles created so far



638
639
640
641
642
643
644
# File 'lib/syskit/actions/profile.rb', line 638

def self.each_submodel
    return enum_for(__method__) if !block_given?
    filter_submodels do |profile|
        yield(profile)
        false
    end
end

.filter_submodels {|profile| ... } ⇒ Object

Helper method that allows to iterate over the registered profiles and possibly delete some of them

Yield Parameters:

Yield Returns:

  • (Boolean)

    true if this profile should be deleted, false otherwise



623
624
625
626
627
628
629
630
631
632
# File 'lib/syskit/actions/profile.rb', line 623

def self.filter_submodels
    profiles.delete_if do |weakref|
        begin
            obj = weakref.__getobj__
            yield(obj)
        rescue WeakRef::RefError
            true
        end
    end
end

.register_profile(profile) ⇒ Object



646
647
648
# File 'lib/syskit/actions/profile.rb', line 646

def self.register_profile(profile)
    profiles << WeakRef.new(profile)
end

Instance Method Details

#all_used_profilesObject

Returns all profiles that are used by self



546
547
548
# File 'lib/syskit/actions/profile.rb', line 546

def all_used_profiles
    resolve_used_profiles(Array.new, Set.new)
end

#basenameObject

The profile's basename



109
# File 'lib/syskit/actions/profile.rb', line 109

def basename; name.gsub(/.*::/, '') end

#clear_modelObject

Clears this profile of all data, leaving it blank

This is mostly used in Roby's model-reloading procedures



601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
# File 'lib/syskit/actions/profile.rb', line 601

def clear_model
    @robot = Robot::RobotDefinition.new
    definitions.clear
    @dependency_injection = DependencyInjection.new
    @deployment_groups = Hash.new
    @deployment_group = Syskit::Models::DeploymentGroup.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

#define(name, requirements) ⇒ Definition

Create a new definition based on a given instance requirement object

Parameters:

  • the (String)

    definition name

  • requirements (#to_instance_requirements)

    the IR object

Returns:



359
360
361
362
363
364
365
# File 'lib/syskit/actions/profile.rb', line 359

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

#define_deployment_group(name, &block) ⇒ Object

Create a deployment group to specify definition deployments

This only defines the group, but does not declare that the profile should use it. To use a group in a profile, do the following:

Examples:

create_deployment_group 'left_arm' do
    use_deployments_from 'left_arm'
end
use_group left_arm_deployment_group


519
520
521
522
523
# File 'lib/syskit/actions/profile.rb', line 519

def define_deployment_group(name, &block)
    group = Syskit::Models::DeploymentGroup.new
    group.instance_eval(&block)
    deployment_groups[name] = group
end

#definition(name) ⇒ Object

Returns the instance requirement object that represents the given definition

Parameters:

  • name (String)

    the definition name

Raises:

  • (ArgumentError)

    if the definition does not exist

See Also:



405
406
407
408
409
410
411
# File 'lib/syskit/actions/profile.rb', line 405

def definition(name)
    if req = find_definition_by_name(name)
        req
    else
        raise ArgumentError, "profile #{self.name} has no definition called #{name}"
    end
end

#each_action {|action_model| ... } ⇒ Object

Yield all actions that can be used to access this profile's definitions and devices

Yield Parameters:



663
664
665
666
667
668
669
670
671
672
673
674
675
676
# File 'lib/syskit/actions/profile.rb', line 663

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

#each_definition {|definition| ... } ⇒ Object

Enumerate all definitions available on this profile

Yield Parameters:

See Also:



389
390
391
392
393
394
395
396
397
# File 'lib/syskit/actions/profile.rb', line 389

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

#each_resolved_definition {|definition| ... } ⇒ Object

Enumerate all definitions on this profile and resolve them

Yield Parameters:



466
467
468
469
470
471
# File 'lib/syskit/actions/profile.rb', line 466

def each_resolved_definition
    return enum_for(__method__) if !block_given?
    definitions.each_value do |req|
        yield(req.resolve)
    end
end

#each_submodelObject

Defined here to make profiles look like models w.r.t. Roby's clear_model implementation

It does nothing



100
101
# File 'lib/syskit/actions/profile.rb', line 100

def each_submodel
end

#each_tag {|| ... } ⇒ Object

Enumerate the tags declared on this profile

It never enumerates tags from used profiles

Yield Parameters:



223
224
225
# File 'lib/syskit/actions/profile.rb', line 223

def each_tag(&block)
    tags.each_value(&block)
end

#each_used_profile {|profile| ... } ⇒ Object

Enumerate the profiles that have directly been imported in self

Yield Parameters:



305
306
307
308
309
310
# File 'lib/syskit/actions/profile.rb', line 305

def each_used_profile(&block)
    return enum_for(__method__) unless block_given?
    used_profiles.each do |profile, _tags|
        yield(profile)
    end
end

#find_definition_by_name(name) ⇒ nil, InstanceRequirements

Returns the instance requirement object that represents the given definition, if there is one

Parameters:

  • name (String)

    the definition name

Returns:

See Also:



420
421
422
423
424
# File 'lib/syskit/actions/profile.rb', line 420

def find_definition_by_name(name)
    if req = definitions[name]
        req.dup
    end
end

#find_deployed_task_by_name(task_name) ⇒ Object



474
475
476
# File 'lib/syskit/actions/profile.rb', line 474

def find_deployed_task_by_name(task_name)
    deployment_group.find_deployed_task_by_name(task_name)
end

#find_deployment_group_by_name(name) ⇒ Object

Returns a deployment group defined with #create_deployment_group



531
532
533
# File 'lib/syskit/actions/profile.rb', line 531

def find_deployment_group_by_name(name)
    deployment_groups[name]
end

#find_device_requirements_by_name(name) ⇒ InstanceRequirements?

Returns the instance requirements that represent a certain device

Parameters:

  • name (String)

Returns:



536
537
538
# File 'lib/syskit/actions/profile.rb', line 536

def find_device_requirements_by_name(device_name)
    robot.devices[device_name].to_instance_requirements.dup
end

#find_tag(name) ⇒ Tag?

Returns a tag by its name

Parameters:

  • name (String)

Returns:



687
688
689
# File 'lib/syskit/actions/profile.rb', line 687

def find_tag(name)
    tags[name]
end

#find_tag_by_name(name) ⇒ Object

Returns the tag object for a given name



541
542
543
# File 'lib/syskit/actions/profile.rb', line 541

def find_tag_by_name(name)
    tags[name]
end

#find_through_method_missing(m, args) ⇒ Object



715
716
717
718
719
720
721
722
723
# File 'lib/syskit/actions/profile.rb', line 715

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,
        '_task' => :find_deployed_task_by_name,
        '_deployment_group' => :find_deployment_group_by_name) || super
end

#from(accessor) ⇒ Object

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))


153
154
155
# File 'lib/syskit/actions/profile.rb', line 153

def from(accessor)
    Roby::Task.from(accessor)
end

#from_state(state_object = State) ⇒ Object

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?))


163
164
165
# File 'lib/syskit/actions/profile.rb', line 163

def from_state(state_object = State)
    Roby::Task.from_state(state_object)
end

#has_definition?(name) ⇒ Boolean

Tests whether self has a definition with a given name

Returns:

  • (Boolean)


381
382
383
# File 'lib/syskit/actions/profile.rb', line 381

def has_definition?(name)
    definitions.has_key?(name)
end

#has_deployed_task?(task_name) ⇒ Boolean

Returns:

  • (Boolean)


479
480
481
# File 'lib/syskit/actions/profile.rb', line 479

def has_deployed_task?(task_name)
    deployment_group.has_deployed_task?(task_name)
end

#has_deployment_group?(name) ⇒ Boolean

Whether this profile has a group with the given name

Returns:

  • (Boolean)


526
527
528
# File 'lib/syskit/actions/profile.rb', line 526

def has_deployment_group?(name)
    deployment_groups.has_key?(name)
end

#has_device?(name) ⇒ Boolean

Returns:

  • (Boolean)


691
692
693
# File 'lib/syskit/actions/profile.rb', line 691

def has_device?(name)
    !!robot.devices[name]
end

#has_tag?(name) ⇒ Boolean

Whether a tag with this name exists

Returns:

  • (Boolean)


679
680
681
# File 'lib/syskit/actions/profile.rb', line 679

def has_tag?(name)
    !!tags[name]
end

#has_through_method_missing?(m) ⇒ Boolean

Returns:

  • (Boolean)


705
706
707
708
709
710
711
712
713
# File 'lib/syskit/actions/profile.rb', line 705

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?,
        '_task'.freeze => :has_deployed_task?,
        '_deployment_group'.freeze => :has_deployment_group?) || super
end

#initialize_copy(old) ⇒ Object



577
578
579
580
581
582
# File 'lib/syskit/actions/profile.rb', line 577

def initialize_copy(old)
    super
    old.definitions.each do |name, req|
        definitions[name] = req.dup
    end
end

#inject_di_context(req) ⇒ void

This method returns an undefined value.

Injects the DI information registered in this profile in the given instance requirements

Parameters:



570
571
572
573
574
575
# File 'lib/syskit/actions/profile.rb', line 570

def inject_di_context(req)
    req.deployment_group.use_group(deployment_group)
    req.push_dependency_injection(resolved_dependency_injection)
    super if defined? super
    nil
end

#invalidate_dependency_injectionObject

Invalidate the cached dependency inject object



237
238
239
# File 'lib/syskit/actions/profile.rb', line 237

def invalidate_dependency_injection
    @di = nil
end

#nothingObject

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

Examples:

disable the optional 'pose' child of Camera composition

Compositions::Camera.use('pose' => nothing)


143
144
145
# File 'lib/syskit/actions/profile.rb', line 143

def nothing
    DependencyInjection.nothing
end

#permanent_model=(value) ⇒ Object

Whether this profile should be kept across app setup/cleanup cycles and during model reloading



94
# File 'lib/syskit/actions/profile.rb', line 94

attr_predicate :permanent_model?, true

#permanent_model?Boolean

Whether this profile should be kept across app setup/cleanup cycles and during model reloading

Returns:

  • (Boolean)


94
# File 'lib/syskit/actions/profile.rb', line 94

attr_predicate :permanent_model?, true

#promote_requirements(profile, req, tags = Hash.new) ⇒ InstanceRequirements

Promote requirements taken from another profile to this profile

Parameters:

  • profile (Profile)

    the profile the requirements are originating from

  • req (InstanceRequirements)

    the instance requirement object

  • tags ({String=>Object}) (defaults to: Hash.new)

    selections for tags in profile, from the tag name to the selected object

Returns:

  • (InstanceRequirements)

    the promoted requirement object. It might be the same than the req parameter (i.e. it is not guaranteed to be a copy)



275
276
277
278
279
280
281
282
283
# File 'lib/syskit/actions/profile.rb', line 275

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

#register_definition(name, requirements, doc: nil) ⇒ Definition

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Register requirements to a definition name

Returns:



372
373
374
375
376
377
378
# File 'lib/syskit/actions/profile.rb', line 372

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

#resolve_tag_selection(profile, tags) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Resolves the names in the tags argument given to #use_profile



288
289
290
291
292
293
294
295
# File 'lib/syskit/actions/profile.rb', line 288

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

#resolve_used_profiles(list, set) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Recursively lists all profiles that are used by self



553
554
555
556
557
558
559
560
561
562
563
# File 'lib/syskit/actions/profile.rb', line 553

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

#resolved_definition(name) ⇒ InstanceRequirements

Returns the instance requirement object that represents the given definition, with all the dependency injection information contained in this profile applied

Parameters:

  • name (String)

    the definition name

Returns:

Raises:

  • (ArgumentError)

    if the definition does not exist

See Also:



440
441
442
443
444
445
446
447
# File 'lib/syskit/actions/profile.rb', line 440

def resolved_definition(name)
    req = definitions[name]
    unless req
        raise ArgumentError,
            "profile #{self.name} has no definition called #{name}"
    end
    req.resolve
end

#resolved_dependency_injectionObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Resolve the profile's global dependency injection object

This is an internal cache, and is updated as-needed



248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/syskit/actions/profile.rb', line 248

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

#robotSyskit::Robot::RobotDefinition #robot({ ... }) ⇒ Syskit::Robot::RobotDefinition

Gets and/or modifies the robot definition of this profile

Returns:



591
592
593
594
595
596
# File 'lib/syskit/actions/profile.rb', line 591

def robot(&block)
    if block_given?
        @robot.instance_eval(&block)
    end
    @robot
end

#spacenameObject

The profile's namespace



111
# File 'lib/syskit/actions/profile.rb', line 111

def spacename; name.gsub(/::[^:]*$/, '') end

#tag(name, *models) ⇒ Object



209
210
211
212
213
214
215
216
# File 'lib/syskit/actions/profile.rb', line 209

def tag(name, *models)
    tags[name] = Models::Tag.create_for(
        models,
        as: "#{self}.#{name}_tag")
    tags[name].tag_name = name
    tags[name].profile = self
    tags[name]
end

#to_sObject



261
262
263
# File 'lib/syskit/actions/profile.rb', line 261

def to_s
    "profile:#{name}"
end

#use(*args) ⇒ Object

Add some dependency injections for the definitions in this profile



228
229
230
231
232
# File 'lib/syskit/actions/profile.rb', line 228

def use(*args)
    invalidate_dependency_injection
    dependency_injection.add(*args)
    self
end

#use_deployment(*names, on: 'localhost', loader: deployment_group.loader, **run_options) ⇒ Object



499
500
501
# File 'lib/syskit/actions/profile.rb', line 499

def use_deployment(*names, on: 'localhost', loader: deployment_group.loader, **run_options)
    deployment_group.use_deployment(*names, on: on, loader: loader, **run_options)
end

#use_deployments_from(project_name, loader: deployment_group.loader, **use_options) ⇒ Object



504
505
506
# File 'lib/syskit/actions/profile.rb', line 504

def use_deployments_from(project_name, loader: deployment_group.loader, **use_options)
    deployment_group.use_deployments_from(project_name, loader: loader, **use_options)
end

#use_group(deployment_group) ⇒ Object



484
485
486
# File 'lib/syskit/actions/profile.rb', line 484

def use_group(deployment_group)
    deployment_group.use_group(deployment_group)
end

#use_profile(profile, tags = Hash.new, transform_names: ->(k) { k }) ⇒ void

This method returns an undefined value.

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

Parameters:



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/syskit/actions/profile.rb', line 320

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])
    deployment_group.use_group(profile.deployment_group)

    # 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

#use_ruby_tasks(mappings, on: 'ruby_tasks') ⇒ Object



489
490
491
# File 'lib/syskit/actions/profile.rb', line 489

def use_ruby_tasks(mappings, on: 'ruby_tasks')
    deployment_group.use_ruby_tasks(mappings, on: on)
end

#uses_profile?(profile) ⇒ Boolean

Whether self uses the given profile

Returns:

  • (Boolean)


298
299
300
# File 'lib/syskit/actions/profile.rb', line 298

def uses_profile?(profile)
    used_profiles.any? { |used_profile, _tags| used_profile == profile }
end