farmbot_os/farmbot_core/lib/farmbot_core/firmware_open_task.ex

126 lines
4.7 KiB
Elixir

defmodule FarmbotCore.FirmwareOpenTask do
@moduledoc """
Will open the UART interface after it's been successfully flashed .
Must configure in application env: `attempt_threshold`. It can be an integer
or `:infinity` in which case it will try opening it indefinately.
"""
use GenServer
require FarmbotCore.Logger
alias FarmbotFirmware.{UARTTransport, StubTransport}
alias FarmbotCore.{Asset, Config, DepTracker}
@attempt_threshold Application.get_env(:farmbot_core, __MODULE__)[:attempt_threshold]
@attempt_threshold || Mix.raise """
Firmware open attempt threshold not configured:
config :farmbot_core, FarmbotCore.FirmwareOpenTask, [
attempt_threshold: 10
]
"""
@doc false
def start_link(args, opts \\ [name: __MODULE__]) do
GenServer.start_link(__MODULE__, args, opts)
end
@doc false
def swap_transport(tty) do
Application.put_env(:farmbot_firmware, FarmbotFirmware, transport: UARTTransport, device: tty)
# Swap transport on FW module.
# Close tranpsort if it is open currently.
_ = FarmbotFirmware.close_transport()
FarmbotFirmware.open_transport(UARTTransport, device: tty)
end
def unswap_transport() do
Application.put_env(:farmbot_firmware, FarmbotFirmware, transport: StubTransport)
# Swap transport on FW module.
# Close tranpsort if it is open currently.
_ = FarmbotFirmware.close_transport()
FarmbotFirmware.open_transport(StubTransport, [])
end
@impl GenServer
def init(_args) do
send(self(), :open)
firmware_path = Asset.fbos_config(:firmware_path)
firmware_hardware = Asset.fbos_config(:firmware_hardware)
if firmware_path && firmware_hardware do
Config.update_config_value(:bool, "settings", "firmware_needs_open", true)
end
{:ok, %{timer: nil, attempts: 0, threshold: @attempt_threshold}}
end
@impl GenServer
def handle_info(:open, %{attempts: at, threshold: attempt_threshold} = state) when at >= attempt_threshold do
if state.timer, do: Process.cancel_timer(state.timer)
FarmbotCore.Logger.debug 3, "Firmware didn't open after #{@attempt_threshold} tries. Not trying to open anymore"
{:noreply, %{state | timer: nil}}
end
def handle_info(:open, state) do
if state.timer, do: Process.cancel_timer(state.timer)
needs_flash? = Config.get_config_value(:bool, "settings", "firmware_needs_flash")
needs_open? = Config.get_config_value(:bool, "settings", "firmware_needs_open")
firmware_path = Asset.fbos_config(:firmware_path)
firmware_hardware = Asset.fbos_config(:firmware_hardware)
cond do
needs_flash? ->
FarmbotCore.Logger.debug 3, "Firmware needs flash still. Not opening"
timer = Process.send_after(self(), :open, 5000)
{:noreply, increment_attempts(%{state | timer: timer})}
is_nil(firmware_path) ->
FarmbotCore.Logger.debug 3, "Firmware path not detected. Not opening"
timer = Process.send_after(self(), :open, 5000)
{:noreply, increment_attempts(%{state | timer: timer})}
firmware_hardware == "none" && needs_open? ->
FarmbotCore.Logger.debug 3, "Firmware needs to be closed"
unswap_transport()
Config.update_config_value(:bool, "settings", "firmware_needs_open", false)
timer = Process.send_after(self(), :open, 5000)
{:noreply, %{state | timer: timer, attempts: 0}}
needs_open? ->
FarmbotCore.Logger.debug 3, "Firmware needs to be opened"
case swap_transport(firmware_path) do
:ok ->
Config.update_config_value(:bool, "settings", "firmware_needs_open", false)
timer = Process.send_after(self(), :open, 5000)
DepTracker.register_service(:firmware, :init)
{:noreply, %{state | timer: timer, attempts: 0}}
_ ->
FarmbotCore.Logger.debug 3, "Firmware failed to open"
timer = Process.send_after(self(), :open, 5000)
{:noreply, %{state | timer: timer, attempts: 0}}
end
needs_open? == false ->
# Firmware should probably already be opened here.
# Can just ignore
timer = Process.send_after(self(), :open, 5000)
{:noreply, %{state | timer: timer}}
true ->
FarmbotCore.Logger.debug 3, """
Unknown firmware open state:
firmware needs flash?: #{needs_flash?}
firwmare needs open?: #{needs_open?}
firmware path: #{firmware_path}
"""
timer = Process.send_after(self(), :open, 5000)
{:noreply, %{state | timer: timer, attempts: 0}}
end
end
defp increment_attempts(%{attempts: at, attempt_threshold: :infinity} = state) do
%{state | attempts: at + 1}
end
defp increment_attempts(%{attempts: at} = state) do
%{state | attempts: at + 1}
end
end