Class: Syskit::InstanceRequirements

Inherits:
Object
  • Object
show all
Extended by:
Logger::Hierarchy
Includes:
Logger::Hierarchy, MetaRuby::DSLs::FindThroughMethodMissing, Roby::DRoby::Unmarshallable
Defined in:
lib/syskit/instance_requirements.rb

Overview

Generic representation of a configured component instance

Defined Under Namespace

Modules: Auto Classes: CoordinationTask, Dynamics, TemplatePlan

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(models = []) ⇒ InstanceRequirements

Returns a new instance of InstanceRequirements



103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/syskit/instance_requirements.rb', line 103

def initialize(models = [])
    @base_model = Models::Placeholder.for(models)
    @model = base_model
    @arguments = Hash.new
    @selections = DependencyInjection.new
    @pushed_selections = DependencyInjection.new
    @context_selections = DependencyInjection.new
    @deployment_hints = Set.new
    @specialization_hints = Set.new
    @dynamics = Dynamics.new(NetworkGeneration::PortDynamics.new('Requirements'), Hash.new)
    @can_use_template = true
    @deployment_group = Models::DeploymentGroup.new
end

Instance Attribute Details

#argumentsObject (readonly)

Required arguments on the final task



19
20
21
# File 'lib/syskit/instance_requirements.rb', line 19

def arguments
  @arguments
end

#base_modelObject (readonly)

The component model specified by #add



17
18
19
# File 'lib/syskit/instance_requirements.rb', line 17

def base_model
  @base_model
end

#deployment_groupModels::DeploymentGroup (readonly)

The deployments that should be used for self



36
37
38
# File 'lib/syskit/instance_requirements.rb', line 36

def deployment_group
  @deployment_group
end

#deployment_hintsObject (readonly)

A set of hints for deployment disambiguation



41
42
43
# File 'lib/syskit/instance_requirements.rb', line 41

def deployment_hints
  @deployment_hints
end

#dynamicsDynamics (readonly)

Custom specification of task dynamics. This is not used as requirements, but more exactly as hints to the dataflow dynamics computations

Returns:



52
53
54
# File 'lib/syskit/instance_requirements.rb', line 52

def dynamics
  @dynamics
end

#modelObject (readonly)

The component model narrowed down from #base_model using using_spec



15
16
17
# File 'lib/syskit/instance_requirements.rb', line 15

def model
  @model
end

#nameString?

This requirement's name, mostly for debugging / display reasons

Returns:

  • (String, nil)


12
13
14
# File 'lib/syskit/instance_requirements.rb', line 12

def name
  @name
end

#required_hostObject (readonly)

Returns the value of attribute required_host



821
822
823
# File 'lib/syskit/instance_requirements.rb', line 821

def required_host
  @required_host
end

#specialization_hintsObject (readonly)

A set of hints for specialization disambiguation



46
47
48
# File 'lib/syskit/instance_requirements.rb', line 46

def specialization_hints
  @specialization_hints
end

#templatenil, Roby::TemplatePlan (readonly)

The cached instanciated reqirement

Returns:

  • (nil, Roby::TemplatePlan)


57
58
59
# File 'lib/syskit/instance_requirements.rb', line 57

def template
  @template
end

Class Method Details

.from_object(object, original_requirements = Syskit::InstanceRequirements.new) ⇒ Object



143
144
145
146
147
148
149
150
151
# File 'lib/syskit/instance_requirements.rb', line 143

def self.from_object(object, original_requirements = Syskit::InstanceRequirements.new)
    if object.plain?
        object = object.dup
        object.merge(original_requirements, keep_abstract: true)
        object
    else
        object
    end
end

Instance Method Details

#==(obj) ⇒ Object



506
507
508
# File 'lib/syskit/instance_requirements.rb', line 506

def ==(obj)
    eql?(obj)
end

#abstractself

The instanciated task should be marked as abstract

Returns:

  • (self)


640
641
642
643
644
# File 'lib/syskit/instance_requirements.rb', line 640

def abstract
    invalidate_template
    @abstract = true
    self
end

#abstract?Boolean

Tests whether the instanciated task will be marked as abstract or not

Returns:

  • (Boolean)


660
661
662
# File 'lib/syskit/instance_requirements.rb', line 660

def abstract?
    !!@abstract
end

#add_models(new_models) ⇒ Object

Add new models to the set of required ones



154
155
156
157
158
# File 'lib/syskit/instance_requirements.rb', line 154

def add_models(new_models)
    invalidate_template
    @base_model = base_model.merge(Models::Placeholder.for(new_models))
    narrow_model
end

#add_port_period(port_name, period, sample_count = 1) ⇒ self

Declare the period of a port

When computing dataflow, this overrides propagated values

Parameters:

  • port_name (String)
  • period (Float)

    the period in seconds

  • sample_count (Integer) (defaults to: 1)

    how many samples are written each time

Returns:

  • (self)


1122
1123
1124
1125
1126
1127
1128
# File 'lib/syskit/instance_requirements.rb', line 1122

