class Orocos::ROS::Node

A TaskContext-compatible interface of a ROS node

The following caveats apply:

Attributes

doc[R]
exit_status[R]

@return [Integer] if started by this Ruby process, and then stopped,

the exit status object that represents how the node finished
name_mappings[R]

@return [NameMappings] the name mappings that are applied from the

node implementation to this running node. This is useful only
when using a model
name_service[R]
NameService

access to the state of the ROS graph

pid[R]

@return [Integer] if started by this Ruby process, the PID of the

ROS node process
server[R]
ROSSlave

access to the node XMLRPC API

topics[R]
Hash<String,Topic>

a cache of the topics that are known to be

associated with this node. It should never be used directly, as it may contain stale entries. The key is the port name of the topic

Public Class Methods

new(name_service, server, name = nil, options = Hash.new) click to toggle source
Calls superclass method Orocos::TaskContextBase.new
# File lib/orocos/ros/node.rb, line 35
def initialize(name_service, server, name = nil, options = Hash.new)
    @name_service = name_service
    @server = server
    @input_topics = Hash.new
    @output_topics = Hash.new
    @name_mappings = NameMappings.new

    if name.kind_of?(Hash)
        name, options = nil, name
    end

    with_defaults, options = Kernel.filter_options options,
        :model => OroGen::ROS::Spec::Node.new(nil,name),
        :namespace => name_service.namespace
    options = options.merge(with_defaults)

    # We allow models to be specified by name
    if options[:model].respond_to?(:to_str)
        options[:model] = Orocos.default_loader.task_model_from_name(options[:model])
    end
    # Initialize the name from the model if it has one, and no name
    # was given
    if !name
        if options[:model]
            name = options[:model].name.gsub(/.*::/, '')
        else
            raise ArgumentError, "no name and no model given. At least one of the two must be provided."
        end
    end
    super(name, options)

    if running?
        @state_queue << :RUNNING
    else
        @state_queue << :PRE_OPERATIONAL
    end
end

Public Instance Methods

==(other) click to toggle source
# File lib/orocos/ros/node.rb, line 78
def ==(other)
    other.class == self.class &&
        other.name_service == self.name_service &&
        other.name == self.name
end
apply_name_mappings(name) click to toggle source

Applies the name mappings configured on this node on the given name.

In effect, given a name from {model}, it gives the name of the corresponding object in the context of the running node

# File lib/orocos/ros/node.rb, line 215
def apply_name_mappings(name)
    name_mappings.apply(name)
end
attribute_names() click to toggle source
# File lib/orocos/ros/node.rb, line 253
def attribute_names; [] end
cleanup(wait_for_completion = true) click to toggle source
# File lib/orocos/ros/node.rb, line 175
def cleanup(wait_for_completion = true)
    # This is no-op for ROS nodes
end
configure(wait_for_completion = true) click to toggle source
# File lib/orocos/ros/node.rb, line 119
def configure(wait_for_completion = true)
    # This is a no-op for ROS nodes
    if state == :PRE_OPERATIONAL
        @state_queue << :STOPPED
    else
        ROS.warn "setting state of Orocos::ROS::Node '#{ros_name}' to #{state}, though true configuration of #{self} is not supported."
        raise StateTransitionFailed, "#{self} cannot be configured in state #{state}"
    end
end
dead!(exit_status) click to toggle source
# File lib/orocos/ros/node.rb, line 90
def dead!(exit_status)
    exit_status = (@exit_status ||= exit_status)

    if !exit_status
        ROS.info "deployment #{name} exited, exit status unknown"
    elsif exit_status.success?
        ROS.info "deployment #{name} exited normally"
    elsif exit_status.signaled?
        if @expected_exit == exit_status.termsig
            ROS.info "ROS node #{name} terminated with signal #{exit_status.termsig}"
        elsif @expected_exit
            ROS.info "ROS node #{name} terminated with signal #{exit_status.termsig} but #{@expected_exit} was expected"
        else
            ROS.warn "ROS node #{name} unexpectedly terminated with signal #{exit_status.termsig}"
            @state_queue << :EXCEPTION
        end
    else
        ROS.warn "ROS node #{name} terminated with code #{exit_status.to_i}"
        @state_queue << :EXCEPTION
    end

    if @state_queue.last != :EXCEPTION
        @state_queue << :STOPPED
    end

    @pid = nil 
    @server = nil
