146 lines
4.8 KiB
Elixir
146 lines
4.8 KiB
Elixir
defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.Peripheral do
|
|
use GenServer
|
|
require Logger
|
|
require FarmbotCore.Logger
|
|
|
|
alias FarmbotCore.{Asset.FbosConfig, Asset.Peripheral, BotState, DepTracker}
|
|
alias FarmbotCeleryScript.AST
|
|
|
|
@retry_ms 1_000
|
|
|
|
@unacceptable_fbos_config_statuses [
|
|
nil,
|
|
:init,
|
|
:firmware_flash,
|
|
:bootup_sequence,
|
|
]
|
|
|
|
@impl true
|
|
def preload(%Peripheral{}), do: []
|
|
|
|
@impl true
|
|
def tracks_changes?(%Peripheral{}), do: false
|
|
|
|
@impl true
|
|
def start_link(peripheral, _args) do
|
|
GenServer.start_link(__MODULE__, peripheral)
|
|
end
|
|
|
|
@impl true
|
|
def init(peripheral) do
|
|
:ok = DepTracker.register_asset(peripheral, :init)
|
|
%{
|
|
informational_settings: %{
|
|
idle: idle,
|
|
firmware_version: fw_version,
|
|
firmware_configured: fw_configured
|
|
}
|
|
} = BotState.subscribe()
|
|
state = %{
|
|
peripheral: peripheral,
|
|
errors: 0,
|
|
fw_idle: idle || false,
|
|
fw_version: fw_version,
|
|
fw_configured: fw_configured || false,
|
|
fbos_config_status: nil
|
|
}
|
|
:ok = DepTracker.subscribe_asset(FbosConfig)
|
|
send self(), :try_read_peripheral
|
|
{:ok, state}
|
|
end
|
|
|
|
@impl true
|
|
def handle_info({DepTracker, {FbosConfig, _}, _old, status}, state) do
|
|
{:noreply, %{state | fbos_config_status: status}}
|
|
end
|
|
|
|
def handle_info(:try_read_peripheral, %{fbos_config_status: fbos_config_status} = state)
|
|
when fbos_config_status in @unacceptable_fbos_config_statuses do
|
|
# Logger.debug("Not reading peripheral. fbos_config not in acceptable state: #{fbos_config_status}")
|
|
Process.send_after(self(), :try_read_peripheral, @retry_ms)
|
|
{:noreply, state}
|
|
end
|
|
|
|
def handle_info(:try_read_peripheral, %{fbos_config_status: :bootup_sequence} = state) do
|
|
# Logger.debug("Not reading peripheral. Bootup sequence not complete")
|
|
Process.send_after(self(), :try_read_peripheral, @retry_ms)
|
|
{:noreply, state}
|
|
end
|
|
|
|
def handle_info(:try_read_peripheral, %{fw_version: nil} = state) do
|
|
# Logger.debug("Not reading peripheral. Firmware not booted.")
|
|
Process.send_after(self(), :try_read_peripheral, @retry_ms)
|
|
{:noreply, state}
|
|
end
|
|
|
|
def handle_info(:try_read_peripheral, %{fw_version: "none"} = state) do
|
|
# Logger.debug("Not reading peripheral. Firmware not booted.")
|
|
Process.send_after(self(), :try_read_peripheral, @retry_ms)
|
|
{:noreply, state}
|
|
end
|
|
|
|
def handle_info(:try_read_peripheral, %{fw_configured: false} = state) do
|
|
# Logger.debug("Not reading peripheral. Firmware not configured.")
|
|
Process.send_after(self(), :try_read_peripheral, @retry_ms)
|
|
{:noreply, state}
|
|
end
|
|
|
|
def handle_info(:try_read_peripheral, %{fw_idle: false} = state) do
|
|
# Logger.debug("Not reading peripheral. Firmware not idle.")
|
|
Process.send_after(self(), :try_read_peripheral, @retry_ms)
|
|
{:noreply, state}
|
|
end
|
|
|
|
def handle_info(:try_read_peripheral, %{peripheral: peripheral, errors: errors} = state) do
|
|
Logger.debug("Read peripheral: #{peripheral.label}")
|
|
rpc = peripheral_to_rpc(peripheral)
|
|
case FarmbotCeleryScript.execute(rpc, make_ref()) do
|
|
:ok ->
|
|
:ok = DepTracker.register_asset(peripheral, :complete)
|
|
Logger.debug("Read peripheral: #{peripheral.label} ok")
|
|
{:noreply, state}
|
|
|
|
{:error, reason} when errors < 5 ->
|
|
Logger.error("Read peripheral: #{peripheral.label} error: #{reason} errors=#{state.errors} status=#{state.fbos_config_status}")
|
|
Process.send_after(self(), :try_read_peripheral, @retry_ms)
|
|
{:noreply, %{state | errors: state.errors + 1}}
|
|
|
|
{:error, reason} when errors == 5 ->
|
|
Logger.error("Read peripheral: #{peripheral.label} error: #{reason} errors=5 not trying again.")
|
|
{:noreply, state}
|
|
end
|
|
end
|
|
|
|
def handle_info({BotState, %{changes: %{informational_settings: %{changes: %{idle: idle}}}}}, state) do
|
|
{:noreply, %{state | fw_idle: idle}}
|
|
end
|
|
|
|
def handle_info({BotState, %{changes: %{informational_settings: %{changes: %{firmware_version: fw_version}}}}}, state) do
|
|
{:noreply, %{state | fw_version: fw_version}}
|
|
end
|
|
|
|
def handle_info({BotState, %{changes: %{informational_settings: %{changes: %{firmware_configured: fw_configured}}}}}, state) do
|
|
# this should really be fixed upstream not to dispatch if version is none.
|
|
if state.fw_version == "none" do
|
|
{:noreply, state}
|
|
else
|
|
{:noreply, %{state | fw_configured: fw_configured}}
|
|
end
|
|
end
|
|
|
|
def handle_info({BotState, _}, state) do
|
|
{:noreply, state}
|
|
end
|
|
|
|
def handle_info({:step_complete, _, _}, state) do
|
|
{:noreply, state}
|
|
end
|
|
|
|
def peripheral_to_rpc(peripheral) do
|
|
AST.Factory.new()
|
|
|> AST.Factory.rpc_request("peripheral." <> peripheral.local_id)
|
|
|> AST.Factory.set_pin_io_mode(peripheral.pin, "output")
|
|
|> AST.Factory.read_pin(peripheral.pin, peripheral.mode)
|
|
end
|
|
end
|