Class: OroGen::Spec::TaskContext

Inherits:
Object
  • Object
show all
Defined in:
lib/orogen/spec/task_context.rb

Overview

Model of a task context, i.e. a task context interface

The corresponding code generation support is done in Gen::RTT_CPP::TaskContextGeneration

Direct Known Subclasses

ROS::Spec::Node

Constant Summary collapse

STATE_TYPES =
[ :toplevel, :runtime, :error, :fatal, :exception ]

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(project, name = nil, subclasses: project.default_task_superclass) ⇒ TaskContext

Create a new task context in the given project and with the given name. If a block is given, it is evaluated in the context of the newly created TaskContext object.

TaskContext objects should not be created directly. You should use Project#task_context for that.


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
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/orogen/spec/task_context.rb', line 320

def initialize(project, name = nil, subclasses: project.default_task_superclass)
    @project  = project

    if subclasses
        @superclass =
            if subclasses.respond_to?(:to_str)
                project.task_model_from_name subclasses
            else
                subclasses
            end
        @default_activity  = @superclass.default_activity.dup
        @required_activity = @superclass.required_activity?
    else
        @superclass = false
        default_activity 'triggered'
        @required_activity = false
    end

    @implemented_classes = []
    @name = name

    # This is an array, as we don't want to have it reordered
    # unnecessarily
    @states = Array.new

    @properties = Hash.new
    @attributes = Hash.new
    @operations = Hash.new
    @output_ports = Hash.new
    @input_ports  = Hash.new
    @dynamic_ports = Array.new
    @event_ports = Hash.new
    @initial_state = 'Stopped'
    @default_extensions = Array.new
    @fixed_initial_state = false
    @needs_configuration = false

    ## WARN: this must be kept an array so that the generation order
    ## WARN: is deterministic
    @extensions = Array.new

    super()

    if block_given?
        instance_eval(&proc)
    end
end

Class Attribute Details

.default_extensionsSymbol (readonly)

Returns set of method names that should be called on the newly created task at creation time. This is meant to register some default extensions automatically

Returns:

  • (Symbol)

    set of method names that should be called on the newly created task at creation time. This is meant to register some default extensions automatically


66
67
68
# File 'lib/orogen/spec/task_context.rb', line 66

def default_extensions
  @default_extensions
end

.extensions_disabledObject (readonly)

Returns the value of attribute extensions_disabled


67
68
69
# File 'lib/orogen/spec/task_context.rb', line 67

def extensions_disabled
  @extensions_disabled
end

Instance Attribute Details

#dynamic_portsObject (readonly)

A set of Port objects that can be created at runtime


162
163
164
# File 'lib/orogen/spec/task_context.rb', line 162

def dynamic_ports
  @dynamic_ports
end

#extensionsObject (readonly)

Set of extensions registered for this task

Extensions are named objects of arbitrary type that can be used to extend the task context model. If they contain a #generate method, this method is also called to add some code generation elements

This is an array (not a hash) so that it keeps order


97
98
99
# File 'lib/orogen/spec/task_context.rb', line 97

def extensions
  @extensions
end

#implemented_classesObject (readonly)

A set of classes the TaskContext has to implement as well


160
161
162
# File 'lib/orogen/spec/task_context.rb', line 160

def implemented_classes
  @implemented_classes
end

#nameObject (readonly)

The task name


155
156
157
# File 'lib/orogen/spec/task_context.rb', line 155

def name
  @name
end

#projectProject (readonly)

The oroGen project this task is part of

Returns:


48
49
50
# File 'lib/orogen/spec/task_context.rb', line 48

def project
  @project
end

#superclassObject (readonly)

The subclass of TaskContext which should be used to define this class


158
159
160
# File 'lib/orogen/spec/task_context.rb', line 158

def superclass
  @superclass
end

#uses_qtObject (readonly)

True if QT is used within this Task


165
166
167
# File 'lib/orogen/spec/task_context.rb', line 165

def uses_qt
  @uses_qt
end

Class Method Details

.apply_default_extensions(task_context) ⇒ Object


77
78
79
80
81
82
83
# File 'lib/orogen/spec/task_context.rb', line 77

def apply_default_extensions(task_context)
    if !extensions_disabled
        default_extensions.each do |ext|
            task_context.send(ext)
        end
    end
end

.blank(name = nil) ⇒ Object

Returns a blank task context model, possibly with a name


307
308
309
310
311
312
# File 'lib/orogen/spec/task_context.rb', line 307

def self.blank(name = nil)
    loader = Loaders::Base.new
    project = Project.new(loader)
    project.default_task_superclass = false
    TaskContext.new(project, name)
end

.disable_default_extensionsObject


69
70
71
# File 'lib/orogen/spec/task_context.rb', line 69

def disable_default_extensions
    @extensions_disabled = true
end

.enable_default_extensionsObject


73
74
75
# File 'lib/orogen/spec/task_context.rb', line 73

def enable_default_extensions
    @extensions_disabled = false
end

Instance Method Details

#abstractObject

Call to declare that this task model is not meant to run in practice


169
# File 'lib/orogen/spec/task_context.rb', line 169

def abstract; @abstract = true; end

#abstract?Boolean

True if this task model is only meant to declare an interface, and should not be deployed

Returns:

  • (Boolean)

172
# File 'lib/orogen/spec/task_context.rb', line 172

def abstract?; @abstract end

#all_portsObject


1119
1120
1121
# File 'lib/orogen/spec/task_context.rb', line 1119

def all_ports
    all_input_ports + all_output_ports
end

#ancestorsObject

Returns the task context models that are in this model's ancestry


