diff --git a/farmbot_core/lib/farmbot_core/firmware_side_effects.ex b/farmbot_core/lib/farmbot_core/firmware_side_effects.ex index 22358b13..b5e2bb3a 100644 --- a/farmbot_core/lib/farmbot_core/firmware_side_effects.ex +++ b/farmbot_core/lib/farmbot_core/firmware_side_effects.ex @@ -31,6 +31,36 @@ defmodule FarmbotCore.FirmwareSideEffects do :noop end + @impl FarmbotFirmware.SideEffects + def handle_stall_detected() do + FarmbotCore.Logger.error(1, "Movement failed: stall detected") + :noop + end + + @impl FarmbotFirmware.SideEffects + def handle_calibration_error() do + FarmbotCore.Logger.error(1, "Calibration failed") + :noop + end + + @impl FarmbotFirmware.SideEffects + def handle_invalid_command() do + FarmbotCore.Logger.error(1, "Invalid command") + :noop + end + + @impl FarmbotFirmware.SideEffects + def handle_no_configuration() do + FarmbotCore.Logger.error(1, "No configuration") + :noop + end + + @impl FarmbotFirmware.SideEffects + def handle_unknown_error(err) do + FarmbotCore.Logger.error(1, "Unknown error: #{inspect(err)}") + :noop + end + @impl FarmbotFirmware.SideEffects def handle_home_complete(_) do :noop diff --git a/farmbot_firmware/lib/farmbot_firmware.ex b/farmbot_firmware/lib/farmbot_firmware.ex index 64b5bd71..0defbd9d 100644 --- a/farmbot_firmware/lib/farmbot_firmware.ex +++ b/farmbot_firmware/lib/farmbot_firmware.ex @@ -729,6 +729,51 @@ defmodule FarmbotFirmware do {:noreply, goto(state, :busy)} end + def handle_report({:report_error, [:no_error]}, state) do + Logger.error("Error: no error") + handle_report({:report_error, []}, state) + end + + def handle_report({:report_error, [:emergency_lock]}, state) do + Logger.error("Error: emergency lock") + handle_report({:report_error, []}, state) + end + + def handle_report({:report_error, [:timeout]}, state) do + Logger.error("Error: timeout") + handle_report({:report_error, []}, state) + end + + def handle_report({:report_error, [:stall_detected]}, state) do + Logger.error("Error: stall detected") + side_effects(state, :handle_stall_detected, []) + handle_report({:report_error, []}, state) + end + + def handle_report({:report_error, [:calibration_error]}, state) do + Logger.error("Error: calibration error") + side_effects(state, :handle_calibration_error, []) + handle_report({:report_error, []}, state) + end + + def handle_report({:report_error, [:invalid_command]}, state) do + Logger.error("Error: invalid command") + side_effects(state, :handle_invalid_command, []) + handle_report({:report_error, []}, state) + end + + def handle_report({:report_error, [:no_config]}, state) do + Logger.error("Error: no configuration") + side_effects(state, :handle_no_configuration, []) + handle_report({:report_error, []}, state) + end + + def handle_report({:report_error, [unk]}, state) do + Logger.error("Error: #{inspect(unk)}") + side_effects(state, :handle_unknown_error, [unk]) + handle_report({:report_error, []}, state) + end + def handle_report( {:report_error, _} = code, %{status: :configuration} = state diff --git a/farmbot_firmware/lib/farmbot_firmware/command.ex b/farmbot_firmware/lib/farmbot_firmware/command.ex index f9d1a859..7595c4d5 100644 --- a/farmbot_firmware/lib/farmbot_firmware/command.ex +++ b/farmbot_firmware/lib/farmbot_firmware/command.ex @@ -49,9 +49,14 @@ defmodule FarmbotFirmware.Command do debug_log("#{GCODE.encode(code)} position change") wait_for_command_result(tag, code, retries, error) - {_, {:report_error, _}} -> - debug_log("#{GCODE.encode(code)} firmware error") - if err, do: {:error, err}, else: {:error, :firmware_error} + {_, {:report_error, [error_code]}} -> + if err do + debug_log("#{GCODE.encode(code)} error: #{inspect(err)}") + {:error, err} + else + debug_log("#{GCODE.encode(code)} #{inspect(error_code)}") + {:error, "firmware error: #{inspect(error_code)}"} + end {_, {:report_invalid, []}} -> debug_log("#{GCODE.encode(code)} invalid command") diff --git a/farmbot_firmware/lib/farmbot_firmware/gcode/decoder.ex b/farmbot_firmware/lib/farmbot_firmware/gcode/decoder.ex index 5cf22902..5f4e5c8a 100644 --- a/farmbot_firmware/lib/farmbot_firmware/gcode/decoder.ex +++ b/farmbot_firmware/lib/farmbot_firmware/gcode/decoder.ex @@ -9,7 +9,7 @@ defmodule FarmbotFirmware.GCODE.Decoder do def do_decode("R01", []), do: {:report_begin, []} def do_decode("R02", []), do: {:report_success, []} def do_decode("R03", []), do: {:report_error, [:no_error]} - def do_decode("R03", error), do: {:report_error, decode_error(error)} + def do_decode("R03", v), do: {:report_error, decode_v(v)} def do_decode("R04", []), do: {:report_busy, []} def do_decode("R05", xyz), do: {:report_axis_state, decode_axis_state(xyz)} @@ -99,17 +99,20 @@ defmodule FarmbotFirmware.GCODE.Decoder do {:unknown, [kind | args]} end - def decode_error(["V0"]), do: [:no_error] - def decode_error(["V1"]), do: [:emergency_lock] - def decode_error(["V2"]), do: [:timeout] - def decode_error(["V3"]), do: [:stall_detected] - def decode_error(["V14"]), do: [:invalid_command] - def decode_error(["V15"]), do: [:no_config] - def decode_error([unk]), do: [unknown_error: unk] - # TODO(Rick): This is a guess. Can be removed if - # better solution is found. - # FarmBot/farmbot_os/issues/1105#issuecomment-572381069 - def decode_error(unk), do: [unknown_error: [unk]] + def decode_v(["V" <> value]) do + {value, ""} = Float.parse(value) + + case value do + 0.0 -> [:no_error] + 1.0 -> [:emergency_lock] + 2.0 -> [:timeout] + 3.0 -> [:stall_detected] + 4.0 -> [:calibration_error] + 14.0 -> [:invalid_command] + 15.0 -> [:no_config] + unk -> [unknown_error: unk] + end + end defp decode_floats(list, acc \\ []) diff --git a/farmbot_firmware/lib/farmbot_firmware/gcode/encoder.ex b/farmbot_firmware/lib/farmbot_firmware/gcode/encoder.ex index 9eb0813e..cbd565f2 100644 --- a/farmbot_firmware/lib/farmbot_firmware/gcode/encoder.ex +++ b/farmbot_firmware/lib/farmbot_firmware/gcode/encoder.ex @@ -8,10 +8,8 @@ defmodule FarmbotFirmware.GCODE.Encoder do def do_encode(:report_idle, []), do: "R00" def do_encode(:report_begin, []), do: "R01" def do_encode(:report_success, []), do: "R02" - # TODO(Rick): Why are some `:report_error`s sending - # tuples instead of lists?? - # https://github.com/FarmBot/farmbot_os/issues/1105#issuecomment-572381069 - def do_encode(:report_error, _), do: "R03" + def do_encode(:report_error, []), do: "R03" + def do_encode(:report_error, error), do: "R03 " <> encode_error(error) def do_encode(:report_busy, []), do: "R04" def do_encode(:report_axis_state, xyz), do: "R05 " <> encode_axis_state(xyz) @@ -158,6 +156,19 @@ defmodule FarmbotFirmware.GCODE.Encoder do "P#{param_id} V#{binary_float}" end + defp encode_error(error) do + case error do + :no_error -> "V0" + :emergency_lock -> "V1" + :timeout -> "V2" + :stall_detected -> "V3" + :calibration_error -> "V4" + :invalid_command -> "V14" + :no_config -> "V15" + _ -> "" + end + end + defp encode_ints(args) do Enum.map(args, fn {key, val} -> String.upcase(to_string(key)) <> to_string(val) diff --git a/farmbot_firmware/lib/farmbot_firmware/request.ex b/farmbot_firmware/lib/farmbot_firmware/request.ex index 2d21101c..71def3c3 100644 --- a/farmbot_firmware/lib/farmbot_firmware/request.ex +++ b/farmbot_firmware/lib/farmbot_firmware/request.ex @@ -74,8 +74,10 @@ defmodule FarmbotFirmware.Request do do: {:ok, {tag, result}}, else: wait_for_request_result(tag, code, result) - {_, {:report_error, _}} -> - {:error, :firmware_error} + {_, {:report_error, error_code}} -> + if error_code, + do: {:error, error_code}, + else: {:error, :firmware_error} {_, {:report_invalid, []}} -> {:error, :invalid_command} diff --git a/farmbot_firmware/test/gcode_test.exs b/farmbot_firmware/test/gcode_test.exs index 1d9e02e0..9bf0756e 100644 --- a/farmbot_firmware/test/gcode_test.exs +++ b/farmbot_firmware/test/gcode_test.exs @@ -40,11 +40,17 @@ defmodule FarmbotFirmware.GCODETest do test "error" do assert {nil, {:report_error, [:no_error]}} = GCODE.decode("R03") - assert {nil, {:report_error, [:no_error]}} = GCODE.decode("R03 V0") + assert {"1", {:report_error, [:no_error]}} = GCODE.decode("R03 V0 Q1") assert {nil, {:report_error, [:emergency_lock]}} = GCODE.decode("R03 V1") assert {nil, {:report_error, [:timeout]}} = GCODE.decode("R03 V2") assert {nil, {:report_error, [:stall_detected]}} = GCODE.decode("R03 V3") + assert {nil, {:report_error, [unknown_error: 987.0]}} = + GCODE.decode("R03 V987") + + assert {nil, {:report_error, [:calibration_error]}} = + GCODE.decode("R03 V4") + assert {nil, {:report_error, [:invalid_command]}} = GCODE.decode("R03 V14") @@ -52,6 +58,13 @@ defmodule FarmbotFirmware.GCODETest do assert {"100", {:report_error, [:no_error]}} = GCODE.decode("R03 Q100") assert "R03" = GCODE.encode({nil, {:report_error, []}}) + assert "R03 V0" = GCODE.encode({nil, {:report_error, :no_error}}) + assert "R03 V1" = GCODE.encode({nil, {:report_error, :emergency_lock}}) + assert "R03 V2" = GCODE.encode({nil, {:report_error, :timeout}}) + assert "R03 V3" = GCODE.encode({nil, {:report_error, :stall_detected}}) + assert "R03 V4" = GCODE.encode({nil, {:report_error, :calibration_error}}) + assert "R03 V14" = GCODE.encode({nil, {:report_error, :invalid_command}}) + assert "R03 V15" = GCODE.encode({nil, {:report_error, :no_config}}) assert "R03 Q100" = GCODE.encode({"100", {:report_error, []}}) end