farmbot_os/farmbot_core/lib/regimen/name_provider.ex

90 lines
2.5 KiB
Elixir

defmodule Farmbot.Regimen.NameProvider do
@moduledoc """
Provides global names for running regimens as started by the
RegimenSupervisor.
# Example
```
%Regimen{} = reg = Farmbot.Asset.get_regimen_by_id(123, 100)
via = Farmbot.Regimen.NameProvider.via(reg)
pid = GenServer.whereis(via)
```
"""
alias Farmbot.Asset.Regimen
import Farmbot.Asset, only: [persistent_regimen: 1, delete_persistent_regimen: 1]
use GenServer
require Farmbot.Logger
@checkup 45_000
def start_link(args) do
GenServer.start_link(__MODULE__, args, name: __MODULE__)
end
def via(%Regimen{} = regimen) do
regimen.farm_event_id || raise "Regimen lookups require a farm_event_id"
{:via, __MODULE__, regimen}
end
def whereis_name(%Regimen{} = regimen) do
GenServer.call(__MODULE__, {:whereis_name, regimen})
end
def register_name(%Regimen{} = regimen, pid) do
GenServer.call(__MODULE__, {:register_name, regimen, pid})
end
def unregister_name(%Regimen{} = regimen) do
GenServer.call(__MODULE__, {:unregister_name, regimen})
end
def init([]) do
start_timer()
{:ok, %{}}
end
def handle_call({:whereis_name, regimen}, _, state) do
# Farmbot.Logger.info 3, "whereis_name: #{regimen.name} #{regimen.farm_event_id}"
case persistent_regimen(regimen) do
nil ->
{:reply, :undefined, state}
%{id: id} ->
{:reply, Map.get(state, id) || :undefined, state}
end
end
def handle_call({:register_name, regimen, pid}, _, state) do
# Farmbot.Logger.info 3, "register_name: #{regimen.name} #{regimen.farm_event_id}"
case persistent_regimen(regimen) do
nil ->
Farmbot.Logger.error 1, "No persistent regimen for #{regimen.name} #{regimen.farm_event_id}"
{:reply, :no, state}
%{id: id} ->
{:reply, :yes, Map.put(state, id, pid)}
end
end
def handle_call({:unregister_name, regimen}, _, state) do
# Farmbot.Logger.info 3, "unregister_name: #{regimen.name}"
case delete_persistent_regimen(regimen) do
{:ok, id} -> {:reply, :yes, Map.delete(state, id)}
{:error, reason} ->
Farmbot.Logger.error 1, "Failed to unregister #{regimen.name}: #{inspect reason}"
{:reply, :no, state}
end
end
def handle_info(:checkup, state) do
new_state = Enum.filter(state, fn({_pr_id, pid}) ->
Process.alive?(pid)
end) |> Map.new()
start_timer()
{:noreply, new_state}
end
defp start_timer do
Process.send_after(self(), :checkup, @checkup)
end
end