372
373
374
375
376
377
378
379
# File 'lib/orogen/spec/task_context.rb', line 372

def ancestors
    m = self
    result = [self]
    while m = m.superclass
        result << m
    end
    result
end

#attribute(name, type, default_value = nil) ⇒ Object

:method: self_attributes :call-seq:

self_attributes -> set_of_attributes

Returns the set of attributes that are added at this level of the model hierarchy. I.e. attributes that are defined on this task context, but not on its parent models.


527
528
529
530
531
# File 'lib/orogen/spec/task_context.rb', line 527

def attribute(name, type, default_value = nil)
    @attributes[name] = att = configuration_object(Attribute, name, type, default_value)
    Spec.load_documentation(att, /attribute/)
    att
end

#check_uniqueness(name) ⇒ Object

Raises ArgumentError if an object named name is already present in the set attribute set_name.

This is an internal helper method


447
448
449
450
451
452
453
454
455
456
457
# File 'lib/orogen/spec/task_context.rb', line 447

def check_uniqueness(name) # :nodoc:
    obj = find_input_port(name) ||
        find_output_port(name) ||
        find_operation(name) ||
        find_property(name) ||
        find_attribute(name)

    if obj
        raise ArgumentError, "#{name} is already used in the interface of #{self.name}, as a #{obj.class}"
    end
end

#configuration_object(klass, name, type, default_value) ⇒ Object


533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
# File 'lib/orogen/spec/task_context.rb', line 533

def configuration_object(klass, name, type, default_value)
    name = OroGen.verify_valid_identifier(name)
    check_uniqueness(name)

    begin
        type = project.find_interface_type(type)
    rescue Typelib::NotFound => e
        raise ConfigError, "invalid type #{type}: #{e.message}", e.backtrace
    end

    if default_value
        accepted = [Numeric, Symbol, String, TrueClass, FalseClass].
            any? { |valid_klass| default_value.kind_of?(valid_klass) }
        if !accepted
            raise ArgumentError, "default values for #{klass.name.downcase} can be specified only for simple types (numeric, string and boolean)"
        end
        begin
            Typelib.from_ruby(default_value, type)
        rescue Typelib::UnknownConversionRequested => e
            raise ArgumentError, e.message, e.backtrace
        end
    end

    klass.new(self, name, type, default_value)
end

#default_activityObject

:method: default_activity :call-seq:

default_activity 'activity_type', *args

The kind of activity that should be used by default. This is the name of the corresponding method on the deployment objects (:periodic, :aperiodic, :slave, :irq_driven, :fd_driven)

This is a default value, i.e. the use of such an activity is not mandatory. If #required_activity is set to true, then this activity is the only kind of activity that can be used with this task context.

See also #required_activity


285
286
287
288
289
290
291
292
293
294
295
# File 'lib/orogen/spec/task_context.rb', line 285

dsl_attribute :default_activity do |type, *args|
    if required_activity? && @default_activity
        raise ArgumentError, "the #{default_activity[0]} activity is required, you cannot change it"
    end

    type = type.to_sym
    if !ACTIVITY_TYPES.has_key?(type)
        raise ArgumentError, "#{type} is not a valid activity type"
    end
    [type, *args]
end

#define_state(name, type) ⇒ Object

Internal method for state definition


640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
# File 'lib/orogen/spec/task_context.rb', line 640

def define_state(name, type) # :nodoc:
    name = name.to_s
    type = type.to_sym
    if !STATE_TYPES.include?(type)
        raise ArgumentError, "unknown state type #{type.inspect}"
    end

    if !extended_state_support? && (type != :toplevel)
        extended_state_support
    end

    if kind = state_kind(name.to_s)
        if kind != type
            raise ArgumentError, "state #{name} is already defined as #{kind}, cannot overload into #{type}"
        end
    else
        @states << [name, type]
        if type != :toplevel
            @states = @states.sort_by { |n, _| n }
        end
    end
end

#docObject

:method: doc :call-seq:

doc => string
doc "documentation string"

Gets or sets the documentation string for this task context


60
# File 'lib/orogen/spec/task_context.rb', line 60

dsl_attribute :doc

#dynamic_input_port(name, type) ⇒ Object

call-seq:

dynamic_input_port name_regex, typename

Declares that a port whose name matches name_regex can be declared at runtime, with the type. This is not used by orogen himself, but can be used by potential users of the orogen specification.


1193
1194
1195
1196
# File 'lib/orogen/spec/task_context.rb', line 1193

def dynamic_input_port(name, type)
    dynamic_ports << DynamicInputPort.new(self, name, type)
    dynamic_ports.last
end

#dynamic_output_port(name, type) ⇒ Object

call-seq:

dynamic_output_port name_regex, typename

Declares that a port whose name matches name_regex can be declared at runtime, with the type. This is not used by orogen himself, but can be used by potential users of the orogen specification.


1204
1205
1206
1207
# File 'lib/orogen/spec/task_context.rb', line 1204

def dynamic_output_port(name, type)
    dynamic_ports << DynamicOutputPort.new(self, name, type)
    dynamic_ports.last
end

#dynamic_portObject

:method: self_dynamic_ports :call-seq:

self_dynamic_ports -> set_of_ports

Returns the set of dynamic ports that are added at this level of the model hierarchy. I.e. ports that are defined on this task context, but not on its parent models.


885
# File 'lib/orogen/spec/task_context.rb', line 885

enumerate_inherited_set("dynamic_port", "dynamic_ports")

#each_dynamic_input_port(only_self = false) ⇒ Object

:method: each_dynamic_input_port :call-seq:

