Class: Orocos::TaskContext

Inherits:
Object
  • Object
show all
Defined in:
lib/orocos/task_context.rb,
lib/orocos/async/orocos.rb

Overview

A proxy for a remote task context. The communication between Ruby and the RTT component is done through the CORBA transport.

See README.txt for information on how you can manipulate a task context through this class.

The available information about this task context can be displayed using Ruby's pretty print library:

require 'pp'
pp task_object

Defined Under Namespace

Modules: StateReader

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ior, options = Hash.new) ⇒ TaskContext

A new TaskContext instance representing the remote task context with the given IOR

If a remote task is only known by its name use Orocos.name_service to create an handle to the remote task.

Parameters:

  • ior (String)

    The IOR of the remote task.

  • options (Hash) (defaults to: Hash.new)

    The options.

Options Hash (options):

  • :name (String)

    Overwrites the real name of remote task

  • :process (Orocos::Process)

    The process supporting the task

  • :namespace (String)

    The namespace of the task


135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/orocos/task_context.rb', line 135

def initialize(ior,options=Hash.new)
    options,other_options = Kernel.filter_options options,:name

    name = if options.has_key?(:name)
               options[:name]
           else
               do_real_name
           end
    super(name,other_options)
    @ior = ior

    if process && (process.default_logger_name != name)
        self.logger = process.default_logger
    end
end

Instance Attribute Details

#logger#log

The logger task that should be used to log data that concerns this task