end
doc?() click to toggle source
# File lib/orocos/ros/node.rb, line 243
def doc?; false end
each_input_port(verify = true) { |input_topics ||= input_topic| ... } click to toggle source

Enumerates each “input topics” of this node

# File lib/orocos/ros/node.rb, line 371
def each_input_port(verify = true)
    return enum_for(:each_input_port, verify) if !block_given?

    if !verify
        return @input_topics.values.each(&proc)
    end

    if !running?
        model.each_input_port do |m|
            yield(@input_topics[m.name] ||= InputTopic.new(self, m.topic_name, m.topic_type, m.name))
        end
    else
        name_service.input_topics_for(ros_name).each do |topic_name|
            topic_type = name_service.topic_message_type(topic_name)
            if ROS.compatible_message_type?(topic_type)
                name, model = resolve_input_topic_name(topic_name)
                topic = (@input_topics[name] ||= InputTopic.new(self, topic_name, topic_type, model, name))
                yield(topic)
            end
        end
    end
end
each_output_port(verify = true) { |output_topics ||= output_topic| ... } click to toggle source

Enumerates each “output topics” of this node

# File lib/orocos/ros/node.rb, line 347
def each_output_port(verify = true)
    return enum_for(:each_output_port, verify) if !block_given?

    if !verify
        return @output_topics.values.each(&proc)
    end
    
    if !running?
        model.each_output_port do |m|
            yield(@output_topics[m.name] ||= OutputTopic.new(self, m.topic_name, m.topic_type, m.name))
        end
    else
        name_service.output_topics_for(ros_name).each do |topic_name, topic_type|
            topic_type = name_service.topic_message_type(topic_name)
            if ROS.compatible_message_type?(topic_type)
                name, model = resolve_output_topic_name(topic_name)
                topic = (@output_topics[name] ||= OutputTopic.new(self, topic_name, topic_type, model, name))
                yield(topic)
            end
        end
    end
end
each_port(verify = true) { |p| ... } click to toggle source
# File lib/orocos/ros/node.rb, line 318
def each_port(verify = true)
    return enum_for(:each_port, verify) if !block_given?
    each_output_port(verify) { |p| yield(p) }
    each_input_port(verify) { |p| yield(p) }
end
each_property() click to toggle source
# File lib/orocos/ros/node.rb, line 246
def each_property; end
find_input_port(name, verify = true, wait_if_unavailable = true) click to toggle source

Finds the name of a topic this node is subscribed to

@return [ROS::Topic,nil] the topic if found, nil otherwise

# File lib/orocos/ros/node.rb, line 306
def find_input_port(name, verify = true, wait_if_unavailable = true)
    each_input_port(verify) do |p|
        if p.name == name || p.topic_name == OroGen::ROS.normalize_topic_name(name)
            return p
        end
    end
    if verify && wait_if_unavailable
        name_service.wait_for_update
        find_input_port(name, true, false)
    end
end
find_output_port(name, verify = true, wait_if_unavailable = true) click to toggle source

Finds the name of a topic this node is publishing

@return [ROS::Topic,nil] the topic if found, nil otherwise

# File lib/orocos/ros/node.rb, line 291
def find_output_port(name, verify = true, wait_if_unavailable = true)
    each_output_port(verify) do |p|
        if p.name == name || p.topic_name == OroGen::ROS.normalize_topic_name(name)
            return p
        end
    end
    if verify && wait_if_unavailable
        name_service.wait_for_update
        find_output_port(name, true, false)
    end
