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 ]

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.



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
367
368
369
370
371
372
373
# File 'lib/orogen/spec/task_context.rb', line 327

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

Instance Attribute Details

#dynamic_portsObject (readonly)

A set of Port objects that can be created at runtime



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

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



104
105
106
# File 'lib/orogen/spec/task_context.rb', line 104

def extensions
  @extensions
end

#implemented_classesObject (readonly)

A set of classes the TaskContext has to implement as well



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

def implemented_classes
  @implemented_classes
end

#nameObject (readonly)

The task name



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

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



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

def superclass
  @superclass
end

#uses_qtObject (readonly)

True if QT is used within this Task



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

def uses_qt
  @uses_qt
end

Class Method Details

.apply_default_extensions(task_context) ⇒ Object

Apply the currently enabled default extension on the given task context model



89
90
91
92
93
# File 'lib/orogen/spec/task_context.rb', line 89

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

.blank(name = nil) ⇒ Object

Returns a blank task context model, possibly with a name



314
315
316
317
318
319
# File 'lib/orogen/spec/task_context.rb', line 314

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

.default_extensionsObject

The set of extensions that should be applied when a task context is created



83
84
85
# File 'lib/orogen/spec/task_context.rb', line 83

def default_extensions
    @default_extensions_state.last
end

.pop_default_extensions_stateObject

Pop the extensions enabled at this level, reverting to the list before the last call to #push_default_extensions_state



77
78
79
# File 'lib/orogen/spec/task_context.rb', line 77

def pop_default_extensions_state
    @default_extensions_state.pop if @default_extensions_state.size > 1
end

.push_default_extensions_state(extensions) ⇒ Object

Make the given list of extensions the default until the next #pop_default_extensions_state

Default extensions are the extensions that are enabled when a task context is created. This method allows to locally override the set of extensions

Parameters:

  • extensions (Array<String>)


71
72
73
# File 'lib/orogen/spec/task_context.rb', line 71

def push_default_extensions_state(extensions)
    @default_extensions_state.push(extensions.dup)
end

Instance Method Details

#abstractObject

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



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

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)


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

def abstract?; @abstract end

#all_portsObject



1126
1127
1128
# File 'lib/orogen/spec/task_context.rb', line 1126

def all_ports
    all_input_ports + all_output_ports
end

#ancestorsObject

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



379
380
381
382
383
384
385
386
# File 'lib/orogen/spec/task_context.rb', line 379

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.



534
535
536
537
538
# File 'lib/orogen/spec/task_context.rb', line 534

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



454
455
456
457
458
459
460
461
462
463
464
# File 'lib/orogen/spec/task_context.rb', line 454

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



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

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



292
293
294
295
296
297
298
299
300
301
302
# File 'lib/orogen/spec/task_context.rb', line 292

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



647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
# File 'lib/orogen/spec/task_context.rb', line 647

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.



1200
1201
1202
1203
# File 'lib/orogen/spec/task_context.rb', line 1200

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.



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

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.



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

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.



837
838
839
840
841
842
843
844
# File 'lib/orogen/spec/task_context.rb', line 837

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.



852
853
854
855
856
857
858
859
# File 'lib/orogen/spec/task_context.rb', line 852

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



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/orogen/spec/task_context.rb', line 122

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



1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
# File 'lib/orogen/spec/task_context.rb', line 1414

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



1135
1136
1137
1138
1139
1140
1141
1142
# File 'lib/orogen/spec/task_context.rb', line 1135

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



726
727
728
729
730
731
732
733
# File 'lib/orogen/spec/task_context.rb', line 726

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



779
780
781
782
783
# File 'lib/orogen/spec/task_context.rb', line 779

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



791
792
793
794
795
# File 'lib/orogen/spec/task_context.rb', line 791

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.



612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
# File 'lib/orogen/spec/task_context.rb', line 612

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)


635
636
637
# File 'lib/orogen/spec/task_context.rb', line 635

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