Returns:

  • (#log)

122
123
124
# File 'lib/orocos/task_context.rb', line 122

def logger
  @logger
end

Class Method Details

.corba_wrap(m, *args) ⇒ Object

Automated wrapper to handle CORBA exceptions coming from the C extension


78
79
80
81
82
83
84
# File 'lib/orocos/task_context.rb', line 78

def self.corba_wrap(m, *args) # :nodoc:
    class_eval <<-EOD
    def #{m}(#{args.join(". ")})
        CORBA.refine_exceptions(self) { do_#{m}(#{args.join(", ")}) }
    end
    EOD
end

.state_transition_call(m, expected_state, target_state) ⇒ Object


86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/orocos/task_context.rb', line 86

def self.state_transition_call(m, expected_state, target_state)
    class_eval <<-EOD, __FILE__, (__LINE__ + 1)
    def #{m}(wait_for_completion = true, polling = 0.05)
        if wait_for_completion
            current_state = peek_current_state
        end
        CORBA.refine_exceptions(self) do
            begin
                do_#{m}
            rescue Orocos::StateTransitionFailed => e
                current_state = rtt_state
                reason =
                    if current_state == :EXCEPTION
                        ". The task is in an exception state. You must call #reset_exception before trying again"
                    elsif current_state == :PRE_OPERATIONAL && '#{m}' == 'start'
                        ". The Task must be configured before it could started. Did you forgot to call configure on the task?"
                    elsif current_state != :#{expected_state}
                        ". Tasks must be in #{expected_state} state before calling #{m}, but was in \#{current_state}"
                    end

                raise e, "\#{e.message} the '\#{self.name}' task\#{ " of type \#{self.model.name}" if self.model}\#{reason}", e.backtrace
            end
        end
        if wait_for_completion
            while current_state == peek_current_state#{" && current_state != :#{target_state}" if target_state}
                sleep polling
            end
        end
    end
    EOD
end

Instance Method Details

#apply_conf(section_names = Array.new, override = false) ⇒ Object

Applies the TaskContext configuration stored by the main configuration manager to the TaskContext

See also #load_conf and #Orocos.load_config_dir


301
302
303
# File 'lib/orocos/task_context.rb', line 301

def apply_conf(section_names = Array.new, override=false)
    Orocos.conf.apply(self, section_names, override)
end

#apply_conf_file(file, section_names = Array.new, override = false) ⇒ Object

Loads the configuration for the TaskContext from a file, into the main configuration manager and applies it to the TaskContext

See also #apply_conf and #Orocos.load_config_dir


288
289
290
291
# File 'lib/orocos/task_context.rb', line 288

def apply_conf_file(file,section_names=Array.new,override=false)
    Orocos.conf.load_file(file,model.name)
    apply_conf(section_names,override)
end

#attribute(name) ⇒ Object

Returns an Attribute object representing the given attribute

Raises NotFound if no such attribute exists.

Attributes can also be read and written by calling directly the relevant method on the task context:

task.attribute("myProperty").read
task.attribute("myProperty").write(value)

is equivalent to

task.myProperty
task.myProperty = value

432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
# File 'lib/orocos/task_context.rb', line 432

def attribute(name)
    name = name.to_s
    if a = attributes[name]
        if has_attribute?(name)
            return a
        else
            attributes.delete(name)
            raise Orocos::InterfaceObjectNotFound.new(self, name), "task #{self.name} does not have an attribute named #{name}", e.backtrace
        end
    end

    type_name = CORBA.refine_exceptions(self) do
        begin
            do_attribute_type_name(name)
        rescue ArgumentError => e
            raise Orocos::InterfaceObjectNotFound.new(self, name), "task #{self.name} does not have an attribute named #{name}", e.backtrace
        end
    end

    a = Attribute.new(self, name, type_name)
    if configuration_log
        create_property_log_stream(a)
        a.log_current_value
    end
    attributes[name] = a
end

#attribute_namesObject

Returns the array of the names of available attributes on this task context


411
412
413
414
415
# File 'lib/orocos/task_context.rb', line 411

def attribute_names
    CORBA.refine_exceptions(self) do
        do_attribute_names
    end
end

#callop(name, *args) ⇒ Object

Calls the required operation with the given argument

This is a shortcut for operation(name).calldop(*arguments)


566
567
568
# File 'lib/orocos/task_context.rb', line 566

def callop(name, *args)
    operation(name).callop(*args)
end

#cleanupObject

:method: cleanup

Cleans the component, i.e. do the transition from STATE_STOPPED into STATE_PRE_OPERATIONAL.

Raises StateTransitionFailed if the component was not in STATE_STOPPED state before the call. The component cannot refuse to perform the transition (but can take an arbitrarily long time to do it).


363
# File 'lib/orocos/task_context.rb', line 363

state_transition_call :cleanup, 'STOPPED', 'PRE_OPERATIONAL'

#configureObject

:method: configure

Configures the component, i.e. do the transition from STATE_PRE_OPERATIONAL into STATE_STOPPED.

Raises StateTransitionFailed if the component was not in STATE_PRE_OPERATIONAL state before the call, or if the component refused to do the transition (startHook() returned false)


319
# File 'lib/orocos/task_context.rb', line 319

state_transition_call :configure, 'PRE_OPERATIONAL', 'STOPPED'

#connect_to(sink, policy = Hash.new) ⇒ Object


612
613
614
615
616
617
618
# File 'lib/orocos/task_context.rb', line 612

def connect_to(sink, policy = Hash.new)
    port = find_output_port(sink.type, nil)
    if !port
        raise ArgumentError, "port #{sink.name} does not match any output port of #{name}"
    end
    port.connect_to(sink, policy)
end

#create_property_log_stream(p) ⇒ Object


247
248
249
250
251
252
253
254
# File 'lib/orocos/task_context.rb', line 247

def create_property_log_stream(p)
    stream_name = "#{self.name}.#{p.name}"
    if !configuration_log.has_stream?(stream_name)
        p.log_stream = configuration_log.create_stream(stream_name, p.type, p.)
    else
        p.log_stream = configuration_log.stream(stream_name)
    end
end

#disconnect_from(sink, policy = Hash.new) ⇒ Object


620
621
622
623
624
625
626
627
# File 'lib/orocos/task_context.rb', line 620

def disconnect_from(sink, policy = Hash.new)
    each_output_port do |out_port|
        if out_port.type == sink.type
            out_port.disconnect_from(sink)
        end
    end
    nil
end

#has_operation?(name) ⇒ Boolean

Returns true if this task context has a command with the given name

Returns:

  • (Boolean)

366
367
368
369
370
371
372
373
374
375
# File 'lib/orocos/task_context.rb', line 366

def has_operation?(name)
    name = name.to_s
    CORBA.refine_exceptions(self) do
        begin
            do_has_operation?(name)
        rescue Orocos::NotFound
            false
        end
    end
end

#has_port?(name) ⇒ Boolean

Returns true if this task context has a port with the given name

Returns:

  • (Boolean)

378
379
380
381
382
383
384
385
386
387
# File 'lib/orocos/task_context.rb', line 378

def has_port?(name)
    name = name.to_s
    CORBA.refine_exceptions(self) do
        begin
            do_has_port?(name)
        rescue Orocos::NotFound
            false
        end
    end
end

#log_all_configuration(logfile) ⇒ Object

Tell the task to use the given Pocolog::Logfile object to log all changes to its properties


258
259
260
261
262
263
264
# File 'lib/orocos/task_context.rb', line 258

def log_all_configuration(logfile)
    @configuration_log = logfile
    each_property do |p|
        create_property_log_stream(p)
        p.log_current_value
    end
end

#log_all_ports(options = Hash.new) ⇒ Set<String,String>

Connects all ports of the task with the logger of the deployment task.log_all_ports(:exclude_ports => “frame”)

Examples:

logging all ports beside a port called frame

Parameters:

  • options (Hash) (defaults to: Hash.new)

    option hash to exclude specific ports

Options Hash (options):

  • :exclude_ports (String, Array<String>)

    The name of the excluded ports

Returns:

  • (Set<String,String>)

    Sets of task and port names


234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/orocos/task_context.rb', line 234

def log_all_ports(options = Hash.new)
    # Right now, the only allowed option is :exclude_ports
    options, logger_options = Kernel.filter_options options,:exclude_ports => nil
    exclude_ports = Array(options[:exclude_ports])

    logger_options[:tasks] = Regexp.new(basename)
    ports = Orocos.log_all_process_ports(process,logger_options) do |port|
        !exclude_ports.include? port.name
    end
    raise "#{name}: no ports were selected for logging" if ports.empty?
    ports
end

#modelObject

Returns the Orogen specification object for this task's model. It will return a default model if the remote task does not respond to getModelName or the description file cannot be found.

See also #info


582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
# File 'lib/orocos/task_context.rb', line 582

def model
    model = super
    if model
        return model
    end

    model_name = begin
                     self.getModelName
                 rescue NoMethodError
                     nil
                 end

    self.model =
        if !model_name
            if name !~ /.*orocosrb_(\d+)$/
                Orocos.warn "#{name} is a task context not generated by orogen, using default task model"
            end
            Orocos.create_orogen_task_context_model(name)
        elsif model_name.empty?
            Orocos.create_orogen_task_context_model
        else
            begin
                Orocos.default_loader.task_model_from_name(model_name)
            rescue OroGen::NotFound
                Orocos.warn "#{name} is a task context of class #{model_name}, but I cannot find the description for it, falling back"
                Orocos.create_orogen_task_context_model(model_name)
            end
        end
end

#operation(name) ⇒ Object

Returns an Operation object that represents the given method on the remote component.

Raises NotFound if no such operation exists.


551
552
553
554
555
556
557
558
559
560
561
# File 'lib/orocos/task_context.rb', line 551

def operation(name)
    name = name.to_s
    CORBA.refine_exceptions(self) do
        return_types = operation_return_types(name)
        arguments = operation_argument_types(name)
        Operation.new(self, name, return_types, arguments)
    end

rescue Orocos::NotFound => e
    raise Orocos::InterfaceObjectNotFound.new(self, name), "task #{self.name} does not have an operation named #{name}", e.backtrace
end

#operation_namesObject

Returns the array of the names of available operations on this task context


391
392
393
394
395
396
397
# File 'lib/orocos/task_context.rb', line 391

def operation_names
    CORBA.refine_exceptions(self) do
        do_operation_names.each do |str|
            str.force_encoding('ASCII') if str.respond_to?(:force_encoding)
        end
    end
end

#peek_stateObject

Reads all state transitions that have been announced by the task and pushes them to @state_queue

The following call to #state will first look at @state_queue before accessing the task context


191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/orocos/task_context.rb', line 191

def peek_state
    if model && model.extended_state_support?
	    	if !@state_reader || !@state_reader.connected?
            @state_reader = state_reader
            @state_queue << rtt_state
		end
        while new_state = @state_reader.read_new
            @state_queue << new_state
        end
    else
        super
    end
    @state_queue
end

#pingObject


151
152
153
154
# File 'lib/orocos/task_context.rb', line 151

def ping
    rtt_state
    nil
end

#port(name, verify = true) ⇒ Object

Returns an object that represents the given port on the remote task context. The returned object is either an InputPort or an OutputPort

Raises NotFound if no such port exists.

Ports can also be accessed by calling directly the relevant method on the task context:

task.port("myPort")

is equivalent to

task.myPort

515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
# File 'lib/orocos/task_context.rb', line 515

def port(name, verify = true)
    name = name.to_str
    CORBA.refine_exceptions(self) do
        if @ports[name]
            if !verify || has_port?(name) # Check that this port is still valid
                @ports[name]
            else
                @ports.delete(name)
                raise NotFound, "no port named '#{name}' on task '#{self.name}'"
            end
        else
            port_model = if model
                             model.find_port(name)
                         end
            @ports[name] = do_port(name, port_model)
        end
    end

rescue Orocos::NotFound => e
    raise Orocos::InterfaceObjectNotFound.new(self, name), "task #{self.name} does not have a port named #{name}", e.backtrace
end

#port_namesObject

Returns the names of all the ports defined on this task context


539
540
541
542
543
544
545
# File 'lib/orocos/task_context.rb', line 539

def port_names
    CORBA.refine_exceptions(self) do
        do_port_names.each do |str|
            str.force_encoding('ASCII') if str.respond_to?(:force_encoding)
        end
    end
end

#property(name) ⇒ Object

Returns a Property object representing the given property

Raises NotFound if no such property exists.

Ports can also be accessed by calling directly the relevant method on the task context:

task.property("myProperty").read
task.property("myProperty").write(value)

is equivalent to

task.myProperty
task.myProperty = value

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
499
# File 'lib/orocos/task_context.rb', line 474

def property(name)
    name = name.to_s
    if p = properties[name]
        if has_property?(name)
            return p
        else
            properties.delete(name)
            raise Orocos::InterfaceObjectNotFound.new(self, name), "task #{self.name} does not have a property named #{name}", e.backtrace
        end
    end

    type_name = CORBA.refine_exceptions(self) do
        begin
            do_property_type_name(name)
        rescue ArgumentError => e
            raise Orocos::InterfaceObjectNotFound.new(self, name), "task #{self.name} does not have a property named #{name}", e.backtrace
        end
    end

    p = Property.new(self, name, type_name)
    if configuration_log
        create_property_log_stream(p)
        p.log_current_value
    end
    properties[name] = p
end

#property_namesObject

Returns the array of the names of available properties on this task context


401
402
403
404
405
406
407
# File 'lib/orocos/task_context.rb', line 401

def property_names
    CORBA.refine_exceptions(self) do
        do_property_names.each do |str|
            str.force_encoding('ASCII') if str.respond_to?(:force_encoding)
        end
    end
end

#reset_exceptionObject

:method: reset_exception

Recover from the exception state. It does the transition from STATE_EXCEPTION to either STATE_STOPPED if the component does not need any configuration or STATE_PRE_OPERATIONAL otherwise

Raises StateTransitionFailed if the component was not in a proper state before the call.


341
# File 'lib/orocos/task_context.rb', line 341

state_transition_call :reset_exception, 'EXCEPTION', nil

#resolve_connection_from(source, policy = Hash.new) ⇒ Object


629
630
631
632
633
634
635
# File 'lib/orocos/task_context.rb', line 629

def resolve_connection_from(source, policy = Hash.new)
    port = find_input_port(source.type,nil)
    if !port
        raise ArgumentError, "port #{source.name} does not match any input port of #{name}."
    end
    source.connect_to(port, policy)
end

#resolve_disconnection_from(source) ⇒ Object


637
638
639
640
641
642
643
644
# File 'lib/orocos/task_context.rb', line 637

def resolve_disconnection_from(source)
    each_input_port do |in_port|
        if in_port.type == source.type
            source.disconnect_from(in_port)
        end
    end
    nil
end

#rtt_stateObject

Reads the state announced by the task's getState() operation


222
223
224
225
# File 'lib/orocos/task_context.rb', line 222

def rtt_state
    value = CORBA.refine_exceptions(self) { do_state() }
    @state_symbols[value]
end

#save_conf(file, section_names = nil) ⇒ Object

Saves the current configuration into a file


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

def save_conf(file, section_names = nil)
    Orocos.conf.save(self,file,section_names)
end

#sendop(name, *args) ⇒ Object

Sends the required operation with the given argument

This is a shortcut for operation(name).sendop(*arguments)


573
574
575
# File 'lib/orocos/task_context.rb', line 573

def sendop(name, *args)
    operation(name).sendop(*args)
end

#startObject

:method: start

Starts the component, i.e. do the transition from STATE_STOPPED into STATE_RUNNING.

Raises StateTransitionFailed if the component was not in STATE_STOPPED state before the call, or if the component refused to do the transition (startHook() returned false)


330
# File 'lib/orocos/task_context.rb', line 330

state_transition_call :start, 'STOPPED', 'RUNNING'

#state_reader(policy = Hash.new) ⇒ Object

Returns a StateReader object that allows to flexibly monitor the task's state


177
178
179
180
181
182
183
184
# File 'lib/orocos/task_context.rb', line 177

def state_reader(policy = Hash.new)
    policy = Port.prepare_policy({:init => true, :type => :buffer, :size => 10}.merge(policy))

    reader = port('state').reader(policy)
    reader.extend StateReader
    reader.instance_variable_set :@state_symbols, @state_symbols
    reader
end

#stopObject

:method: stop

Stops the component, i.e. do the transition from STATE_RUNNING into STATE_STOPPED.

Raises StateTransitionFailed if the component was not in STATE_RUNNING state before the call. The component cannot refuse to perform the transition (but can take an arbitrarily long time to do it).


352
# File 'lib/orocos/task_context.rb', line 352

state_transition_call :stop, 'RUNNING', 'STOPPED'

#tidObject

Returns the PID of the thread this task runs on

This is available only on oroGen task, for which oroGen adds an orogen_getPID operation that returns this information


210
211
212
213
214
215
216
217
218
219
# File 'lib/orocos/task_context.rb', line 210

def tid
    if !@tid
        if has_operation?('__orogen_getTID')
            @tid = operation('__orogen_getTID').callop()
        else
            raise ArgumentError, "#tid is available only on oroGen tasks, not #{self}"
        end
    end
    @tid
end

#to_async(options = Hash.new) ⇒ Object


79
80
81
82
83
# File 'lib/orocos/async/orocos.rb', line 79

def to_async(options = Hash.new)
    options[:name] ||= name
    options[:ior] ||= ior
    Orocos::Async::CORBA::TaskContext.new(options)
end

#to_proxy(options = Hash.new) ⇒ Object


85
86
87
88
89
90
# File 'lib/orocos/async/orocos.rb', line 85

def to_proxy(options = Hash.new)
    options[:use] ||= to_async    # use name service to check if there is already 
    # a proxy for the task

    Orocos::Async.proxy(name,options)
end

#to_sObject


293
294
295
# File 'lib/orocos/task_context.rb', line 293

def to_s
    "#<TaskContext: #{self.class.name}/#{name}>"
end

#wait_for_state(state_name, timeout = nil, polling = 0.1) ⇒ Object

Waits for the task to be in state state_name for the specified amount of time

Raises RuntimeError on timeout


270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/orocos/task_context.rb', line 270

def wait_for_state(state_name, timeout = nil, polling = 0.1)
    state_name = state_name.to_sym

    start = Time.now
    peek_state
    while !@state_queue.include?(state_name)
        if timeout && (Time.now - start) > timeout
            raise "timing out while waiting for #{self} to be in state #{state_name}. It currently is in state #{current_state}"
        end
        sleep polling
        peek_state
    end
end