Orocos.rb: deploying and supervising Orocos/RTT modules

Orocos.rb is a Ruby library which allows to access, read out and control Orocos RTT (http://www.orocos.org) components. It uses the CORBA transport of Orocos for that, so you need to have the CORBA library installed, as well as the rtt-corba library.

Most intersting features of Orocos.rb, like reading/writing ports and properties, starting out processes, ... Require the module to be generated by orogen. This is because of the type handling library that orogen is built on, and could probably be changed in the future. The rest of the documentation will assume that the modules you are working on have been orogen-generated.

All script snippets in this documentation are Ruby scripts, and assume that you have first done:

  require 'orocos'
  include Orocos

Installation

This package is part of the rubim.orocos package set of RubyInMotion. See http://sites.google.com/site/rubyinmotion for installation instructions.

Starting processes

As always with orogen, the dependency tracking is done using pkg-config. So make sure that the PKG_CONFIG_PATH environment variable is set properly: it should include the lib/pkgconfig subdirectory of where you installed the orogen module.

Then, start your modules with

  p1, p2 = Orocos::Process.spawn 'canbus', 'hokuyo'

where canbus and hokuyo are the names of the processes as deployed by orogen, i.e. the name given to #deployment in the orogen specification:

  deployment 'canbus' do
    # Spawn task contexts
  end

Alternatively, you can use the block form. In this form, when the do ... end block is exited, then the processes are properly shut down.

  Orocos::Process.spawn 'canbus', 'hokuyo' do |p1, p2|
    # Use the process objects.
  end

In both cases, +p1+ and +p2+ refer to Orocos::Process instances that respectively represent the +canbus+ and +hokuyo+ processes.

See the documentation of Orocos::Process for more details.

== Getting a grasp on existing task contexts

You can grab an object represeting the remote RTT component with:

  c = TaskContext.new IOR
  # or 
  d = Orocos.name_service.get TaskName

where TaskName is the name given to the task during deployment and IOR the Interoperable Object Reference (IOR). You can list all existing tasks using the orocos_state script included in orocos.rb.

Alternatively, one can get a task based on the interfaces it implements. In orogen, this is marked by the class hierarchy (i.e. either directly the task's class name or one of its parent class names).

For instance, with the orogen specification looking like:

  name "controldev"
  task_context "Device" do
    ... generic interface definition ...
  end
  task_context "Joystick" do
    subclasses "Device"
    ... specific joystick parts ...
  end
  task_context "Joypad" do
    subclasses "Device"
    ... specific joypad parts ...
  end
  task_context "Remote" do
    subclasses "Device"
    ... specific remote parts ...
  end

During deployment, you would choose the "right" one for your system. For instance, a local computer would use controldev::Joystick, the robot would require a remote control and therefore use controldev::Remote and so on.

Though, the script will be independent from that choice because you can use

  c = Orocos.name_service.get_provides("controldev::Device")

Orocos.name_service.get_provides returns a Orocos::TaskContext instance. See the documentation of that class for more details.

Changing and reading a task context's state

Manipulating a task context

You can change the task's properties with:

  # This property is a "simple" type (i.e. not a struct)
  c.device_name = "/dev/ttyUSB0"

  # This one is a struct, so get the component's default value and change it 
  value = c.parameters
  value.maxSpeed = 20
  value. = 200
  c.propertyName = value

The properties are represented by Orocos::Attribute objects. In the same way, ports can be accessed through Orocos::InputPort and Orocos::OutputPort objects:

  output_port = c.currentReadings
  input_port  = c.speed

Reading/writing these ports is done with:

  port_reader = c.currentReadings.reader
  port_reader.read # => returns the current reading instance

  port_writer = c.speed.writer
  port_writer.write(10) # => returns the current reading instance

If the port's type is a structure, then you can either do:

  sample = port_writer.new_sample # or c.speed.new_sample
  sample.value = 100
  port_writer.write(sample)

or represent the structure through a hash:

  port_writer.write(:value => 100)

Orocos::InputPort#writer and Orocos::OutputPort#reader return respectively Orocos::InputWriter and Orocos::OutputReader objects. These objects are connected to the remote ports using the standard RTT data flow mechanism, so it is possible to specify a connection policy:

  port_reader = c.currentReadings.reader(:type => :buffer, :size => 10, :pull => true)
  port_writer = c.speed.writer(:type => :buffer, :size => 10, :pull => true)

See Orocos::Port#validate_policy for an in-depth explanation of possible values.

Finally, the task's methods can be used in this way:

  m = c.rtt_method('watch')
  ret = m.call(arg1, arg2)

and commands are used:

  m = c.rtt_command('watch')
  handle = m.call(arg1, arg2)
  # Check if the command is finished

Both calls store the arguments on the remote component, so if the same method is to be called repeatedly, use #recall:

  m = c.rtt_method('watch')
  ret = m.call(arg1, arg2)
  ret2 = m.recall

Orocos::TaskContext#rtt_method returns an Orocos::RTTMethod object while Orocos::TaskContext#rtt_command returns an Orocos::Command one.

Connecting task contexts together

It is possible to connect ports using orocos.rb. Simply do:

  # Get the InputPort and OutputPort instances# Get the InputPort and OutputPort
  instances# Get the InputPort and OutputPort instances
  input_port  = c.input
  output_port = c.output
  output_port.connect_to(input_port, policy)

+policy+ is the connection. It is a hash whose arguments are the policy parameters. See Orocos::Port#validate_policy for an in-depth explanation of possible values.