each_dynamic_port(only_self = false) { |port| }

Yields all dynamic input ports that are defined on this task context.


830
831
832
833
834
835
836
837
# File 'lib/orogen/spec/task_context.rb', line 830

def each_dynamic_input_port(only_self = false)
    return enum_for(:each_dynamic_input_port, only_self) if !block_given?
    each_dynamic_port do |port|
        if port.kind_of?(InputPort)
            yield(port)
        end
    end
end

#each_dynamic_output_port(only_self = false) ⇒ Object

:method: each_dynamic_output_port :call-seq:

each_dynamic_output_port(only_self = false) { |port| }

Yields all dynamic output ports that are defined on this task context.


845
846
847
848
849
850
851
852
# File 'lib/orogen/spec/task_context.rb', line 845

def each_dynamic_output_port(only_self = false)
    return enum_for(:each_dynamic_output_port, only_self) if !block_given?
    each_dynamic_port do |port|
        if port.kind_of?(OutputPort)
            yield(port)
        end
    end
end

#each_extension(with_subclasses = true, &block) ⇒ Object

Enumerates the extensions registered on this task model, as (name, extension_object) pairs


115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/orogen/spec/task_context.rb', line 115

def each_extension(with_subclasses = true, &block)
    if !block_given?
        return enum_for(:each_extension, with_subclasses)
    end

    seen = Set.new
    klass = self
    begin
        klass.extensions.each do |ext|
            if !seen.include?(ext.name)
                seen << ext.name
                yield(ext)
            end
        end
        klass = klass.superclass
    end while (klass && with_subclasses)
end

#each_interface_typeObject

Enumerate all the types that are used on this component's interface


1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
# File 'lib/orogen/spec/task_context.rb', line 1407

def each_interface_type
    return enum_for(__method__) if !block_given?

    seen = Set.new
    (all_properties + all_attributes + all_operations + all_ports + all_dynamic_ports).
        each do |obj|
            if !seen.include?(obj)
                obj.each_interface_type { |t| yield(t) }
                seen << obj
            end
        end
end

#each_port(&block) ⇒ Object

Enumerates both the input and output ports


1128
1129
1130
1131
1132
1133
1134
1135
# File 'lib/orogen/spec/task_context.rb', line 1128

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

    each_input_port(&block)
    each_output_port(&block)
end

#each_state(with_superclass: true) {|name, type| ... } ⇒ Object

Enumerates each state defined on this task context.

Parameters:

  • with_superclass (Boolean)

    whether only states defined on this level of the task hierarchy should be enumerated, or the states from the superclass too.

Yield Parameters:

  • name (String)

    the state name

  • type (Symbol)

    the state type, one of STATE_TYPES


719
720
721
722
723
724
725
726
# File 'lib/orogen/spec/task_context.rb', line 719

def each_state(with_superclass: true, &block)
    return enum_for(__method__) if !block

    if superclass && with_superclass
        superclass.each_state(&block)
    end
    @states.each(&block)
end

#error_states(*state_names) ⇒ Object

Declares a certain number of runtime error states

This method will do nothing if it defines a state that is already defined by one of the superclasses.

See #runtime_states, #exception_states, #each_state, #each_error_state


772
773
774
775
776
# File 'lib/orogen/spec/task_context.rb', line 772

def error_states(*state_names)
    state_names.each do |name|
        define_state(name, :error)
    end
end

#exception_states(*state_names) ⇒ Object

Declares a certain number of exception states

This method will do nothing if it defines a state that is already defined by one of the superclasses.

See #runtime_states, #fatal_states, #error_states, #each_state, #each_error_state


784
785
786
787
788
# File 'lib/orogen/spec/task_context.rb', line 784

def exception_states(*state_names)
    state_names.each do |name|
        define_state(name, :exception)
    end
end

#extended_state_supportObject

Asks orogen to implement the extended state support interface in the Base class. This adds:

* a 'state' output port in which the current task's state is written
* an enumeration type named CLASS_NAME_STATES in which one value
  is defined for each states

Note that, for all of this to work, it is actually required that all the hooks overloaded in the task's class call their parent in the call chain.


605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
# File 'lib/orogen/spec/task_context.rb', line 605

def extended_state_support
    state_port = find_port("state")
    if state_port
        if state_port.kind_of?(InputPort)
            raise ArgumentError, 
                "there is already an input port called 'state', cannot enable extended state support"
        elsif state_port.type != project.find_type("/int32_t")
            raise ArgumentError, 
                "there is already an output port called 'state', but it is not of type 'int' (found #{state_port.type_name}"
        end
    else
        output_port('state', '/int32_t').
            triggered_once_per_update
    end

    # Force typekit generation. The typekit code will take care of
    # generating the state enumeration type for us
    project.typekit(true)

    @extended_state_support = true
end

#extended_state_support?Boolean

True if the extended state support is enabled

Returns:

  • (Boolean)

628
629
630
# File 'lib/orogen/spec/task_context.rb', line 628

def extended_state_support?
    @extended_state_support || (superclass.extended_state_support? if superclass)
end

#extension(name, with_subclasses = true) ⇒ Object

Returns the extension named name, or raises ArgumentError if none is registered with that name


144
145
146
147
148
149
# File 'lib/orogen/spec/task_context.rb', line 144

def extension(name, with_subclasses = true)
    if ext = find_extension(name, with_subclasses)
        ext
    else raise ArgumentError, "no extension registered under the name '#{name}'"
    end
end

#fatal_states(*state_names) ⇒ Object

Declares a certain number of fatal error states

This method will do nothing if it defines a state that is already defined by one of the superclasses.