end
has_port?(name) click to toggle source
# File lib/orocos/ros/node.rb, line 256
def has_port?(name)
    verify = true
    if model.spec_available?
        verify = false
    end
    !!(find_output_port(name, verify) || find_input_port(name, verify))
end
implements?(class_name) click to toggle source

True if this task's model is a subclass of the provided class name

This is available only if the deployment in which this task context runs has been generated by orogen.

# File lib/orocos/ros/node.rb, line 223
def implements?(class_name)
    model.implements?(class_name)
end
input_port(name, verify = true) click to toggle source
# File lib/orocos/ros/node.rb, line 272
def input_port(name, verify = true)
    p = find_input_port(name, verify)
    if !p
        raise Orocos::NotFound, "cannot find topic #{name} as a subscription of node #{self.name}"
    end
    p
end
join() click to toggle source
# File lib/orocos/ros/node.rb, line 164
def join
    return if !running?

    begin
        ::Process.waitpid(pid)
        exit_status = $?
            dead!(exit_status)
    rescue Errno::ECHILD
    end
end
kill() click to toggle source
# File lib/orocos/ros/node.rb, line 160
def kill
    ::Process.kill('INT', @pid)
end
log_all_configuration(logfile) click to toggle source
# File lib/orocos/ros/node.rb, line 440
def log_all_configuration(logfile)
    # n/a for ROS node
end
operation_names() click to toggle source
# File lib/orocos/ros/node.rb, line 254
def operation_names; [] end
output_port(name, verify = true) click to toggle source
# File lib/orocos/ros/node.rb, line 280
def output_port(name, verify = true)
    p = find_output_port(name, verify)
    if !p
        raise Orocos::NotFound, "cannot find topic #{name} as a publication of node #{self.name}"
    end
    p
end
ping() click to toggle source

Tests if this node is still available

@raise [Orocos::ComError] if the node is not available anymore

# File lib/orocos/ros/node.rb, line 434
def ping
    if !name_service.has_node?(name)
        raise Orocos::ComError, "ROS node #{name} is not available on the ROS graph anymore"
    end
end
port(name, verify = true) click to toggle source
# File lib/orocos/ros/node.rb, line 264
def port(name, verify = true)
    p = (find_output_port(name, verify) || find_input_port(name, verify))
    if !p
        raise Orocos::NotFound, "cannot find topic #{name} attached to node #{self.name}"
    end
    p
end
port_names() click to toggle source
# File lib/orocos/ros/node.rb, line 248
def port_names
    each_port.map(&:name)
end
pretty_print(pp) click to toggle source
# File lib/orocos/ros/node.rb, line 394
def pretty_print(pp)
    pp.text "ROS Node #{name}"
    pp.breakable

    inputs  = each_input_port.to_a
    outputs = each_output_port.to_a
    ports = enum_for(:each_port).to_a
    if ports.empty?
        pp.text "No ports"
        pp.breakable
    else
        pp.text "Ports:"
        pp.breakable
        pp.nest(2) do
            pp.text "  "
            each_port do |port|
                port.pretty_print(pp)
                pp.breakable
            end
        end
        pp.breakable
    end
end
property_names() click to toggle source
# File lib/orocos/ros/node.rb, line 252
def property_names; [] end
reachable?() click to toggle source

Tests if this node is still available on the ROS system

A node is reachable if it is still available on the ROS graph

@return [Boolean]

# File lib/orocos/ros/node.rb, line 236
def reachable?
    name_service.has_node?(name)
    true
rescue ComError
    false
end
reset_exception(wait_for_completion = true) click to toggle source
# File lib/orocos/ros/node.rb, line 179
def reset_exception(wait_for_completion = true)
    @state_queue << :STOPPED
    @exit_status = nil
end
resolve_input_topic_name(topic_name) click to toggle source