def add_port_period(port_name, period, sample_count = 1)
    if !model.has_port?(port_name)
        raise ArgumentError, "#{model} has not port called #{port_name}"
    end
    dynamics.add_port_period(port_name, period, sample_count)
    self
end

#as(models) ⇒ Object



365
366
367
368
369
370
371
372
373
374
# File 'lib/syskit/instance_requirements.rb', line 365

def as(models)
    if service
        result = to_component_model
        result.select_service(service.as(models))
        result
    else
        models = Array(models) if !models.respond_to?(:each)
        Models::FacetedAccess.new(self, Models::Placeholder.for(models))
    end
end

#as_plan(**arguments) ⇒ Syskit::Component

Returns a plan pattern (main task and planning task) that will deploy self

The main task is an instance of #model and the planning task an instance of Syskit::InstanceRequirementsTask.

Returns:



982
983
984
985
986
987
988
989
990
# File 'lib/syskit/instance_requirements.rb', line 982

def as_plan(**arguments)
    if arguments.empty?
        req = self
    else
        req = dup
        req.with_arguments(**arguments)
    end
    Syskit::InstanceRequirementsTask.subplan(req, **arguments)
end

#as_real_modelObject



376
377
378
379
380
# File 'lib/syskit/instance_requirements.rb', line 376

def as_real_model
    result = dup
    result.as_real_model!
    result
end

#as_real_model!Object



382
383
384
385
386
# File 'lib/syskit/instance_requirements.rb', line 382

def as_real_model!
    @base_model = base_model.as_real_model
    @model = model.as_real_model
    self
end

#bind(task) ⇒ Object

Maps the given task to the underlying model



221
222
223
# File 'lib/syskit/instance_requirements.rb', line 221

def bind(task)
    model.bind(task)
end

#can_use_template=(value) ⇒ Object

Whether instanciating this object can use a template plan

Template plans cannot be used if the dependency injection explicitely refers to a task



63
# File 'lib/syskit/instance_requirements.rb', line 63

attr_predicate :can_use_template?, true

#can_use_template?Boolean

Whether instanciating this object can use a template plan

Template plans cannot be used if the dependency injection explicitely refers to a task

Returns:

  • (Boolean)


63
# File 'lib/syskit/instance_requirements.rb', line 63

attr_predicate :can_use_template?, true

#component_modelModel<Component>

The component model that is required through this object

Returns:



235
236
237
238
239
240
241
# File 'lib/syskit/instance_requirements.rb', line 235

def component_model
    model = self.model.to_component_model
    if model.placeholder?
        return model.proxied_component_model
    else return model
    end
end

#component_model?Boolean

Tests if these requirements explicitly point to a component model

Returns:

  • (Boolean)


1087
1088
1089
1090
1091
1092
# File 'lib/syskit/instance_requirements.rb', line 1087

def component_model?
    if model.placeholder?
        model.proxied_component_model != Syskit::Component
    else true
    end
end

#composition_model?Boolean

Tests if these requirements explicitly point to a composition model

Returns:

  • (Boolean)


1095
1096
1097
# File 'lib/syskit/instance_requirements.rb', line 1095

def composition_model?
    base_model.fullfills?(Syskit::Composition)
end

#compute_templateObject



883
884
885
886
887
888
889
890
891
892
893
# File 'lib/syskit/instance_requirements.rb', line 883

def compute_template
    base_requirements = dup.with_no_arguments
    template = TemplatePlan.new
    template.root_task = base_requirements.
        instanciate(template, use_template: false).
        to_task
    merge_solver = NetworkGeneration::MergeSolver.new(template)
    merge_solver.merge_identical_tasks
    template.root_task = merge_solver.replacement_for(template.root_task)
    @template = template
end

#create_proxy_taskObject

Returns a task that can be used in the plan as a placeholder for this instance requirements

The returned task is always marked as abstract



835
836
837
838
839
840
# File 'lib/syskit/instance_requirements.rb', line 835

def create_proxy_task
    task = component_model.new(**@arguments)
    task.required_host = self.required_host
    task.abstract = true
    task
end

#do_copy(old) ⇒ Object

HACK: allows CompositionChild#to_instance_requirements to return a HACK: problem InstanceRequirements object HACK: HACK: the proper fix would be to make the IR an attribute of HACK: CompositionChild instead of a superclass



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/syskit/instance_requirements.rb', line 122

def do_copy(old)
    @di = nil
    @template = old.template
    @abstract = old.abstract?
    @model = old.model
    @base_model = old.base_model
    @arguments = old.arguments.dup
    @selections = old.selections.dup
    @pushed_selections = old.pushed_selections.dup
    @deployment_hints = old.deployment_hints.dup
    @specialization_hints = old.specialization_hints.dup
    @context_selections = old.context_selections.dup
    @deployment_group = old.deployment_group.dup
    @can_use_template = old.can_use_template?
end

#each_childObject



1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
# File 'lib/syskit/instance_requirements.rb', line 1033