See #runtime_states, #error_states, #each_state, #each_error_state


796
797
798
799
800
# File 'lib/orogen/spec/task_context.rb', line 796

def fatal_states(*state_names)
    state_names.each do |name|
        define_state(name, :fatal)
    end
end

#fd_drivenObject

Declares that this task context is designed to be woken up when new data is available on a I/O file descriptor. The resulting task must also use the fd_driven activity, which is done by default.

The only thing you have to do in the implementation is therefore

task = task("MyDFDrivenTask").
    start

To configure the activity, you will need to declare the FDs you want to watch in the configureHook():

RTT::extras::FileDescriptorActivity* fd_activity =
    getActivity<RTT::extras::FileDescriptorActivity>();
if (fd_activity)
    fd_activity->watch(my_fd);

Don't forget to remove all watches in cleanupHook with

RTT::extras::FileDescriptorActivity* fd_activity =
    getActivity<RTT::extras::FileDescriptorActivity>();
if (fd_activity)
    fd_activity->clearAllWatches();

1305
1306
1307
1308
# File 'lib/orogen/spec/task_context.rb', line 1305

def fd_driven
    default_activity "fd_driven"
    needs_configuration
end

#fd_driven?Boolean

True if this task context's default activity is a FD-driven activity

Returns:

  • (Boolean)

1311
1312
1313
# File 'lib/orogen/spec/task_context.rb', line 1311

def fd_driven?
    default_activity.first == :fd_driven
end

#find_dynamic_input_ports(name, type) ⇒ Object

Returns the set of dynamic input port definitions that match the given name and type pair. If type is nil, the type is ignored in the matching.


1220
1221
1222
1223
1224
1225
# File 'lib/orogen/spec/task_context.rb', line 1220

def find_dynamic_input_ports(name, type)
    if type
        type = project.find_type(type)
    end
    each_dynamic_input_port.find_all { |p| (!type || !p.type || p.type == type) && p.name === name }
end

#find_dynamic_output_ports(name, type) ⇒ Object

Returns the set of dynamic output port definitions that match the given name and type pair. If type is nil, the type is ignored in the matching.


1237
1238
1239
1240
1241
1242
# File 'lib/orogen/spec/task_context.rb', line 1237

def find_dynamic_output_ports(name, type)
    if type
        type = project.find_type(type)
    end
    each_dynamic_output_port.find_all { |p| (!type || !p.type || p.type == type) && p.name === name }
end

#find_extension(name, with_subclasses = true) ⇒ Object

Returns the extension named name, or nil if there is none


134
135
136
137
138
139
140
# File 'lib/orogen/spec/task_context.rb', line 134

def find_extension(name, with_subclasses = true)
    if result = extensions.find { |ext| ext.name == name }
        result
    elsif with_subclasses && superclass
        superclass.find_extension(name, true)
    end
end

#find_port(name, type = nil) ⇒ Object

Finds a port with the given name, and optionally type

Returns nil if there are none

See also #find_input_port and #find_output_port


1142
1143
1144
1145
1146
1147
# File 'lib/orogen/spec/task_context.rb', line 1142

def find_port(name, type = nil)
    p = find_input_port(name) || find_output_port(name)
    if !type || (p && p.type == type)
        return p
    end
end

#fixed_initial_stateObject

Declares that the initial state of this class cannot be specified. For orogen-declared tasks, it is the same as #needs_configuration?. This mechanism is here for classes that have not been generated by orogen and either have a no way to specify the initial state, or a non-standard one.


1372
# File 'lib/orogen/spec/task_context.rb', line 1372

def fixed_initial_state; @fixed_initial_state = true end

#fixed_initial_state?Boolean

If true, then the initial state of this class cannot be specified. For orogen-declared tasks, it is the same as #needs_configuration?. This mechanism is here for classes that have not been generated by orogen and either have a no way to specify the initial state, or a non-standard one.

Returns:

  • (Boolean)

1365
# File 'lib/orogen/spec/task_context.rb', line 1365

def fixed_initial_state?; @fixed_initial_state || needs_configuration? || (superclass.fixed_initial_state? if superclass) end

#has_dynamic_attributes?Boolean

Return true if this task interface has an dynamic property.

Returns:

  • (Boolean)

1180
1181
1182
1183
1184
1185
# File 'lib/orogen/spec/task_context.rb', line 1180

def has_dynamic_attributes?
    self_attributes.each do |p|
        return true if p.dynamic?
    end
    return false
end

#has_dynamic_input_port?(name, type = nil) ⇒ Boolean

Returns true if there is an input port definition that match the given name and type pair. If type is nil, the type is ignored in the matching.

Returns:

  • (Boolean)

1230
1231
1232
# File 'lib/orogen/spec/task_context.rb', line 1230

def has_dynamic_input_port?(name, type = nil)
    !find_dynamic_input_ports(name, type).empty?
end

#has_dynamic_output_port?(name, type = nil) ⇒ Boolean

Returns true if an output port of the given name and type could be created at runtime.

Returns:

  • (Boolean)

1246
1247
1248
# File 'lib/orogen/spec/task_context.rb', line 1246

def has_dynamic_output_port?(name, type = nil)
    !find_dynamic_output_ports(name, type).empty?
end

#has_dynamic_port?(name, type) ⇒ Boolean

Returns true if there is a dynamic port definition that matches the given name and type pair.

If type is nil, the type is ignored in the matching.

Returns:

  • (Boolean)

1213
1214
1215
# File 'lib/orogen/spec/task_context.rb', line 1213

