Current section
Files
Jump to
Current section
Files
lib/mavlink_util/param_set.ex
defmodule XMAVLink.Util.ParamSet do
@moduledoc """
Convenience helper for setting cached MAVLink parameters.
The helper uses the parameter type cached by `XMAVLink.Util.CacheManager`,
sends `PARAM_SET`, and waits for a matching `PARAM_VALUE` confirmation.
Retries are bounded by default; pass `:context`, `:retries`,
`:retry_interval_ms`, or `:router` in the options to override the defaults.
"""
@param_retry_interval 3000
@param_retries 5
require Logger
alias XMAVLink.Util.CacheManager
alias XMAVLink.Util.Context
import XMAVLink.Util.FocusManager, only: [focus: 1]
def param_set(param, new_value, opts \\ []) when is_list(opts) do
with {:ok, {system_id, component_id, mavlink_version}} <- focus(opts) do
param_set(system_id, component_id, mavlink_version, param, new_value, opts)
end
end
def param_set(system_id, component_id, mavlink_version, param, new_value, opts \\ []) do
param = normalize_param_id(param)
opts = command_opts(opts)
params = opts.context.tables.params
param_value_module = message_module(opts.dialect, :ParamValue)
with :ok <- require_table(params),
[
{{^system_id, ^component_id, ^param},
{_time, %{__struct__: ^param_value_module, param_type: param_type}}}
] <-
:ets.lookup(params, {system_id, component_id, param}),
:ok <-
XMAVLink.Router.subscribe(opts.router,
message: param_value_module,
source_system: system_id
) do
try do
do_param_set(system_id, component_id, mavlink_version, param, new_value, param_type, opts)
after
XMAVLink.Router.unsubscribe(opts.router)
end
else
[] -> {:error, :unknown_param}
{:error, reason} -> {:error, reason}
end
end
defp do_param_set(
_system_id,
_component_id,
_mavlink_version,
_param,
_new_value,
_param_type,
_opts = %{attempts: 0}
),
do: {:error, :timeout}
defp do_param_set(system_id, component_id, mavlink_version, param, new_value, param_type, opts) do
param_value_module = message_module(opts.dialect, :ParamValue)
with :ok <-
XMAVLink.Router.pack_and_send(
opts.router,
struct(message_module(opts.dialect, :ParamSet), %{
target_system: system_id,
target_component: component_id,
param_id: param,
param_value: new_value,
param_type: param_type
}),
mavlink_version
) do
receive do
%{__struct__: ^param_value_module, param_id: ^param, param_value: ^new_value} ->
Logger.info(
"Set #{String.downcase(param)} to #{inspect(new_value)} for vehicle #{system_id}.#{component_id}"
)
:ok
after
opts.retry_interval_ms ->
do_param_set(
system_id,
component_id,
mavlink_version,
param,
new_value,
param_type,
%{opts | attempts: next_attempts(opts.attempts)}
)
end
end
end
defp normalize_param_id(param) when is_atom(param),
do: param |> Atom.to_string() |> String.upcase()
defp normalize_param_id(param) when is_binary(param), do: String.upcase(param)
defp command_opts(opts) do
context = opts |> Keyword.put(:router, CacheManager.router(opts)) |> Context.new()
retries = Keyword.get(opts, :retries, @param_retries)
%{
context: context,
router: context.router,
dialect: context.dialect,
table_prefix: context.table_prefix,
retry_interval_ms: Keyword.get(opts, :retry_interval_ms, @param_retry_interval),
attempts: attempts(retries)
}
end
defp attempts(:infinity), do: :infinity
defp attempts(retries) when is_integer(retries) and retries >= 0, do: retries + 1
defp next_attempts(:infinity), do: :infinity
defp next_attempts(attempts), do: attempts - 1
defp require_table(table) do
case :ets.info(table) do
:undefined -> {:error, :not_started}
_ -> :ok
end
end
defp message_module(dialect, name), do: Module.concat([dialect, Message, name])
end