def each_child
    return enum_for(__method__) if !block_given?
    if !composition_model?
        raise RuntimeError, "cannot call #each_child on #{self} as it does not represent a composition model"
    end
    resolved_di = resolved_dependency_injection
    model.each_child do |child_name, _|
        selected_child, _ = model.find_child_model_and_task(
            child_name, resolved_di)
        yield(child_name, selected_child)
    end
end

#each_fullfilled_model(&block) ⇒ Object



964
965
966
# File 'lib/syskit/instance_requirements.rb', line 964

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

#each_input_portObject



439
440
441
442
443
444
# File 'lib/syskit/instance_requirements.rb', line 439

def each_input_port
    return enum_for(:each_input_port) if !block_given?
    model.each_input_port do |p|
        yield(p.attach(self))
    end
end

#each_output_portObject



446
447
448
449
450
451
# File 'lib/syskit/instance_requirements.rb', line 446

def each_output_port
    return enum_for(:each_output_port) if !block_given?
    model.each_output_port do |p|
        yield(p.attach(self))
    end
end

#each_port(&block) ⇒ Object

Enumerates all of this component's ports



433
434
435
436
437
# File 'lib/syskit/instance_requirements.rb', line 433

def each_port(&block)
    return enum_for(:each_port) if !block_given?
    each_output_port(&block)
    each_input_port(&block)
end

#each_required_modelObject



1079
1080
1081
1082
1083
1084
# File 'lib/syskit/instance_requirements.rb', line 1079

def each_required_model
    return enum_for(:each_required_model) if !block_given?
    model.each_required_model do |m|
        yield(m)
    end
end

#each_required_service_modelObject



1072
1073
1074
1075
1076
1077
# File 'lib/syskit/instance_requirements.rb', line 1072

def each_required_service_model
    return enum_for(:each_required_service_model) if !block_given?
    model.each_required_model do |m|
        yield(m) if m.kind_of?(Syskit::Models::DataServiceModel)
    end
end

#eql?(obj) ⇒ Boolean

Returns:

  • (Boolean)


499
500
501
502
503
504
505
# File 'lib/syskit/instance_requirements.rb', line 499

def eql?(obj)
    obj.kind_of?(InstanceRequirements) &&
        obj.base_model == base_model &&
        obj.selections == selections &&
        obj.pushed_selections == pushed_selections &&
        obj.arguments == arguments
end

#find_all_data_services_from_type(service_type) ⇒ Object

Finds all the data services that match the given service type



351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/syskit/instance_requirements.rb', line 351

def find_all_data_services_from_type(service_type)
    if model.respond_to?(:find_all_data_services_from_type)
        model.find_all_data_services_from_type(service_type).map do |service|
            result = dup
            result.select_service(service)
            result
        end
    elsif model.fullfills?(service_type)
        [self]
    else
        []
    end
end

#find_child(name) ⇒ Object

Finds the composition's child by name

Raises:

  • (ArgumentError)

    if this InstanceRequirements object does not refer to a composition



396
397
398
399
400
401
402
403
# File 'lib/syskit/instance_requirements.rb', line 396

def find_child(name)
    if !model.respond_to?(:find_child)
        raise ArgumentError, "#{self} is not a composition"
    end
    if child = model.find_child(name)
        return child.attach(self)
    end
end

#find_data_service(service_name) ⇒ InstanceRequirements?

Finds a data service by name

Parameters:

  • service_name (String)

    the service name

Returns:

  • (InstanceRequirements, nil)

    the requirements with the requested data service selected or nil if there are no service with the requested name



323
324
325
326
327
328
329
# File 'lib/syskit/instance_requirements.rb', line 323

def find_data_service(service_name)
    if service = model.find_data_service(service_name)
        result = dup
        result.select_service(service)
        result
    end
end

#find_data_service_from_type(service_type) ⇒ InstanceRequirements?

Finds the only data service that matches the given service type

Parameters:

Returns:

  • (InstanceRequirements, nil)

    this instance requirement object with the relevant service selected; nil if there are no matches

Raises:



338
339
340
341
342
343
344
345
346
347
348
# File 'lib/syskit/instance_requirements.rb', line 338

def find_data_service_from_type(service_type)
    if model.respond_to?(:find_data_service_from_type)
        if service = model.find_data_service_from_type(service_type)
            result = dup
            result.select_service(service)
            result
        end
    elsif model.fullfills?(service_type)
        self
    end
end

#find_input_port(name) ⇒ Object



405
406
407
408
409
# File 'lib/syskit/instance_requirements.rb', line 405

def find_input_port(name)
    if p = model.find_input_port(name)
        p.attach(self)
    end
end

#find_output_port(name) ⇒ Object



411
412
413
414
415
# File 'lib/syskit/instance_requirements.rb', line 411

def find_output_port(name)
    if p = model.find_output_port(name)
        p.attach(self)
    end
end

#find_port(name) ⇒ Object



421
422
423
# File 'lib/syskit/instance_requirements.rb', line 421

def find_port(name)
    find_input_port(name) || find_output_port(name)
end

#find_port_dynamics(port_name) ⇒ NetworkGeneration::PortDynamics?

Returns the port dynamics defined for a given port, or nil