151
152
153
154
155
156
# File 'lib/orogen/spec/task_context.rb', line 151

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

#external_definition=(value) ⇒ Object

True if this task context is defined by one of our dependencies.



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

attr_predicate :external_definition?, true

#external_definition?Boolean

True if this task context is defined by one of our dependencies.

Returns:

  • (Boolean)


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

attr_predicate :external_definition?, true

#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



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

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


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

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)


1318
1319
1320
# File 'lib/orogen/spec/task_context.rb', line 1318

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.



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

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.



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

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



141
142
143
144
145
146
147
# File 'lib/orogen/spec/task_context.rb', line 141

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



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

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.



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

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)


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

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)


1187
1188
1189
1190
1191
1192
# File 'lib/orogen/spec/task_context.rb', line 1187

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)


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

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)


1253
1254
1255
# File 'lib/orogen/spec/task_context.rb', line 1253

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)


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

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)


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

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)


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

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)


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

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)


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

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)


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

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)


585
586
587
# File 'lib/orogen/spec/task_context.rb', line 585

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)



820
821
822
823
824
825
826
827
828
829
# File 'lib/orogen/spec/task_context.rb', line 820

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.



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

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)


223
224
225
226
227
# File 'lib/orogen/spec/task_context.rb', line 223

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

#initialize_copy(from) ⇒ Object



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

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



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

enumerate_inherited_map("input_port", "input_ports")

#inspectObject



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

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:



592
593
594
595
596
597
598
599
600
601
# File 'lib/orogen/spec/task_context.rb', line 592

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.



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

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.



517
518
519
520
521
522
523
524
# File 'lib/orogen/spec/task_context.rb', line 517

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)


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

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)



1026
1027
1028
1029
1030
1031
# File 'lib/orogen/spec/task_context.rb', line 1026

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.



811
812
813
814
815
816
# File 'lib/orogen/spec/task_context.rb', line 811

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



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

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



306
307
308
# File 'lib/orogen/spec/task_context.rb', line 306

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



1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
# File 'lib/orogen/spec/task_context.rb', line 1263

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



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
442
443
444
445
446
447
448
# File 'lib/orogen/spec/task_context.rb', line 404

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



388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/orogen/spec/task_context.rb', line 388

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.



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

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.



111
112
113
114
115
116
117
118
# File 'lib/orogen/spec/task_context.rb', line 111

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.



757
758
759
# File 'lib/orogen/spec/task_context.rb', line 757

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



268
269
270
271
272
273
274
275
# File 'lib/orogen/spec/task_context.rb', line 268

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=(value) ⇒ Object

:method: required_activity?

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



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

attr_predicate :required_activity?, true

#required_activity?Object

:method: required_activity?

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



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

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.



1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
# File 'lib/orogen/spec/task_context.rb', line 1108

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



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

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



767
768
769
770
771
# File 'lib/orogen/spec/task_context.rb', line 767

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

#self_portsObject



1130
1131
1132
# File 'lib/orogen/spec/task_context.rb', line 1130

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.



1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
# File 'lib/orogen/spec/task_context.rb', line 1080

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)


640
641
642
# File 'lib/orogen/spec/task_context.rb', line 640

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



671
672
673
674
675
# File 'lib/orogen/spec/task_context.rb', line 671

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



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

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.



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/orogen/spec/task_context.rb', line 189

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



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
1359
1360
1361
1362
1363
1364
1365
# File 'lib/orogen/spec/task_context.rb', line 1323

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)


1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
# File 'lib/orogen/spec/task_context.rb', line 1400

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



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

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



747
748
749
750
751
# File 'lib/orogen/spec/task_context.rb', line 747

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



705
706
707
708
709
710
711
712
713
714
715
716
717
# File 'lib/orogen/spec/task_context.rb', line 705

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



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

def use_qt; @uses_qt = true; end

#uses_qt?Boolean

Returns:

  • (Boolean)


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

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



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

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