def has_dynamic_port?(name, type)
    has_dynamic_input_port?(name, type) || has_dynamic_output_port?(name, type)
end

#has_dynamic_properties?Boolean

Return true if this task interface has an dynamic property.

Returns:

  • (Boolean)

1172
1173
1174
1175
1176
1177
# File 'lib/orogen/spec/task_context.rb', line 1172

def has_dynamic_properties?
    self_properties.each do |p|
        return true if p.dynamic?
    end
    return false
end

#has_extension?(name, with_subclasses = true) ⇒ Boolean

True if an extension with the given name has been registered

Returns:

  • (Boolean)

100
# File 'lib/orogen/spec/task_context.rb', line 100

def has_extension?(name, with_subclasses = true); !!find_extension(name, with_subclasses) end

#has_input_port?(name) ⇒ Boolean

Returns true if this task interface has a port named 'name'. If a type is given, the corresponding port will be matched against that type as well

Returns:

  • (Boolean)

1159
1160
1161
# File 'lib/orogen/spec/task_context.rb', line 1159

def has_input_port?(name)
    !!find_input_port(name)
end

#has_output_port?(name) ⇒ Boolean

Returns true if this task interface has a port named 'name'. If a type is given, the corresponding port will be matched against that type as well

Returns:

  • (Boolean)

1166
1167
1168
# File 'lib/orogen/spec/task_context.rb', line 1166

def has_output_port?(name)
    !!find_output_port(name)
end

#has_port?(name, type = nil) ⇒ Boolean

Returns true if this task interface has a port named 'name'. If a type is given, the corresponding port will be matched against that type as well

Returns:

  • (Boolean)

1152
1153
1154
# File 'lib/orogen/spec/task_context.rb', line 1152

def has_port?(name, type = nil)
    !!find_port(name, type)
end

#has_property?(name) ⇒ Boolean

True if this task has a property with that name

Returns:

  • (Boolean)

578
579
580
# File 'lib/orogen/spec/task_context.rb', line 578

def has_property?(name)
    !!find_property(name)
end

#hidden_operation(name, body = nil) ⇒ Object

Defines an operation whose implementation is in the Base class (i.e. “hidden” from the user)


813
814
815
816
817
818
819
820
821
822
# File 'lib/orogen/spec/task_context.rb', line 813

def hidden_operation(name, body=nil)
    op = operation(name)
    op.hidden = true
    if body
        OroGen.warn "body argument for hidden_operation '#{self.name}'.'#{name}' is deprecated, please set the body during generation phase"
        OroGen.warn "If you call this from a plugin, define the implementation within your :early_register_for_generation method"
        op.base_body(body)
    end
    op
end

#implements(name, include_file = nil) ⇒ Object

Declares that this task context is also a subclass of the following class. name does not have to be a task context class.


210
211
212
# File 'lib/orogen/spec/task_context.rb', line 210

def implements(name, include_file = nil)
    @implemented_classes << [name, include_file]
end

#implements?(name) ⇒ Boolean

True if the task context implements a parent class which matches name. name can either be a string or a regular expression.

Returns:

  • (Boolean)

216
217
218
219
220
# File 'lib/orogen/spec/task_context.rb', line 216

def implements?(name)
    ancestor_names = ancestors.map(&:name)
    self.name == name ||
        ancestor_names.include?(name)
end

#initialize_copy(from) ⇒ Object


368
369
# File 'lib/orogen/spec/task_context.rb', line 368

def initialize_copy(from)
end

#input_port(name, type, options = Hash.new) ⇒ Object

call-seq:

input_port 'name', '/type'

Add a new write port with the given name and type, and returns the corresponding InputPort object.

See also #output_port


911
# File 'lib/orogen/spec/task_context.rb', line 911

enumerate_inherited_map("input_port", "input_ports")

#inspectObject


152
# File 'lib/orogen/spec/task_context.rb', line 152

def inspect; to_s end

#loaderLoaders::Base

The loader that has been used to load this task context

Returns:


52
# File 'lib/orogen/spec/task_context.rb', line 52

def loader; project.loader end

#make_property_dynamic(name) ⇒ Property

Make an existing property dynamic

Returns:


585
586
587
588
589
590
591
592
593
594
# File 'lib/orogen/spec/task_context.rb', line 585

def make_property_dynamic(name)
    prop = find_property(name)
    if !prop
        raise ArgumentError, "The requested property " + name + " could not be found"
    end
    property = @properties[name] = prop.dup
    property.task = self
    property.dynamic
    property
end

#merge_ports_from(other_model, name_mappings = Hash.new) ⇒ Object

Add in self the ports of other_model that don't exist.

Raises ArgumentError if other_model has ports whose name is used in self, but for which the definition is different.


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
# File 'lib/orogen/spec/task_context.rb', line 463

def merge_ports_from(other_model, name_mappings = Hash.new)
    other_model.each_port do |p|
        if target_name = name_mappings[p.name]
            p = p.dup
            p.instance_variable_set(:@name, target_name.to_str)
        end

        if has_port?(p.name)
            self_port = find_port(p.name)
            if self_port.class != p.class
                raise ArgumentError, "cannot merge as #{self_port.name} is a #{self_port.class} in #{self} and a #{p.class} in #{other_model}"
            elsif self_port.type != p.type
                raise ArgumentError, "cannot merge as #{self_port.name} is of type #{self_port.type} in #{self} and of type #{p.type} in #{other_model}"
            end
        elsif p.kind_of?(OutputPort)
            @output_ports[p.name] = p
        elsif p.kind_of?(InputPort)
            @input_ports[p.name] = p
        end
    end
    other_model.each_dynamic_port do |p|
        existing = each_dynamic_port.find_all do |self_p|
            p.name == self_p.name
        end
        if !existing.any? { |self_p| self_p.type == p.type }
            self.dynamic_ports << p.dup
        end
    end
