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
This package is part of the rubim.orocos package set of RubyInMotion. See http://sites.google.com/site/rubyinmotion for installation instructions.
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 = ::. '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.
::. '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 = .. 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 = ..("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.