ROS v0.1.0 ROS.Node.Spec View Source
A set of functions for declaring ROS abstractions for your Supervisor setup.
Add ROS abstractions to your lib/my_project/application.ex
like so:
iex> import ROS.Node.Spec
iex> children = [
...> node(:"/mynode", [
...> publisher(:mypub, "chatter", "std_msgs/String"),
...> subscriber("other_chatter", "std_msgs/Int16", &IO.inspect/1),
...> service_proxy(:myproxy, "add_two_ints", "rospy_tutorials/AddTwoInts"),
...> service("add_two_ints", "rospy_tutorials/AddTwoInts", fn %RospyTutorials.AddTwoInts.Request{a: a, b: b} ->
...> %RospyTutorials.AddTwoInts.Response{sum: a + b}
...> end)
...> ])
...> ]
iex> {ok?, _pid} = Supervisor.start_link(children, strategy: :one_for_one)
iex> ok?
:ok
Note that you can also write any ROS types in their module form after you’ve
compiled them with mix genmsg
or mix gensrv
iex> import ROS.Node.Spec
iex> publisher(:mypub, "chatter", StdMsgs.String)
{ROS.Publisher, %ROS.Publisher{name: :mypub, topic: "chatter", type: StdMsgs.String}}
iex> service_proxy(:myproxy, "add_two_ints", RospyTutorials.AddTwoInts)
{ROS.Service.Proxy, %ROS.Service.Proxy{name: :myproxy, service: "add_two_ints", type: RospyTutorials.AddTwoInts}}
Link to this section Summary
Types
An identifier for something that listens to messages received by a subscriber or service. Follow the naming conventions of GenServer
Functions
Creates a child spec for a node
Creates a child spec for a publisher process
Creates a child spec for a service process
Creates a child spec for a service proxy process
Creates a child spec for a subscriber process
Link to this section Types
An identifier for something that listens to messages received by a subscriber or service. Follow the naming conventions of GenServer.
Link to this section Functions
Creates a child spec for a node.
A node is the ROS equivalent of a Supervisor. You should group your publishers, subscribers, services, and service proxies as children of the node. Nodes also startup hidden processes like a Slave API server and an XML-RPC server for interacting with ROS master.
Creates a child spec for a publisher process.
Parameters
name
an atom or reference to call the publisher. This will allow you to call the publisher by name later when making a call toROS.Publisher.publish/2
.topic
the ROS topic to listen totype
the msg type expected in that topic. Either string format (“std_msgs/Int16”) or module formatStdMsgs.Int16
are accepted.
Creates a child spec for a service process.
Parameters
service
the ROS service name to listen totype
the srv type expected in that topic. Either string format (“std_srv/Bool”) or module formatStdMsgs.Bool
are accepted.
The third parameter can either be a callback function a pid or atom name.
If it’s a function, that function will be executed and the return value
will be sent as a service response. If it’s a pid or atom, the request
will be forwarded to that process using GenServer.call/2
. The reply value
from the call
will be sent to the requestor as a service response.
There may only be one listener for services.
Each call
sent to the listener will take the form of
{:service, %<service-type>.Request{}}
. (e.g.
{:service, %RospyTutorials.AddTwoInts.Request{a: 3, b: 4}}
).
Creates a child spec for a service proxy process.
Creates a child spec for a subscriber process.
Parameters
topic
the ROS topic to listen totype
the msg type expected in that topic. Either string format (“std_msgs/Int16”) or module formatStdMsgs.Int16
are accepted.
The third parameter can either be a callback function, a pid or atom name, or list of pids/atom names.
If it’s a callback, that callback function will be executed every time the subscriber receives a new message. This function call will be blocking, but the GenServer that the subscriber is running on will buffer incoming messages, so each callback call will be in order. This behavior is meant to most closely simulate the bahavior of the Python and C++ client library behaviors.
If it’s a pid or atom, the subscriber process will send a cast
message
to that pid or atom using GenServer.cast/2
containing
{:subscription, :from_subscriber_proc_name, %<incoming-message-type>{}}
(e.g. {:subscription, :from_subscriber_proc_name, %StdMsgs.String{data: "hello world"}}
).
Due to the behavior of cast
, messages should arrive in order.
If it’s a list of pids or atoms, the subscriber process will send a cast
message as described above to each of the processes in the list. There is
no garuantee about order in sending to each process.
Examples
import ROS.Node.Spec
children = [node(:mynode, [
subscriber("chatter", "std_msgs/String", &IO.inspect/1),
subscriber("other_chatter", "std_msgs/String", self()),
subscriber("another_chatter", "std_msgs/String", MyModule.ChatterServer)
])]
Supervisor.start_link(children)
flush()
# => {:"$gen_cast", {:subscription, :mynode_other_chatter, %StdMsgs.String{data: "hello world"}}}
If you’re attaching a GenServer to the subscriber, you’ll need to provide a handle for the subscription:
defmodule MyModule.ChatterServer do
use GenServer
def start_link(_otps), do: GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
def init(args), do: {:ok, args}
def handle_cast({:subscription, _from, %StdMsgs.String{data: data}}, state) do
# do something interesting with `data`
{:noreply, state}
end
end
The subscriber will send its process name (an atom) in the cast
tuple. This
is so that you can differentiate between different subscribers of the same type
if you’re listening to multiple subscriber processes. This is a small use case,
though. For common use, you should underscore the process name as shown above.