end

#needs_configurationObject

Declares that this task needs to be configured before it is started (i.e. its initial state will be PreOperational instead of Stopped).

If #fixed_initial_state? returns true, then this method raises ArgumentError. This is done so that it is possible to declare that some task contexts's implementation require the initial state to be either PreOperational or Stopped.


510
511
512
513
514
515
516
517
# File 'lib/orogen/spec/task_context.rb', line 510

def needs_configuration
    if superclass && superclass.fixed_initial_state? && !superclass.needs_configuration?
        raise ArgumentError, "cannot change the start state of this task context: the superclass #{superclass.name} does not allow it"
    elsif fixed_initial_state? && !needs_configuration?
        raise ArgumentError, "cannot change the start state of this task context: #fixed_initial_state has been specified for it"
    end
    @needs_configuration = true
end

#needs_configuration?Boolean

If true, the task context will start in the PreOperational state, and will not be able to run until configure() has been called and returned true.

When subclassing, it is NOT possible to have a subclass starting in the Stopped state while its superclass starts from PreOperational.

Returns:

  • (Boolean)

500
# File 'lib/orogen/spec/task_context.rb', line 500

def needs_configuration?; @needs_configuration || (superclass.needs_configuration? if superclass) end

#new_operationsObject

Operations that are added by this task context (i.e. operations that are defined there but are not present in the superclass)


1019
1020
1021
1022
1023
1024
# File 'lib/orogen/spec/task_context.rb', line 1019

def new_operations
    super_names = superclass.all_operations.map(&:name).to_set
    @operations.values.find_all do |t|
        !super_names.include?(t)
    end
end

#operation(name) ⇒ Object

:operation: self_operations :call-seq:

self_operations -> set_of_operations

Returns the set of operations that are added at this level of the model hierarchy. I.e. operations that are either newly defined on this task context, or overload operations from the parent models.


804
805
806
807
808
809
# File 'lib/orogen/spec/task_context.rb', line 804

def operation(name)
    name = OroGen.verify_valid_identifier(name)
    @operations[name] = op = Operation.new(self, name)
    Spec.load_documentation(op, /operation/)
    op
end

#output_port(name, type, options = Hash.new) ⇒ Object

call-seq:

output_port 'name', '/type'

Add a new write port with the given name and type, and returns the corresponding OutputPort object.

See also #input_port


937
# File 'lib/orogen/spec/task_context.rb', line 937

enumerate_inherited_map("output_port", "output_ports")

#periodic(period) ⇒ Object

Declares that this task should be deployed using a default periodic activity, with the given period


299
300
301
# File 'lib/orogen/spec/task_context.rb', line 299

def periodic(period)
    default_activity :periodic, period
end

#port_driven(*names) ⇒ Object

Declares that this task context is designed to be woken up when new data is available on one of the given ports (or all already defined ports if no names are given).


1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
# File 'lib/orogen/spec/task_context.rb', line 1256

def port_driven(*names)
    default_activity 'triggered'
    names = names.map { |n| n.to_s }
    relevant_ports =
        if names.empty? then all_input_ports
        else
            names.map do |n|
                obj = find_input_port(n)
                if !obj
                    if has_output_port?(n)
                        raise ArgumentError, "#{n} is an output port of #{self.name}, only input ports can be used in #port_driven"
                    else
                        raise ArgumentError, "#{n} is not a port of #{self.name}"
                    end
                end
                obj
            end
        end

    relevant_ports.each do |port|
        port.trigger_port = true
        @event_ports[port.name] = port
    end
end

#pretty_print(pp) ⇒ Object


397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
# File 'lib/orogen/spec/task_context.rb', line 397

def pretty_print(pp)
    pp.text "------- #{name} ------"
    pp.breakable
    if doc
        first_line = true
        doc.split("\n").each do |line|
            pp.breakable if !first_line
            first_line = false
            pp.text "# #{line}"
        end
        pp.breakable
        pp.text "# "
    else
        pp.text "no documentation defined for this task context model"
    end
    pp.breakable
    pp.text "subclass of #{superclass.name} (the superclass elements are displayed below)"
    pp.breakable
    triggers = all_event_ports
    if !triggers.empty?
        pp.text "Triggered on input: #{triggers.map(&:name).join(", ")}"
        pp.breakable
    end
    if needs_configuration?
        pp.text "Needs configuration"
    else
        pp.text "Does NOT need configuration"
    end
    pp.breakable

    pretty_print_interface(pp, "Ports", each_port.to_a)
    pretty_print_interface(pp, "Dynamic Ports", each_dynamic_port.to_a)
    pretty_print_interface(pp, "Properties", each_property.to_a)
    pretty_print_interface(pp, "Attributes", each_attribute.to_a)
    pretty_print_interface(pp, "Operations", each_operation.to_a)

    extensions.each do |ext|
        pp.breakable
        pp.text "Extension: #{ext.name}"
        pp.nest(2) do
            pp.breakable
            ext.pretty_print(pp)
        end
    end
end

#pretty_print_interface(pp, name, set) ⇒ Object


381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
# File 'lib/orogen/spec/task_context.rb', line 381

def pretty_print_interface(pp, name, set)
    if set.empty?
        pp.text "No #{name.downcase}"
    else
        pp.text name
        pp.nest(2) do
            set = set.to_a.sort_by { |p| p.name.to_s }
            set.each do |element|
                pp.breakable
                element.pretty_print(pp)
            end
        end
    end
    pp.breakable