Parameters:

  • port_name (String)

Returns:

See Also:



1109
1110
1111
# File 'lib/syskit/instance_requirements.rb', line 1109

def find_port_dynamics(port_name)
    dynamics.find_port_dynamics(port_name.to_s)
end

#find_through_method_missing(m, args) ⇒ Object



1054
1055
1056
1057
1058
1059
1060
# File 'lib/syskit/instance_requirements.rb', line 1054

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

#fullfilled_modelObject



968
969
970
971
972
973
# File 'lib/syskit/instance_requirements.rb', line 968

def fullfilled_model
    fullfilled = model.fullfilled_model
    task_model = fullfilled.find { |m| m <= Roby::Task } || Syskit::Component
    tags = fullfilled.find_all { |m| m.kind_of?(Syskit::Models::DataServiceModel) || m.kind_of?(Roby::Models::TaskServiceModel) }
    [task_model.concrete_model, tags, @arguments.dup]
end

#fullfills?(required_models) ⇒ Boolean

Return true if these requirements provide all of the required models

Returns:

  • (Boolean)


454
455
456
# File 'lib/syskit/instance_requirements.rb', line 454

def fullfills?(required_models)
    model.fullfills?(required_models)
end

#has_child?(name) ⇒ Boolean

Returns:

  • (Boolean)


388
389
390
# File 'lib/syskit/instance_requirements.rb', line 388

def has_child?(name)
    model.has_child?(name)
end

#has_data_service?(service_name) ⇒ Boolean

Returns:

  • (Boolean)


313
314
315
# File 'lib/syskit/instance_requirements.rb', line 313

def has_data_service?(service_name)
    !!model.find_data_service(service_name)
end

#has_port?(name) ⇒ Boolean

Returns:

  • (Boolean)


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

def has_port?(name)
    model.has_port?(name)
end

#has_template?Boolean

Returns:

  • (Boolean)


904
905
906
# File 'lib/syskit/instance_requirements.rb', line 904

def has_template?
    !!@template
end

#has_through_method_missing?(m) ⇒ Boolean

Returns:

  • (Boolean)


1046
1047
1048
1049
1050
1051
1052
# File 'lib/syskit/instance_requirements.rb', line 1046

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

#hashObject



498
# File 'lib/syskit/instance_requirements.rb', line 498

def hash; model.hash end

#if_already_presentself

Optional dependency injection

