121 lines
3.1 KiB
Elixir
121 lines
3.1 KiB
Elixir
defmodule FarmbotCore.AssetSupervisor do
|
|
@moduledoc """
|
|
Supervises all database-backed records.
|
|
"""
|
|
|
|
use Supervisor
|
|
alias FarmbotCore.{Asset.Repo, AssetWorker}
|
|
|
|
def get_state(%{} = asset) do
|
|
case whereis_child(asset) do
|
|
{_id, pid, _, _} -> :sys.get_state(pid)
|
|
_ -> :error
|
|
end
|
|
end
|
|
|
|
def get_pid(%{} = asset) do
|
|
case whereis_child(asset) do
|
|
{_id, pid, _, _} -> pid
|
|
_ -> :error
|
|
end
|
|
end
|
|
|
|
@doc "List all children for an asset"
|
|
def list_children(kind) do
|
|
name = Module.concat(__MODULE__, kind)
|
|
Supervisor.which_children(name)
|
|
end
|
|
|
|
@doc "looks up a pid for an asset"
|
|
def whereis_child(%kind{local_id: id}) do
|
|
:ok = Protocol.assert_impl!(AssetWorker, kind)
|
|
name = Module.concat(__MODULE__, kind)
|
|
|
|
Supervisor.which_children(name)
|
|
|> Enum.find(fn {sup_id, _pid, _type, _} ->
|
|
sup_id == id
|
|
end)
|
|
end
|
|
|
|
# TODO(Connor) does this really belong here?
|
|
@doc "Does a GenServer call on a server"
|
|
def cast_child(%{} = data, cast) do
|
|
case whereis_child(data) do
|
|
nil -> :error
|
|
{_, pid, _, _} -> GenServer.cast(pid, cast)
|
|
end
|
|
end
|
|
|
|
@doc "Start a process that manages an asset"
|
|
def start_child(%kind{local_id: id} = asset) when is_binary(id) do
|
|
:ok = Protocol.assert_impl!(AssetWorker, kind)
|
|
name = Module.concat(__MODULE__, kind)
|
|
spec = worker_spec(asset)
|
|
Supervisor.start_child(name, spec)
|
|
end
|
|
|
|
@doc "Removes a child if it exists"
|
|
def terminate_child(kind, id) when is_binary(id) do
|
|
:ok = Protocol.assert_impl!(AssetWorker, kind)
|
|
name = Module.concat(__MODULE__, kind)
|
|
Supervisor.terminate_child(name, id)
|
|
end
|
|
|
|
@doc "Updates a child if it exists"
|
|
def update_child(%kind{local_id: id} = asset) do
|
|
:ok = Protocol.assert_impl!(AssetWorker, kind)
|
|
# if the worker tracks changes, we don't want to
|
|
# restart it.
|
|
if AssetWorker.tracks_changes?(asset) do
|
|
cast_child(asset, {:new_data, asset})
|
|
{_, pid, _, _} = whereis_child(asset)
|
|
{:ok, pid}
|
|
else
|
|
name = Module.concat(__MODULE__, kind)
|
|
_ = terminate_child(kind, id)
|
|
_ = Supervisor.delete_child(name, id)
|
|
start_child(asset)
|
|
end
|
|
end
|
|
|
|
# Non public supervisor stuff
|
|
|
|
@doc false
|
|
def child_spec(args) do
|
|
module = Keyword.fetch!(args, :module)
|
|
id_and_name = Module.concat(__MODULE__, module)
|
|
|
|
%{
|
|
id: id_and_name,
|
|
start: {__MODULE__, :start_link, [args]}
|
|
}
|
|
end
|
|
|
|
@doc false
|
|
def worker_spec(%{local_id: id, monitor: true} = asset) do
|
|
%{
|
|
id: id,
|
|
start: {AssetWorker, :start_link, [asset]},
|
|
restart: :transient
|
|
}
|
|
end
|
|
|
|
@doc false
|
|
def start_link(args) when is_list(args) do
|
|
module = Keyword.fetch!(args, :module)
|
|
Supervisor.start_link(__MODULE__, args, name: Module.concat(__MODULE__, module))
|
|
end
|
|
|
|
@doc false
|
|
def init(args) do
|
|
module = Keyword.fetch!(args, :module)
|
|
|
|
module
|
|
|> Repo.all()
|
|
|> Enum.filter(fn %{monitor: mon} -> mon == false end)
|
|
|> Enum.map(&Repo.preload(&1, AssetWorker.preload(&1)))
|
|
|> Enum.map(&worker_spec/1)
|
|
|> Supervisor.init(strategy: :one_for_one)
|
|
end
|
|
end
|