end

#property(name, type, default_value = nil) ⇒ Object

:method: self_properties :call-seq:

self_properties -> set_of_properties

Returns the set of properties that are added at this level of the model hierarchy. I.e. properties that are defined on this task context, but not on its parent models.


571
572
573
574
575
# File 'lib/orogen/spec/task_context.rb', line 571

def property(name, type, default_value = nil)
    @properties[name] = prop = configuration_object(Property, name, type, default_value)
    Spec.load_documentation(prop, /property/)
    prop
end

#register_extension(obj) ⇒ Object

Registers an extension with the given name. Raises ArgumentError if there is already one.


104
105
106
107
108
109
110
111
# File 'lib/orogen/spec/task_context.rb', line 104

def register_extension(obj)
    if (old = find_extension(obj.name, false)) && old != obj
        raise ArgumentError, "there is already an extension called #{obj.name}: #{old}"
    else
        extensions << obj
        obj.registered_on(self)
    end
end

#reports(*state_names) ⇒ Object

Declares a certain number of reports

This method will do nothing if it defines a report that is already defined by one of the superclasses.


750
751
752
# File 'lib/orogen/spec/task_context.rb', line 750

def reports(*state_names)
    runtime_states(*state_names)
end

#required_activityObject

:method: required_activity :call-seq:

required_activity 'activity_type', *args

The kind of activity that must be used for this task context. This is the name of the corresponding method on the deployment objects. See ACTIVITY_TYPES for the list of known activity types.

See also #default_activity


261
262
263
264
265
266
267
268
# File 'lib/orogen/spec/task_context.rb', line 261

dsl_attribute :required_activity do |type, *args|
    if respond_to?(type.to_sym)
        send(type.to_sym, *args)
    else
        default_activity type, *args
    end
    self.required_activity = true
end

#required_activity?Object

:method: required_activity?

True if the current value of default_activity is actually required by the task context implementation


249
# File 'lib/orogen/spec/task_context.rb', line 249

attr_predicate :required_activity?, true

#ro_ptr(name) ⇒ Object

This method is an easier way use boost::shared_ptr in a task context interface. For instance, instead of writing

input_port 'image', '/RTT/ReadOnlyPointer</Image>'

you can write

input_port 'image', ro_ptr('/Image')

Additionally, this method makes sure that the corresponding type is actually defined on the project's typekit.


1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
# File 'lib/orogen/spec/task_context.rb', line 1101

def ro_ptr(name)
    base_type = project.resolve_type(name)

    full_name = "/RTT/extras/ReadOnlyPointer<#{base_type.name}>"
    begin
        project.resolve_type(full_name)
    rescue Typelib::NotFound
        # HACK: this is needed for the codegen part. Will have to go
        # HACK: away once we migrate the codegen part to the
        # HACK: loading infrastructure
        if project.typekit(true).respond_to?(:ro_ptr)
            project.typekit(true).ro_ptr(name)
            project.resolve_type(full_name)
        else raise
        end
    end
end

#root_modelObject

Declares that this task context is a root model and does not have a superclass


204
205
206
# File 'lib/orogen/spec/task_context.rb', line 204

def root_model
    @superclass = nil
end

#runtime_states(*state_names) ⇒ Object

Declares a certain number of runtime states

This method will do nothing if it defines a state that is already defined by one of the superclasses.

See #error_states, #exception_states, #each_state, #each_runtime_state


760
761
762
763
764
# File 'lib/orogen/spec/task_context.rb', line 760

def runtime_states(*state_names)
    state_names.each do |name|
        define_state(name, :runtime)
    end
end

#self_portsObject


1123
1124
1125
# File 'lib/orogen/spec/task_context.rb', line 1123

def self_ports
    self_input_ports + self_output_ports
end

#shared_ptr(name) ⇒ Object

This method is an easier way use boost::shared_ptr in a task context interface. For instance, instead of writing

input_port 'image', '/boost/shared_ptr</Image>'

you can write

input_port 'image', shared_ptr('/Image')

Additionally, this method makes sure that the corresponding type is actually defined on the project's typekit.


1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
# File 'lib/orogen/spec/task_context.rb', line 1073

def shared_ptr(name)
    base_type = project.resolve_type(name)
    full_name = "/boost/shared_ptr<#{base_type.name}>"
    begin
        project.resolve_type(full_name)
    rescue Typelib::NotFound
        # HACK: this is needed for the codegen part. Will have to go
        # HACK: away once we migrate the codegen part to the
        # HACK: loading infrastructure
        if project.typekit(true).respond_to?(:shared_ptr)
            project.typekit(true).shared_ptr(name)
            project.resolve_type(full_name)
        else raise
        end
    end
end

#state?(name) ⇒ Boolean

Returns true if the given state name is already used

Returns:

  • (Boolean)

633
634
635
# File 'lib/orogen/spec/task_context.rb', line 633

def state?(name)
    state_kind(name) || (superclass.state?(name.to_s) if superclass)
end

#state_kind(name) ⇒ Object

Returns what kind of state name is


664
665
666
667
668
# File 'lib/orogen/spec/task_context.rb', line 664

def state_kind(name) # :nodoc:
    if s = each_state.find { |n, t| n == name }
        s[1]
    end
end

#states(*state_names) ⇒ Object

Deprecated.

use toplevel_state to define toplevel states on root models, and #each_state to enumerate the states


730
731
732
733
734
735
736
# File 'lib/orogen/spec/task_context.rb', line 730