Once this is called, this Syskit::InstanceRequirements object can be used to inject into an optional dependency. This dependency 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 (see CompositionChild#optional)

Returns:

  • (self)


675
676
677
678
# File 'lib/syskit/instance_requirements.rb', line 675

def if_already_present
    invalidate_template
    abstract
end

#initialize_copy(old) ⇒ Object



138
139
140
141
# File 'lib/syskit/instance_requirements.rb', line 138

def initialize_copy(old)
    super
    do_copy(old)
end

#instanciate(plan, context = Syskit::DependencyInjectionContext.new, task_arguments: Hash.new, specialization_hints: Hash.new, use_template: true) ⇒ Object

Create a concrete task for this requirement



909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
# File 'lib/syskit/instance_requirements.rb', line 909

def instanciate(plan,
    context = Syskit::DependencyInjectionContext.new,
    task_arguments: Hash.new,
    specialization_hints: Hash.new,
    use_template: true)

    from_cache = context.empty? && specialization_hints.empty? &&
        use_template && can_use_template?
    if from_cache
        task = instanciate_from_template(plan, task_arguments)
    else
        begin
            task_model = placeholder_model

            context.save
            context.push(resolved_dependency_injection)

            task_arguments = self.arguments.merge(task_arguments)
            specialization_hints = self.specialization_hints | specialization_hints
            task = task_model.instanciate(plan, context,
                task_arguments: task_arguments,
                specialization_hints: specialization_hints)
        ensure
            context.restore if !from_cache
        end
    end

    post_instanciation_setup(task.to_task)
    model.bind(task)

rescue InstanciationError => e
    e.instanciation_chain << self
    raise
ensure
end

#instanciate_from_template(plan, extra_arguments) ⇒ Object



895
896
897
898
899
900
901
902
# File 'lib/syskit/instance_requirements.rb', line 895

def instanciate_from_template(plan, extra_arguments)
    compute_template unless @template

    mappings = @template.deep_copy_to(plan)
    root_task = mappings[@template.root_task]
    root_task.assign_arguments(arguments.merge(extra_arguments))
    return model.bind(root_task)
end

#invalidate_dependency_injectionObject



167
168
169
170
# File 'lib/syskit/instance_requirements.rb', line 167

def invalidate_dependency_injection
    @di = nil
    invalidate_template
end

#invalidate_templateObject



172
173
174
# File 'lib/syskit/instance_requirements.rb', line 172

def invalidate_template
    @template = nil
end

#map_use_selections! {|selection| ... } ⇒ self

Map all selections registered in the use flags

Yield Parameters:

  • selection

    the selected model/task in the use flag

Yield Returns:

  • the value that should replaces selection

Returns:

  • (self)


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

def map_use_selections!
    selections.map! do |value|
        yield(value)
    end
    pushed_selections.map! do |value|
        yield(value)
    end
    invalidate_dependency_injection
    invalidate_template
    self
end

#merge(other_spec, keep_abstract: false) ⇒ Object

Merges self and other_spec into self

Throws ArgumentError if the two specifications are not compatible (i.e. can't be merged)



462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
# File 'lib/syskit/instance_requirements.rb', line 462

def merge(other_spec, keep_abstract: false)
    if keep_abstract
        @abstract ||= other_spec.abstract?
    elsif !other_spec.abstract?
        @abstract = false
    end

    @base_model = base_model.merge(other_spec.base_model)
    @arguments = @arguments.merge(other_spec.arguments) do |name, v1, v2|
        if v1 != v2
            raise ArgumentError, "cannot merge #{self} and #{other_spec}: argument value mismatch for #{name}, resp. #{v1} and #{v2}"
        end
        v1
    end
    @selections.merge(other_spec.selections)
    @pushed_selections.merge(other_spec.pushed_selections)
    @context_selections.merge(other_spec.context_selections)
    @deployment_group.use_group(other_spec.deployment_group)

    @deployment_hints |= other_spec.deployment_hints
    @specialization_hints |= other_spec.specialization_hints

    @dynamics.merge(other_spec.dynamics)

    invalidate_dependency_injection
    invalidate_template

    # Call modules that could have been included in the class to
    # extend it
    super if defined? super

    narrow_model

    self
end

#narrow_modelObject

Computes the value of model based on the current selection (in #selections) and the base model specified in #add or #define



784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
# File 'lib/syskit/instance_requirements.rb', line 784

def narrow_model
    model = @base_model.to_component_model
    if composition_model? && !model.specializations.empty?
        debug do
            debug "narrowing model"
            debug "  from #{model.short_name}"
            break
        end

        context = log_nest(4) do
            selection = self.resolved_dependency_injection.dup
            selection.remove_unresolved
            DependencyInjectionContext.new(selection)
        end

        model = log_nest(2) do
            model.narrow(context, :specialization_hints => specialization_hints)
        end

        debug do
            debug "  using #{model.short_name}"
            break
        end
    end
    if base_model.respond_to?(:component_model)
        model = base_model.attach(model)
    end

    if @model != model
        invalidate_dependency_injection
        invalidate_template

        @model = model
    end
    return model
end

#not_abstractself

The instanciated task should not be marked as abstract

This is the default. This method is here only to be able to un-set #abstract

Returns:

  • (self)


652
653
654
655
656
# File 'lib/syskit/instance_requirements.rb', line 652

def not_abstract
    invalidate_template
    @abstract = false
    self
end

#on_server(name) ⇒ Object

Requires that this spec runs on the given process server, i.e. that all the corresponding tasks are running on that process server



826
827
828
829
# File 'lib/syskit/instance_requirements.rb', line 826

def on_server(name)
    invalidate_template
    @required_host = name
end

#period(period, sample_size = 1) ⇒ Object



1099
1100
1101
1102
# File 'lib/syskit/instance_requirements.rb', line 1099

def period(period, sample_size = 1)
    dynamics.add_period_info(period, sample_size)
    self
end

#placeholder_modelModel<Roby::Task>

Returns the taks model that should be used to represent the result of the deployment of this requirement in a plan

Returns:

  • (Model<Roby::Task>)


845
846
847
# File 'lib/syskit/instance_requirements.rb', line 845

def placeholder_model
    model.to_component_model
end

#plain?Boolean

Returns:

  • (Boolean)


99
100
101
# File 'lib/syskit/instance_requirements.rb', line 99

def plain?
    arguments.empty? && selections.empty? && pushed_selections.empty?
end

#port_by_name(name) ⇒ Object



425
426
427
428
429
430
# File 'lib/syskit/instance_requirements.rb', line 425

def port_by_name(name)
    if p = find_port(name)
        p
    else raise ArgumentError, "#{self} has no port called #{name}, known ports are: #{each_port.map(&:name).sort.join(", ")}"
    end
end

#post_instanciation_setup(task) ⇒ Object



945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
# File 'lib/syskit/instance_requirements.rb', line 945

def post_instanciation_setup(task)
    task_requirements = to_component_model
    task_requirements.map_use_selections! do |sel|
        if sel && !Models.is_model?(sel) &&
            !sel.kind_of?(DependencyInjection::SpecialDIValue)

            sel.to_instance_requirements
        else sel
        end
    end
    task.update_requirements(task_requirements,
        name: name, keep_abstract: true)

    if required_host && task.respond_to?(:required_host=)
        task.required_host = required_host
    end
    task.abstract = true if abstract?
end

#prefer_deployed_tasks(*patterns) ⇒ Object

Add some hints to disambiguate deployment.

Whenever there is an ambiguity during deployed task assignation, the deployed task names that match the given pattern will be preferred for tasks that are part of the subnet generated by this instance requirements

Note that if no ambiguities exist, these patterns are not used at all

Parameters:

  • patterns (#===)

    objects that can match strings (usually regular expressions)



756
757
758
759
760
# File 'lib/syskit/instance_requirements.rb', line 756

def prefer_deployed_tasks(*patterns)
    invalidate_template
    @deployment_hints |= patterns.to_set
    self
end

#prefer_specializations(specialization_selectors) ⇒ self

Give information needed to disambiguate the specialization selection

Whenever a specialization ambiguity exists, the possible matches will be evaluated against each selector given through this method. Only the ones that match at least one will be selected in the end

Parameters:

Returns:

  • (self)


771
772
773
774
775
776
777
778
779
# File 'lib/syskit/instance_requirements.rb', line 771

def prefer_specializations(specialization_selectors)
    if !composition_model?
        raise ArgumentError, "#{self} does not represent a composition, cannot use #prefer_specializations"
    end

    invalidate_template
    @specialization_hints << specialization_selectors
    self
end

#pretty_print(pp) ⇒ Object



1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
# File 'lib/syskit/instance_requirements.rb', line 1010

def pretty_print(pp)
    if model != base_model
        pp.text "#{model}(from #{base_model})"
    else
        pp.text "#{model}"
    end
    pp.nest(2) do
        if !pushed_selections.empty?
            pp.breakable
            pp.text ".use<0>(#{pushed_selections})"
            use_suffix = "<1>"
        end
        if !selections.empty?
            pp.breakable
            pp.text ".use#{use_suffix}(#{selections})"
        end
        if !arguments.empty?
            pp.breakable
            pp.text ".with_arguments(#{arguments.map { |k, v| "#{k} => #{v}" }.join(", ")})"
        end
    end
end

#push_dependency_injection(di) ⇒ void

This method returns an undefined value.

Adds a DI object to the resolution stack

Parameters:



853
854
855
856
857
858
859
860
861
# File 'lib/syskit/instance_requirements.rb', line 853

def push_dependency_injection(di)
    invalidate_dependency_injection
    invalidate_template

    merger = DependencyInjectionContext.new
    merger.push context_selections
    merger.push di
    @context_selections = merger.current_state
end

#push_selectionsObject



625
626
627
628
629
630
631
632
633
634
635
# File 'lib/syskit/instance_requirements.rb', line 625

def push_selections
    invalidate_dependency_injection
    invalidate_template

    merger = DependencyInjectionContext.new
    merger.push pushed_selections
    merger.push selections
    @pushed_selections = merger.current_state
    @selections = DependencyInjection.new
    nil
end

#reset_deployment_selectionObject



717
718
719
720
# File 'lib/syskit/instance_requirements.rb', line 717

def reset_deployment_selection
    deployment_hints.clear
    @deployment_group = Models::DeploymentGroup.new
end

#resolve(object) ⇒ Object

Deprecated.

use #bind instead



206
207
208
209
210
# File 'lib/syskit/instance_requirements.rb', line 206

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

#resolved_dependency_injectionDependencyInjection

Returns the DI object used by this instance requirements task

Returns:



866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
# File 'lib/syskit/instance_requirements.rb', line 866

def resolved_dependency_injection
    if !@di
        context = DependencyInjectionContext.new
        context.push(context_selections)
        # Add a barrier for the names that our models expect. This is
        # required to avoid recursively reusing names (which was once
        # upon a time, and is a very confusing feature)
        barrier = Syskit::DependencyInjection.new
        barrier.add_mask(self.placeholder_model.dependency_injection_names)
        context.push(barrier)
        context.push(pushed_selections)
        context.push(selections)
        @di = context.current_state
    end
    @di
end

#select_service(service) ⇒ Models::BoundDataService

Explicitely selects a given service on the task models required by this task

Parameters:

Returns:

  • (Models::BoundDataService)

    the selected service. If 'service' is a service of a supermodel of a model in #model, the resulting BoundDataService is attached to the actual model in #model and this return value is different from 'service'

Raises:

  • (ArgumentError)

    if the provided service is not a service on a model in self (i.e. not a service of a component model in #base_model



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/syskit/instance_requirements.rb', line 275

def select_service(service)
    if self.service && !self.service.fullfills?(service)
        raise ArgumentError, "#{self} already points to a service which is different from #{service}"
    end

    if !model.to_component_model.fullfills?(service.component_model)
        raise ArgumentError, "#{service} is not a service of #{self}"
    end
    if service.component_model.placeholder?
        if srv = base_model.find_data_service_from_type(service.model)
            @base_model = srv
            @model = srv.attach(model)
        else
            @base_model = model.find_data_service_from_type(service.model)
            @model = base_model
        end
    else
        if srv = base_model.find_data_service(service.name)
            @base_model = srv
            @model = srv.attach(model)
        else
            @base_model = service.attach(model)
            @model = base_model
        end
    end
    self
end

#selected_for(requirements) ⇒ Object



1168
1169
1170
# File 'lib/syskit/instance_requirements.rb', line 1168

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

#self_port_to_component_port(port) ⇒ Object

Resolves the given port into a port that is attached to a component model (NOT a service)



245
246
247
# File 'lib/syskit/instance_requirements.rb', line 245

def self_port_to_component_port(port)
    model.self_port_to_component_port(port).attach(to_component_model)
end

#serviceModels::BoundDataService?

If this object explicitly points to a bound service, return it

Returns:



252
253
254
255
256
257
258
259
260
261
# File 'lib/syskit/instance_requirements.rb', line 252

def service
    if model.kind_of?(Models::BoundDataService)
        model
    elsif model.placeholder?
        ds = model.proxied_data_service_models
        if ds.size == 1
            return model.find_data_service_from_type(ds.first)
        end
    end
end

#simplest_model_representationObject

Returns the simplest model representation for self

It basically checks if #plain? returns true or false. If self is indeed plain, it returns the actual model class



618
619
620
621
622
623
# File 'lib/syskit/instance_requirements.rb', line 618

def simplest_model_representation
    if plain?
        return model
    else return self
    end
end

#specializeObject



160
161
162
163
164
165
# File 'lib/syskit/instance_requirements.rb', line 160

def specialize
    new_ir = dup
    new_model = base_model.specialize
    new_ir.add_models([new_model])
    new_ir
end

#to_actionObject



1202
1203
1204
# File 'lib/syskit/instance_requirements.rb', line 1202

def to_action
    to_action_model.new
end

#to_action_model(doc = self.doc) ⇒ Object



1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
# File 'lib/syskit/instance_requirements.rb', line 1172

def to_action_model(doc = self.doc)
    action_model = Actions::Models::Action.new(self, doc)
    action_model.name = name
    action_model.returns(model.to_component_model)

    task_model = component_model
    root_model = [TaskContext, Composition, Component].find { |m| task_model <= m }
    task_arguments = task_model.arguments.to_a - root_model.arguments.to_a
    task_arguments.each do |arg_name|
        arg = task_model.find_argument(arg_name)
        if arguments.has_key?(arg_name)
            optional, default_argument = true, arguments[arg_name]
        elsif arg.has_default?
            optional, default_argument = true, arg.default
            if default_argument.kind_of?(Roby::DefaultArgument)
                default_argument = default_argument.value
            elsif arg.has_delayed_default?
                default_argument = nil
            end
        end

        if optional
            action_model.optional_arg(arg_name, arg.doc || "#{arg_name} argument of #{task_model.name}", default_argument)
        else
            action_model.required_arg(arg_name, arg.doc || "#{arg_name} argument of #{task_model.name}")
        end
    end
    action_model
end

#to_component_modelObject

Returns a copy of these requirements with any service specification strippped



199
200
201
202
203
# File 'lib/syskit/instance_requirements.rb', line 199

def to_component_model
    result = dup
    result.unselect_service
    result
end

#to_coordination_task(task_model) ⇒ Object



1164
1165
1166
# File 'lib/syskit/instance_requirements.rb', line 1164

def to_coordination_task(task_model)
    CoordinationTask.new(self)
end

#to_instance_requirementsSyskit::InstanceRequirements

Generates the InstanceRequirements object that represents self best



1068
1069
1070
# File 'lib/syskit/instance_requirements.rb', line 1068

def to_instance_requirements
    self
end

#to_sObject



992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
# File 'lib/syskit/instance_requirements.rb', line 992

def to_s
    result = "#{base_model.short_name}"
    if model != base_model
        result << "[narrowed to #{model.short_name}]"
    end
    if !pushed_selections.empty?
        result << ".use<0>(#{pushed_selections})"
        use_suffix = "<1>"
    end
    if !selections.empty?
        result << ".use#{use_suffix}(#{selections})"
    end
    if !arguments.empty?
        result << ".with_arguments(#{arguments.map { |k, v| "#{k}: #{v}" }.join(", ")})"
    end
    result
end

#try_bind(task) ⇒ Object

Maps the given task to the underlying model

Unlike #bind, it returns nil if the task cannot be mapped



228
229
230
# File 'lib/syskit/instance_requirements.rb', line 228

def try_bind(task)
    model.try_bind(task)
end

#try_resolve(task) ⇒ Object

Deprecated.

use #try_bind instead



213
214
215
216
217
# File 'lib/syskit/instance_requirements.rb', line 213

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

#unselect_serviceObject

Removes any service selection



304
305
306
307
308
309
310
311
# File 'lib/syskit/instance_requirements.rb', line 304

def unselect_service
    if base_model.respond_to?(:component_model)
        @base_model = base_model.component_model
    end
    if model.respond_to?(:component_model)
        @model = model.component_model
    end
end

#use(*mappings) ⇒ Object

:call-seq:

use 'child_name' => 'component_model_or_device'
use 'child_name' => ComponentModel
use ChildModel => 'component_model_or_device'
use ChildModel => ComponentModel
use Model1, Model2, Model3

Provides explicit selections for the children of compositions

In the first two forms, provides an explicit selection for a given child. The selection can be given either by name (name of the model and/or of the selected device), or by directly giving the model object.

In the second two forms, provides an explicit selection for any children that provide the given model. For instance,

use IMU => XsensImu::Task

will select XsensImu::Task for any child that provides IMU

Finally, the third form allows to specify preferences without being specific about where to put them. If ambiguities are found, and if only one of the possibility is listed there, then that possibility will be selected. It has a lower priority than the explicit selection.

See also Composition#instanciate



539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
# File 'lib/syskit/instance_requirements.rb', line 539

def use(*mappings)
    if !(model <= Syskit::Composition)
        raise ArgumentError, "#use is available only for compositions, got #{base_model.short_name}"
    end

    invalidate_dependency_injection
    invalidate_template

    mappings.delete_if do |sel|
        if sel.kind_of?(DependencyInjection)
            selections.merge(sel)
            true
        end
    end

    explicit, defaults = DependencyInjection.partition_use_arguments(*mappings)
    explicit.each_value do |v|
        if v.kind_of?(Roby::Task) || v.kind_of?(BoundDataService)
            @can_use_template = false
        end
    end

    debug do
        debug "adding use mappings to #{self}"
        if !explicit.empty?
            explicit.each do |key, obj|
                debug "  #{key.short_name} => #{obj.short_name}"
            end
        end
        if !defaults.empty?
            debug "  #{defaults.map(&:short_name).join(", ")}"
        end
        break
    end

    # Validate the new mappings first
    new_mappings = selections.dup
    # !!! #add_explicit does not do any normalization. User-provided
    # !!! selections should always be added with #add
    new_mappings.add(explicit)
    explicit.each_key do |child_name|
        req = new_mappings.explicit[child_name]
        next if !req.respond_to?(:fullfills?)
        if child = model.find_child(child_name)
            _, selected_m, _ = new_mappings.selection_for(child_name, child)
            if !selected_m.fullfills?(child)
                raise InvalidSelection.new(child_name, req, child), "#{req} is not a valid selection for #{child_name}. Was expecting something that provides #{child}"
            end
        end
    end

    # See comment about #add_explicit vs. #add above
    selections.add(explicit)
    selections.add(*defaults)
    composition_model = narrow_model || composition_model

    selections.each_selection_key do |obj|
        if obj.respond_to?(:to_str)
            # Two choices: either a child of the composition model,
            # or a child of a child that is a composition itself
            parts = obj.split('.')
            first_part = parts.first
            if !composition_model.has_child?(first_part)
                children = Hash.new
                composition_model.each_child do |name, child|
                    children[name] = child
                end
                raise Roby::NoSuchChild.new(composition_model, first_part, children), "#{first_part} is not a known child of #{composition_model.name}"
            end
        end
    end

    self
end

#use_conf(*conf) ⇒ Object

Deprecated.

use #with_conf instead



705
706
707
708
# File 'lib/syskit/instance_requirements.rb', line 705

def use_conf(*conf)
    Roby.warn_deprecated "InstanceRequirements#use_conf is deprecated. Use #with_conf instead"
    with_conf(*conf)
end

#use_configured_deployment(configured_deployment) ⇒ Object



722
723
724
725
726
# File 'lib/syskit/instance_requirements.rb', line 722

def use_configured_deployment(configured_deployment)
    invalidate_template
    deployment_group.register_configured_deployment(configured_deployment)
    self
end

#use_deployment(*spec, **options) ⇒ Object

Declare the deployment that should be used for self



729
730
731
732
733
# File 'lib/syskit/instance_requirements.rb', line 729

def use_deployment(*spec, **options)
    invalidate_template
    deployment_group.use_deployment(*spec, **options)
    self
end

#use_deployment_group(deployment_group) ⇒ Object

Add deployments into the deployments this subnet should be using

Parameters:



738
739
740
741
742
# File 'lib/syskit/instance_requirements.rb', line 738

def use_deployment_group(deployment_group)
    invalidate_template
    self.deployment_group.use_group(deployment_group)
    self
end

#with_arguments(hash_argument = nil, **arguments) ⇒ Object

Specifies new arguments that must be set to the instanciated task



681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
# File 'lib/syskit/instance_requirements.rb', line 681

def with_arguments(hash_argument = nil, **arguments)
    if hash_argument
        Roby.warn_deprecated "InstanceRequirements#with_arguments: providing arguments using a string is not supported anymore use key: value instead of 'key' => value"
        hash_argument.each do |key, arg|
            arguments[key.to_sym] = arg
        end
    end

    arguments.each do |k, v|
        if !v.droby_marshallable?
            raise Roby::NotMarshallable, "values used as task arguments must be marshallable, attempting to set #{k} to #{v} of class #{v.class}, which is not"
        end
    end
    @arguments.merge!(arguments)
    self
end

#with_conf(*conf) ⇒ Object

Specifies that the task that is represented by this requirement should use the given configuration



712
713
714
715
# File 'lib/syskit/instance_requirements.rb', line 712

def with_conf(*conf)
    with_arguments(conf: conf)
    self
end

#with_no_argumentsObject

Clear all arguments



699
700
701
702
# File 'lib/syskit/instance_requirements.rb', line 699

def with_no_arguments
    @arguments.clear
    self
end