260 lines
8.4 KiB
Elixir
260 lines
8.4 KiB
Elixir
defmodule Farmbot.BotState do
|
|
@moduledoc "Central State accumulator."
|
|
alias Farmbot.BotState
|
|
alias BotState.{
|
|
McuParams,
|
|
LocationData,
|
|
Configuration,
|
|
InformationalSettings,
|
|
Pin
|
|
}
|
|
|
|
defstruct [
|
|
mcu_params: struct(McuParams),
|
|
location_data: struct(LocationData),
|
|
configuration: struct(Configuration),
|
|
informational_settings: struct(InformationalSettings),
|
|
pins: %{},
|
|
process_info: %{farmwares: %{}},
|
|
gpio_registry: %{},
|
|
user_env: %{},
|
|
jobs: %{}
|
|
]
|
|
|
|
use GenStage
|
|
|
|
def download_progress_fun(name) do
|
|
alias Farmbot.BotState.JobProgress
|
|
require Farmbot.Logger
|
|
fn(bytes, total) ->
|
|
{do_send, prog} = cond do
|
|
# if the total is complete spit out the bytes,
|
|
# and put a status of complete.
|
|
total == :complete ->
|
|
Farmbot.Logger.success 3, "#{name} complete."
|
|
{true, %JobProgress.Bytes{bytes: bytes, status: :complete}}
|
|
|
|
# if we don't know the total just spit out the bytes.
|
|
total == nil ->
|
|
# debug_log "#{name} - #{bytes} bytes."
|
|
{rem(bytes, 10) == 0, %JobProgress.Bytes{bytes: bytes}}
|
|
# if the number of bytes == the total bytes,
|
|
# percentage side is complete.
|
|
(div(bytes, total)) == 1 ->
|
|
Farmbot.Logger.success 3, "#{name} complete."
|
|
{true, %JobProgress.Percent{percent: 100, status: :complete}}
|
|
# anything else is a percent.
|
|
true ->
|
|
percent = ((bytes / total) * 100) |> round()
|
|
# Logger.busy 3, "#{name} - #{bytes}/#{total} = #{percent}%"
|
|
{rem(percent, 10) == 0, %JobProgress.Percent{percent: percent}}
|
|
end
|
|
if do_send do
|
|
Farmbot.BotState.set_job_progress(name, prog)
|
|
else
|
|
:ok
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc "Set job progress."
|
|
def set_job_progress(name, progress) do
|
|
GenServer.call(__MODULE__, {:set_job_progress, name, progress})
|
|
end
|
|
|
|
def clear_progress_fun(name) do
|
|
GenServer.call(__MODULE__, {:clear_progress_fun, name})
|
|
end
|
|
|
|
@doc "Fetch the current state."
|
|
def fetch do
|
|
GenStage.call(__MODULE__, :fetch)
|
|
end
|
|
|
|
def report_disk_usage(percent) when is_number(percent) do
|
|
GenStage.call(__MODULE__, {:report_disk_usage, percent})
|
|
end
|
|
|
|
def report_memory_usage(megabytes) when is_number(megabytes) do
|
|
GenStage.call(__MODULE__, {:report_memory_usage, megabytes})
|
|
end
|
|
|
|
def report_soc_temp(temp_celcius) when is_number(temp_celcius) do
|
|
GenStage.call(__MODULE__, {:report_soc_temp, temp_celcius})
|
|
end
|
|
|
|
def report_uptime(seconds) when is_number(seconds) do
|
|
GenStage.call(__MODULE__, {:report_uptime, seconds})
|
|
end
|
|
|
|
def report_wifi_level(level) when is_number(level) do
|
|
GenStage.call(__MODULE__, {:report_wifi_level, level})
|
|
end
|
|
|
|
@doc "Put FBOS into maintenance mode."
|
|
def enter_maintenance_mode do
|
|
GenStage.call(__MODULE__, :enter_maintenance_mode)
|
|
end
|
|
|
|
@doc false
|
|
def start_link(args) do
|
|
GenStage.start_link(__MODULE__, args, [name: __MODULE__])
|
|
end
|
|
|
|
@doc false
|
|
def init([]) do
|
|
Farmbot.Registry.subscribe()
|
|
send(self(), :get_initial_configuration)
|
|
send(self(), :get_initial_mcu_params)
|
|
{:consumer, struct(BotState), [subscribe_to: [Farmbot.Firmware]]}
|
|
end
|
|
|
|
@doc false
|
|
def handle_call(:fetch, _from, state) do
|
|
new_state = handle_event({:informational_settings, %{cache_bust: :rand.uniform(1000)}}, state)
|
|
Farmbot.Registry.dispatch(__MODULE__, new_state)
|
|
{:reply, state, [], new_state}
|
|
end
|
|
|
|
# TODO(Connor) - Fix this to use event system.
|
|
def handle_call({:set_job_progress, name, progress}, _from, state) do
|
|
jobs = Map.put(state.jobs, name, progress)
|
|
new_state = %{state | jobs: jobs}
|
|
Farmbot.Registry.dispatch(__MODULE__, new_state)
|
|
{:reply, :ok, [], new_state}
|
|
end
|
|
|
|
# TODO(Connor) - Fix this to use event system.
|
|
def handle_call({:clear_progress_fun, name}, _from, state) do
|
|
jobs = Map.delete(state.jobs, name)
|
|
new_state = %{state | jobs: jobs}
|
|
Farmbot.Registry.dispatch(__MODULE__, new_state)
|
|
{:reply, :ok, [], new_state}
|
|
end
|
|
|
|
def handle_call({:report_disk_usage, percent}, _form, state) do
|
|
event = {:informational_settings, %{disk_usage: percent}}
|
|
new_state = handle_event(event, state)
|
|
Farmbot.Registry.dispatch(__MODULE__, new_state)
|
|
{:reply, :ok, [], new_state}
|
|
end
|
|
|
|
def handle_call({:memory_usage, megabytes}, _form, state) do
|
|
event = {:informational_settings, %{memory_usage: megabytes}}
|
|
new_state = handle_event(event, state)
|
|
Farmbot.Registry.dispatch(__MODULE__, new_state)
|
|
{:reply, :ok, [], new_state}
|
|
end
|
|
|
|
def handle_call({:report_soc_temp, temp}, _form, state) do
|
|
event = {:informational_settings, %{soc_temp: temp}}
|
|
new_state = handle_event(event, state)
|
|
Farmbot.Registry.dispatch(__MODULE__, new_state)
|
|
{:reply, :ok, [], new_state}
|
|
end
|
|
|
|
def handle_call({:uptime, seconds}, _form, state) do
|
|
event = {:informational_settings, %{uptime: seconds}}
|
|
new_state = handle_event(event, state)
|
|
Farmbot.Registry.dispatch(__MODULE__, new_state)
|
|
{:reply, :ok, [], new_state}
|
|
end
|
|
|
|
def handle_call({:report_wifi_level, level}, _form, state) do
|
|
event = {:informational_settings, %{wifi_level: level}}
|
|
new_state = handle_event(event, state)
|
|
Farmbot.Registry.dispatch(__MODULE__, new_state)
|
|
{:reply, :ok, [], new_state}
|
|
end
|
|
|
|
def handle_call(:enter_maintenance_mode, _form, state) do
|
|
event = {:informational_settings, %{sync_status: :maintenance}}
|
|
new_state = handle_event(event, state)
|
|
Farmbot.Registry.dispatch(__MODULE__, new_state)
|
|
{:reply, :ok, [], new_state}
|
|
end
|
|
|
|
@doc false
|
|
def handle_info({Farmbot.Registry, {Farmbot.Config, {"settings", key, val}}}, state) do
|
|
event = {:settings, %{String.to_atom(key) => val}}
|
|
new_state = handle_event(event, state)
|
|
Farmbot.Registry.dispatch(__MODULE__, new_state)
|
|
{:noreply, [], new_state}
|
|
end
|
|
|
|
def handle_info({Farmbot.Registry, {_, {:sync_status, status}}}, state) do
|
|
event = {:informational_settings, %{sync_status: status}}
|
|
new_state = handle_event(event, state)
|
|
Farmbot.Registry.dispatch(__MODULE__, new_state)
|
|
{:noreply, [], new_state}
|
|
end
|
|
|
|
def handle_info({Farmbot.Registry, _}, state), do: {:noreply, [], state}
|
|
|
|
def handle_info(:get_initial_configuration, state) do
|
|
full_config = Farmbot.Config.get_config_as_map()
|
|
settings = full_config["settings"]
|
|
new_state = Enum.reduce(settings, state, fn({key, val}, state) ->
|
|
event = {:settings, %{String.to_atom(key) => val}}
|
|
handle_event(event, state)
|
|
end)
|
|
Farmbot.Registry.dispatch(__MODULE__, new_state)
|
|
{:noreply, [], new_state}
|
|
end
|
|
|
|
def handle_info(:get_initial_mcu_params, state) do
|
|
full_config = Farmbot.Config.get_config_as_map()
|
|
settings = full_config["hardware_params"]
|
|
new_state = Enum.reduce(settings, state, fn({key, val}, state) ->
|
|
event = {:mcu_params, %{String.to_atom(key) => val}}
|
|
handle_event(event, state)
|
|
end)
|
|
Farmbot.Registry.dispatch(__MODULE__, new_state)
|
|
{:noreply, [], new_state}
|
|
end
|
|
|
|
@doc false
|
|
def handle_events(events, _from, state) do
|
|
state = Enum.reduce(events, state, &handle_event(&1, &2))
|
|
Farmbot.Registry.dispatch(__MODULE__, state)
|
|
{:noreply, [], state}
|
|
end
|
|
|
|
@doc false
|
|
def handle_event({:informational_settings, data}, state) do
|
|
new_data = Map.merge(state.informational_settings, data) |> Map.from_struct()
|
|
new_informational_settings = struct(InformationalSettings, new_data)
|
|
%{state | informational_settings: new_informational_settings}
|
|
end
|
|
|
|
def handle_event({:mcu_params, data}, state) do
|
|
new_data = Map.merge(state.mcu_params, data) |> Map.from_struct()
|
|
new_mcu_params = struct(McuParams, new_data)
|
|
%{state | mcu_params: new_mcu_params}
|
|
end
|
|
|
|
def handle_event({:location_data, data}, state) do
|
|
new_data = Map.merge(state.location_data, data) |> Map.from_struct()
|
|
new_location_data = struct(LocationData, new_data)
|
|
%{state | location_data: new_location_data}
|
|
end
|
|
|
|
def handle_event({:pins, data}, state) do
|
|
new_data = Enum.reduce(data, state.pins, fn({number, pin_state}, pins) ->
|
|
Map.put(pins, number, struct(Pin, pin_state))
|
|
end)
|
|
%{state | pins: new_data}
|
|
end
|
|
|
|
def handle_event({:settings, data}, state) do
|
|
new_data = Map.merge(state.configuration, data) |> Map.from_struct()
|
|
new_configuration = struct(Configuration, new_data)
|
|
%{state | configuration: new_configuration}
|
|
end
|
|
|
|
def handle_event(event, state) do
|
|
IO.inspect event, label: "unhandled event"
|
|
state
|
|
end
|
|
end
|