def states(*state_names) # :nodoc:
    if state_names.empty?
        return @states
    end

    toplevel_states(*state_names)
end

#subclasses(task_context) ⇒ Object

Declares that this task context is a subclass of the following TaskContext class. task_context can either be a class name or a TaskContext instance. In both cases, it must be defined in the scope of the enclosing Project object – i.e. either defined in it, or imported by a Project#using_task_library call.


182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/orogen/spec/task_context.rb', line 182

def subclasses(task_context)
    OroGen.warn_deprecated __method__, "in #{project.name}: use task_context \"Name\", subclasses: \"Parent\" do .. end instead"

    if task_context.respond_to?(:to_str)
        if @superclass && (@superclass != project.default_task_superclass)
            raise OroGen::ConfigError, "#{@name} tries to subclass #{task_context} "+
                "while there is already #{@superclass.name}"
        end
        @superclass = project.task_model_from_name task_context
    else
        @superclass = task_context
    end
    if !superclass
        raise ArgumentError, "no such task context #{task_context}"
    end

    @default_activity  = @superclass.default_activity.dup
    @required_activity = @superclass.required_activity?
end

#to_dotObject

Generate a graphviz fragment to represent this task


1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
# File 'lib/orogen/spec/task_context.rb', line 1316

def to_dot
    html_escape = lambda { |s| s.gsub(/</, "&lt;").gsub(/>/, "&gt;") }
    html_table  = lambda do |title, lines|
        label  = "<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">\n"
        label << "  <TR><TD>#{title}</TD></TR>\n"
        label << "  <TR><TD>\n"
        label << lines.join("<BR/>\n")
        label << "  </TD></TR>\n"
        label << "</TABLE>"
    end
        
    result = ""
    result << "  node [shape=none,margin=0,height=.1];"

    label = ""
    label << "<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\">\n"
    label << "  <TR><TD>#{name}</TD></TR>"

    properties = all_properties.
        map { |p| "#{p.name} [#{html_escape[p.type_name]}]" }
    if !properties.empty?
        label << "  <TR><TD>#{html_table["Properties", properties]}</TD></TR>"
    end


    input_ports = all_ports.
        find_all { |p| p.kind_of?(InputPort) }.
        map { |p| "#{p.name} [#{html_escape[p.type_name]}]" }
    if !input_ports.empty?
        label << "  <TR><TD>#{html_table["Input ports", input_ports]}</TD></TR>"
    end

    output_ports =all_ports.
        find_all { |p| p.kind_of?(OutputPort) }.
        map { |p| "#{p.name} [#{html_escape[p.type_name]}]" }
    if !output_ports.empty?
        label << "  <TR><TD>#{html_table["Output ports", output_ports]}</TD></TR>"
    end

    label << "</TABLE>"
    result << "  t#{object_id} [label=<#{label}>]"
    result
end

#to_hHash

Converts this model into a representation that can be fed to e.g. a JSON dump, that is a hash with pure ruby key / values.

The generated hash has the following keys:

name: the name
superclass: the name of this model's superclass (if there is
  one)
states: the list of defined states, as formatted by
  {each_state}
ports: the list of ports, as formatted by {Port#to_h}
properties: the list of properties, as formatted by
  {ConfigurationObject#to_h}
attributes: the list of attributes, as formatted by
  {ConfigurationObject#to_h}
operations: the list of operations, as formatted by
  {Operation#to_h}

Returns:

  • (Hash)

1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
# File 'lib/orogen/spec/task_context.rb', line 1393

def to_h
    Hash[
        name: name,
        superclass: superclass.name,
        states: each_state.to_a,
        ports: each_port.map(&:to_h),
        properties: each_property.map(&:to_h),
        attributes: each_attribute.map(&:to_h),
        operations: each_operation.map(&:to_h)
    ]
end

#to_sObject


151
# File 'lib/orogen/spec/task_context.rb', line 151

def to_s; "#<OroGen::Spec::TaskContext: #{name}>" end

#toplevel_states(*state_names) ⇒ Object

Define the state machine's toplevel states, usually used only on a root model


740
741
742
743
744
# File 'lib/orogen/spec/task_context.rb', line 740

def toplevel_states(*state_names)
    state_names.each do |name|
        define_state(name, :toplevel)
    end
end

#typeObject

:method: each_fatal_state

Enumerates all error states defined for this task context

See also #each_runtime_state, #each_exception_state, #each_error_state and #each_state


698
699
700
701
702
703
704
705
706
707
708
709
710
# File 'lib/orogen/spec/task_context.rb', line 698

STATE_TYPES.each do |type|
    class_eval <<-EOD
    def each_#{type}_state
        if block_given?
            each_state do |name, type|
                yield(name) if type == :#{type}
            end
        else
            enum_for(:each_#{type}_state)
        end
    end
    EOD
end

#use_qtObject


174
# File 'lib/orogen/spec/task_context.rb', line 174

def use_qt; @uses_qt = true; end

#uses_qt?Boolean

Returns:

  • (Boolean)

175
# File 'lib/orogen/spec/task_context.rb', line 175

def uses_qt?; @uses_qt; end

#worstcase_processing_timeObject

:method: worstcase_processing_time :call-seq:

worstcase_processing_time => value
worstcase_processing_time value

Sets or gets the worst-case computation time (i.e. time spent in 'update') for this task context. This should usually not be set in the oroGen file, but in the supervision/deployment code, since the actual computation time will depend on the system.

The time is given in seconds


240
241
242
# File 'lib/orogen/spec/task_context.rb', line 240

dsl_attribute :worstcase_processing_time do |value|
    Float(value)
end