defmodule Rclex do @moduledoc """ User API for `#{__MODULE__}`. """ @namespace_doc "`:namespace` must lead with \"/\". if not specified, the default is \"/\"" @qos_doc "`:qos` if not specified, applied the default which equals return of `Rclex.QoS.profile_default/0`" @topic_name_doc "`topic_name` must lead with \"/\"" @typedoc "#{@topic_name_doc}." @type topic_name :: String.t() @doc """ Start node. ### opts - #{@namespace_doc} ## Examples iex> Rclex.start_node("node", namespace: "/example") :ok iex> Rclex.start_node("node", namespace: "/example") {:error, :already_started} """ @spec start_node(name :: String.t(), opts :: [namespace: String.t()]) :: :ok | {:error, :already_started} | {:error, term()} def start_node(name, opts \\ []) when is_binary(name) and is_list(opts) do context = Rclex.Context.get() namespace = Keyword.get(opts, :namespace, "/") case Rclex.NodesSupervisor.start_child(context, name, namespace) do {:ok, _pid} -> :ok {:error, {:already_started, _pid}} -> {:error, :already_started} {:error, reason} -> {:error, reason} end end @doc """ Stop node. And also stop the entities on the node, `publisher`, `subscription` and `timer`. ### opts - #{@namespace_doc} ## Examples iex> Rclex.stop_node("node", namespace: "/example") :ok iex> Rclex.stop_node("node", namespace: "/example") {:error, :not_found} """ @spec stop_node(name :: String.t(), opts :: [namespace: String.t()]) :: :ok | {:error, :not_found} def stop_node(name, opts \\ []) when is_binary(name) and is_list(opts) do namespace = Keyword.get(opts, :namespace, "/") Rclex.NodesSupervisor.terminate_child(name, namespace) end @doc """ Start publisher. - #{@topic_name_doc} ## Examples iex> alias Rclex.Pkgs.StdMsgs iex> Rclex.start_publisher(StdMsgs.Msg.String, "/chatter", "node", namespace: "/example") :ok iex> Rclex.start_publisher(StdMsgs.Msg.String, "/chatter", "node", namespace: "/example") {:error, :already_started} """ @spec start_publisher( message_type :: module(), topic_name :: topic_name(), node_name :: String.t(), opts :: [namespace: String.t(), qos: Rclex.QoS.t()] ) :: :ok | {:error, :already_started} | {:error, term()} def start_publisher(message_type, topic_name, node_name, opts \\ []) when is_atom(message_type) and is_binary(topic_name) and is_binary(node_name) and is_list(opts) do namespace = Keyword.get(opts, :namespace, "/") qos = Keyword.get(opts, :qos, Rclex.QoS.profile_default()) case Rclex.Node.start_publisher(message_type, topic_name, node_name, namespace, qos) do {:ok, _pid} -> :ok {:error, {:already_started, _pid}} -> {:error, :already_started} {:error, reason} -> {:error, reason} end end @doc """ Stop publisher. - #{@topic_name_doc} ### opts - #{@namespace_doc} ## Examples iex> alias Rclex.Pkgs.StdMsgs iex> Rclex.stop_publisher(StdMsgs.Msg.String, "/chatter", "node", namespace: "/example") :ok iex> Rclex.stop_publisher(StdMsgs.Msg.String, "/chatter", "node", namespace: "/example") {:error, :not_found} """ @spec stop_publisher( message_type :: module(), topic_name :: topic_name(), node_name :: String.t(), opts :: [namespace: String.t()] ) :: :ok | {:error, :not_found} def stop_publisher(message_type, topic_name, name, opts \\ []) when is_atom(message_type) and is_binary(topic_name) and is_binary(name) and is_list(opts) do namespace = Keyword.get(opts, :namespace, "/") Rclex.Node.stop_publisher(message_type, topic_name, name, namespace) end @doc """ Publish message. - #{@topic_name_doc} ### opts - #{@namespace_doc} ## Examples iex> alias Rclex.Pkgs.StdMsgs iex> Rclex.publish(struct(StdMsgs.Msg.String, %{data: "hello"}), "/chatter", "node", namespace: "/example") :ok iex> Rclex.publish(struct(StdMsgs.Msg.String, %{data: "hello"}), "/chatter", "node") {:error, :not_found} """ @spec publish( message :: struct(), topic_name :: topic_name(), node_name :: String.t(), opts :: [namespace: String.t()] ) :: :ok | {:error, :not_found} def publish(message, topic_name, node_name, opts \\ []) when is_struct(message) and is_binary(topic_name) and is_binary(node_name) and is_list(opts) do namespace = Keyword.get(opts, :namespace, "/") Rclex.Publisher.publish(message, topic_name, node_name, namespace) end @doc """ Start subscription. - #{@topic_name_doc} ### opts - #{@namespace_doc} - #{@qos_doc} ## Examples iex> alias Rclex.Pkgs.StdMsgs iex> Rclex.start_subscription(&IO.inspect/1, StdMsgs.Msg.String, "/chatter", "node", namespace: "/example") :ok iex> Rclex.start_subscription(&IO.inspect/1, StdMsgs.Msg.String, "/chatter", "node", namespace: "/example") {:error, :already_started} """ @spec start_subscription( callback :: function(), message_type :: module(), topic_name :: topic_name(), node_name :: String.t(), opts :: [namespace: String.t(), qos: Rclex.QoS.t()] ) :: :ok | {:error, :already_started} | {:error, term()} def start_subscription(callback, message_type, topic_name, node_name, opts \\ []) when is_function(callback) and is_atom(message_type) and is_binary(topic_name) and is_binary(node_name) and is_list(opts) do namespace = Keyword.get(opts, :namespace, "/") qos = Keyword.get(opts, :qos, Rclex.QoS.profile_default()) case Rclex.Node.start_subscription( callback, message_type, topic_name, node_name, namespace, qos ) do {:ok, _pid} -> :ok {:error, {:already_started, _pid}} -> {:error, :already_started} {:error, reason} -> {:error, reason} end end @doc """ Stop subscription. - #{@topic_name_doc} ### opts - #{@namespace_doc} ## Examples iex> alias Rclex.Pkgs.StdMsgs iex> Rclex.stop_subscription(StdMsgs.Msg.String, "/chatter", "node", namespace: "/example") :ok iex> Rclex.stop_subscription(StdMsgs.Msg.String, "/chatter", "node", namespace: "/example") {:error, :not_found} """ @spec stop_subscription( message_type :: module(), topic_name :: topic_name(), node_name :: String.t(), opts :: [namespace: String.t()] ) :: :ok | {:error, :not_found} def stop_subscription(message_type, topic_name, node_name, opts \\ []) when is_atom(message_type) and is_binary(topic_name) and is_binary(node_name) and is_list(opts) do namespace = Keyword.get(opts, :namespace, "/") Rclex.Node.stop_subscription(message_type, topic_name, node_name, namespace) end @doc """ Start timer. ### opts - #{@namespace_doc} ## Examples iex> Rclex.start_timer(1000, fn -> IO.inspect("tick") end, "tick", "node", namespace: "/example") :ok iex> Rclex.start_timer(1000, fn -> IO.inspect("tick") end, "tick", "node", namespace: "/example") {:error, :already_started} """ @spec start_timer( period_ms :: non_neg_integer(), callback :: function(), timer_name :: String.t(), node_name :: String.t(), opts :: [namespace: String.t()] ) :: :ok | {:error, :already_started} | {:error, term()} def start_timer(period_ms, callback, timer_name, node_name, opts \\ []) when is_integer(period_ms) and is_function(callback) and is_binary(timer_name) and is_binary(node_name) and is_list(opts) do namespace = Keyword.get(opts, :namespace, "/") case Rclex.Node.start_timer(period_ms, callback, timer_name, node_name, namespace) do {:ok, _pid} -> :ok {:error, {:already_started, _pid}} -> {:error, :already_started} {:error, reason} -> {:error, reason} end end @doc """ Stop timer. ### opts - #{@namespace_doc} ## Examples iex> Rclex.stop_timer("tick", "node", namespace: "/example") :ok iex> Rclex.stop_timer("tick", "node", namespace: "/example") {:error, :not_found} """ @spec stop_timer( timer_name :: String.t(), node_name :: String.t(), opts :: [namespace: String.t()] ) :: :ok | {:error, :not_found} def stop_timer(timer_name, node_name, opts \\ []) when is_binary(timer_name) and is_binary(node_name) and is_list(opts) do namespace = Keyword.get(opts, :namespace, "/") Rclex.Node.stop_timer(timer_name, node_name, namespace) end end