Resolves the given topic name into a port name and a port model. @return [(String,OroGen::ROS::Spec::InputTopic),(String,nil)]

# File lib/orocos/ros/node.rb, line 337
def resolve_input_topic_name(topic_name)
    model.each_input_port do |m|
        if apply_name_mappings(m.topic_name) == OroGen::ROS.normalize_topic_name(topic_name)
            return m.name, m
        end
    end
    return Topic.default_port_name(topic_name), nil
end
resolve_output_topic_name(topic_name) click to toggle source

Resolves the given topic name into a port name and a port model. @return [(String,OroGen::ROS::Spec::OutputTopic),(String,nil)]

# File lib/orocos/ros/node.rb, line 326
def resolve_output_topic_name(topic_name)
    model.each_output_port do |m|
        if apply_name_mappings(m.topic_name) == OroGen::ROS.normalize_topic_name(topic_name)
            return m.name, m
        end
    end
    return Topic.default_port_name(topic_name), nil
end
ros_name() click to toggle source
# File lib/orocos/ros/node.rb, line 73
def ros_name
    _, basename = split_name(name)
    OroGen::ROS.rosnode_normalize_name(basename)
end
rtt_state() click to toggle source
# File lib/orocos/ros/node.rb, line 227
def rtt_state
    @state_queue.last || @current_state
end
running?() click to toggle source

@return [Boolean] true if this Node is already running (somewhere)

and false otherwise
# File lib/orocos/ros/node.rb, line 86
def running?
    !!server
end
shutdown(wait_for_completion = true) click to toggle source
# File lib/orocos/ros/node.rb, line 150
def shutdown(wait_for_completion = true)
    if !running?
        raise StateTransitionFailed, "#{self} is not running"
    end
    kill
    if wait_for_completion
        join
    end
end
spawn() click to toggle source

Starts this node

# File lib/orocos/ros/node.rb, line 185
def spawn
    args = name_mappings.to_command_line
    package_name, bin_name = *model.name.split("::")
    binary = OroGen::ROS.rosnode_find(package_name.gsub(/^ros_/, ''), bin_name)
    @pid = Utilrb.spawn binary, "__name:=#{name}", *args
end
start(wait_for_completion = true) click to toggle source
# File lib/orocos/ros/node.rb, line 129
def start(wait_for_completion = true)
    if running?
        if state == :RUNNING
            raise StateTransitionFailed, "#{self} is already running"
        else
            @state_queue << :RUNNING
            ROS.warn "setting state of Orocos::ROS::Node '#{ros_name}' to #{state}, though true start of #{self} is not performed, since the node was already started."
        end
    end

    spawn
    if wait_for_completion
        wait_running
    end
end
stop(wait_for_completion = true) click to toggle source
# File lib/orocos/ros/node.rb, line 145
def stop(wait_for_completion = true)
    @state_queue << :STOPPED
    ROS.warn "setting state of Orocos::ROS::Node '#{ros_name}' to #{state}, though true stopping of Orocos::ROS::Node is not performed. Use #shutdown for halting"
end
to_async(options = Hash.new) click to toggle source

@return [Orocos::Async::ROS::Node] an object that gives

asynchronous access to this particular ROS node
# File lib/orocos/ros/node.rb, line 420
def to_async(options = Hash.new)
    Async::ROS::Node.new(name_service, server, name, options)
end
to_proxy(options = Hash.new) click to toggle source
# File lib/orocos/ros/node.rb, line 424
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.merge(:name_service => name_service))
end
wait_running(timeout = nil) click to toggle source

Waits for this node to be available

# File lib/orocos/ros/node.rb, line 193
def wait_running(timeout = nil)
    return if @server

    now = Time.now
    while true
        begin
            node = name_service.get name
            @server = node.server
            break
        rescue Orocos::NotFound
        end
        if timeout && (Time.now - now) > timeout
            raise Orocos::NotFound, "#{self} is still not reachable after #{timeout} seconds"
        end
    end
end