Current section
Files
Jump to
Current section
Files
lib/xplane_cmd.ex
defmodule XPlane.Cmd do
@moduledoc """
Get and set X-Plane data.
"""
@listen_port 59001
use GenServer
# API
@doc """
Start GenServer controlling port used to send commands to a specific X-Plane instance.
## Parameters
- instance: X-Plane instance from list returned by `XPlane.Instance.list/0`
"""
@spec start(XPlane.Instance.t, list) :: {:ok, pid} | {:error, any} | :ignore
def start(instance, opts \\ []) do
GenServer.start(__MODULE__,
{:ok, instance},
[name: name(instance)] ++ opts)
end
@doc """
Send a command to X-pLane
## Parameters
- instance: X-Plane instance from list returned by `XPlane.Instance.list/0`
- commands: List of command atoms - use `XPlane.CmdRef.describe()` to look
these up.
## Example
```
iex> XPlane.Cmd.send(master, [:engines_throttle_up])
:ok
```
"""
@spec send(XPlane.Instance.t, list(atom)) :: :ok | {:error, list}
def send(instance, command_ids) do
case GenServer.call(name(instance), {:send, command_ids}) do
e = {:error, _} -> e
r -> r
end
end
@doc """
Stop the GenServer controlling the port used to send commands.
"""
@spec stop(XPlane.Instance.t) :: :ok | {:error, any}
def stop(instance) do
GenServer.cast(name(instance), :stop)
end
# GensServer Callbacks
@impl true
def init({:ok, instance}) do
{:ok, sock} = :gen_udp.open(@listen_port, [:binary, active: false])
{:ok, {XPlane.CmdRef.load_version(instance.version_number), instance, sock}}
end
@impl true
def handle_call({:send, command_ids}, _from, state={cmd_refs, instance, sock}) do
vetted_command_ids = for cmd_id <- command_ids do
if cmd_refs |> Map.has_key?(cmd_id) do
{:ok, cmd_refs[cmd_id].name}
else
{:error, cmd_id}
end
end
errors = vetted_command_ids |> Enum.filter(&(match?({:error, _}, &1)))
if Enum.empty?(errors) do
for {:ok, name} <- vetted_command_ids do
:ok = :gen_udp.send(
sock,
instance.ip,
instance.port,
<<"CMND\0",
name::binary>>
)
end
{:reply, :ok, state}
else
{:reply, {:error,
for {:error, invalid_cmd_id} <- errors do
"Invalid command id: #{Atom.to_string(invalid_cmd_id)}"
end
}, state}
end
end
@impl true
def handle_cast(:stop, {_, _, sock}) do
:gen_udp.close(sock)
{:stop, :normal, nil}
end
# Helpers
defp name(instance) do
String.to_atom("#{__MODULE__}_#{instance.addr}")
end
end