diff --git a/farmbot_core/lib/farmbot_core/asset_workers/fbos_config_worker.ex b/farmbot_core/lib/farmbot_core/asset_workers/fbos_config_worker.ex index 414c28dd..0405cea1 100644 --- a/farmbot_core/lib/farmbot_core/asset_workers/fbos_config_worker.ex +++ b/farmbot_core/lib/farmbot_core/asset_workers/fbos_config_worker.ex @@ -8,7 +8,7 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.FbosConfig do require Logger require FarmbotCore.Logger alias FarmbotCeleryScript.AST - alias FarmbotCore.{Asset.FbosConfig, BotState, Config} + alias FarmbotCore.{Asset.FbosConfig, BotState, Config, DepTracker} import FarmbotFirmware.PackageUtils, only: [package_to_string: 1] @firmware_flash_attempt_threshold Application.get_env(:farmbot_core, __MODULE__)[:firmware_flash_attempt_threshold] @@ -35,6 +35,7 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.FbosConfig do @impl GenServer def init(%FbosConfig{} = fbos_config) do + :ok = DepTracker.register_asset(fbos_config, :init) if Config.get_config_value(:bool, "settings", "firmware_needs_flash") do Config.update_config_value(:bool, "settings", "firmware_needs_open", false) end @@ -51,6 +52,7 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.FbosConfig do @impl GenServer def handle_info({:step_complete, _, :ok}, state) do + DepTracker.register_asset(state.fbos_config, :idle) Config.update_config_value(:bool, "settings", "firmware_needs_flash", false) Config.update_config_value(:bool, "settings", "firmware_needs_open", true) {:noreply, %{state | firmware_flash_in_progress: false}} @@ -58,6 +60,7 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.FbosConfig do def handle_info({:step_complete, _, {:error, reason}}, %{firmware_flash_attempts: tries, firmware_flash_attempt_threshold: thresh} = state) when tries >= thresh do + DepTracker.register_asset(state.fbos_config, :idle) FarmbotCore.Logger.error 1, """ Failed flashing firmware: #{reason} Tried #{tries} times. Not retrying @@ -68,6 +71,7 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.FbosConfig do end def handle_info({:step_complete, _, {:error, reason}}, %{fbos_config: %FbosConfig{} = fbos_config} = state) do + DepTracker.register_asset(fbos_config, :flash_firmware) Config.update_config_value(:bool, "settings", "firmware_needs_flash", true) Config.update_config_value(:bool, "settings", "firmware_needs_open", false) @@ -138,6 +142,11 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.FbosConfig do {:noreply, %{state | fbos_config: new_fbos_config, firmware_io_timer: nil}} end + def handle_info(:bootup_sequence, state) do + DepTracker.register_asset(state.fbos_config, :idle) + {:noreply, state} + end + @impl GenServer def handle_cast({:new_data, new_fbos_config}, %{fbos_config: %FbosConfig{} = old_fbos_config} = state) do _ = set_config_to_state(new_fbos_config, old_fbos_config) @@ -157,10 +166,11 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.FbosConfig do :ok end - def maybe_flash_firmware(_state, new_hardware, old_hardware) do + def maybe_flash_firmware(state, new_hardware, old_hardware) do force? = Config.get_config_value(:bool, "settings", "firmware_needs_flash") cond do force? -> + DepTracker.register_asset(state.fbos_config, :firmware_flash) FarmbotCore.Logger.warn 1, "Firmware hardware forced flash" Config.update_config_value(:bool, "settings", "firmware_needs_flash", false) new_hardware @@ -168,6 +178,7 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.FbosConfig do |> FarmbotCeleryScript.execute(make_ref()) new_hardware != old_hardware -> + DepTracker.register_asset(state.fbos_config, :firmware_flash) FarmbotCore.Logger.warn 1, "Firmware hardware change from #{package_to_string(old_hardware)} to #{package_to_string(new_hardware)}" new_hardware |> fbos_config_to_flash_firmware_rpc() @@ -175,6 +186,7 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.FbosConfig do true -> # Config.update_config_value(:bool, "settings", "firmware_needs_open", true) + send self(), :bootup_sequence :ok end end @@ -270,7 +282,7 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.FbosConfig do def fbos_config_to_flash_firmware_rpc(firmware_hardware) do AST.Factory.new() - |> AST.Factory.rpc_request("FbosConfig") + |> AST.Factory.rpc_request("fbos_config.flash_firmware") |> AST.Factory.flash_firmware(firmware_hardware) end end diff --git a/farmbot_core/lib/farmbot_core/asset_workers/peripheral_worker.ex b/farmbot_core/lib/farmbot_core/asset_workers/peripheral_worker.ex index d5c67225..d5b03c15 100644 --- a/farmbot_core/lib/farmbot_core/asset_workers/peripheral_worker.ex +++ b/farmbot_core/lib/farmbot_core/asset_workers/peripheral_worker.ex @@ -1,12 +1,20 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.Peripheral do use GenServer require Logger + require FarmbotCore.Logger - alias FarmbotCore.{Asset.Peripheral, BotState} + 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: [] @@ -20,30 +28,69 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.Peripheral do @impl true def init(peripheral) do - %{informational_settings: %{idle: idle, firmware_version: fw_version}} = BotState.subscribe() - state = %{peripheral: peripheral, errors: 0, fw_idle: idle || false, fw_version: fw_version} - send self(), :timeout + %{ + 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(:timeout, %{fw_version: nil} = state) do - # Logger.debug("Not reading peripheral. Firmware not started.") - Process.send_after(self(), :timeout, @retry_ms) + 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(:timeout, %{fw_version: "8.0.0.S"} = state) do + 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(:timeout, %{fw_idle: false} = state) do + 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(), :timeout, @retry_ms) + Process.send_after(self(), :try_read_peripheral, @retry_ms) {:noreply, state} end - def handle_info(:timeout, %{peripheral: peripheral, errors: errors} = state) do + 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 @@ -52,8 +99,8 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.Peripheral do {:noreply, state} {:error, reason} when errors < 5 -> - Logger.error("Read peripheral: #{peripheral.label} error: #{reason} errors=#{state.errors}") - Process.send_after(self(), :timeout, @retry_ms) + 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 -> @@ -70,6 +117,15 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.Peripheral 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 @@ -80,7 +136,7 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.Peripheral do def peripheral_to_rpc(peripheral) do AST.Factory.new() - |> AST.Factory.rpc_request(peripheral.local_id) + |> 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 diff --git a/farmbot_core/lib/farmbot_core/firmware_open_task.ex b/farmbot_core/lib/farmbot_core/firmware_open_task.ex index b94eef1a..c9352f7a 100644 --- a/farmbot_core/lib/farmbot_core/firmware_open_task.ex +++ b/farmbot_core/lib/farmbot_core/firmware_open_task.ex @@ -8,7 +8,7 @@ defmodule FarmbotCore.FirmwareOpenTask do use GenServer require FarmbotCore.Logger alias FarmbotFirmware.{UARTTransport, StubTransport} - alias FarmbotCore.{Asset, Config} + 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: @@ -89,6 +89,7 @@ defmodule FarmbotCore.FirmwareOpenTask 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"