commit
e0068bd3c2
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"coverage_options": {
|
||||
"treat_no_relevant_lines_as_covered": true,
|
||||
"minimum_coverage": 21
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -2,7 +2,6 @@ defmodule FarmbotCore.Asset.Repo.Migrations.ForceResyncPoints do
|
|||
use Ecto.Migration
|
||||
|
||||
alias FarmbotCore.Asset.{Repo, Point}
|
||||
import Ecto.Query, only: [from: 2]
|
||||
|
||||
def change do
|
||||
for %{id: id} = point when is_integer(id) <- Repo.all(Point) do
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
defmodule FarmbotCore.Asset.Repo.Migrations.ForceResyncDevice do
|
||||
use Ecto.Migration
|
||||
alias FarmbotCore.Asset.{Repo, Device}
|
||||
|
||||
def change do
|
||||
execute("UPDATE devices SET updated_at = \"1970-11-07 16:52:31.618000\"")
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
defmodule FarmbotCore.Asset.Repo.Migrations.ForceResyncDeviceForMountedToolId do
|
||||
use Ecto.Migration
|
||||
alias FarmbotCore.Asset.{Repo, Device}
|
||||
|
||||
def change do
|
||||
execute("UPDATE devices SET updated_at = \"1970-11-07 16:52:31.618000\"")
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
defmodule FarmbotCore.Config.Repo.Migrations.AddFirmwareFlashSetting do
|
||||
use Ecto.Migration
|
||||
import FarmbotCore.Config, only: [update_config_value: 4]
|
||||
import FarmbotCore.Config.MigrationHelpers
|
||||
|
||||
def change do
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"coverage_options": {
|
||||
"treat_no_relevant_lines_as_covered": true,
|
||||
"minimum_coverage": 55
|
||||
"minimum_coverage": 63
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 \\ [])
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -47,4 +47,9 @@ defmodule FarmbotFirmware.SideEffects do
|
|||
@callback handle_input_gcode(GCODE.t()) :: any()
|
||||
@callback handle_output_gcode(GCODE.t()) :: any()
|
||||
@callback handle_debug_message([String.t()]) :: any()
|
||||
@callback handle_calibration_error() :: any()
|
||||
@callback handle_invalid_command() :: any()
|
||||
@callback handle_no_configuration() :: any()
|
||||
@callback handle_stall_detected() :: any()
|
||||
@callback handle_unknown_error(any()) :: any()
|
||||
end
|
||||
|
|
|
@ -164,4 +164,19 @@ defmodule FarmbotFirmware.StubSideEffects do
|
|||
|
||||
@impl SideEffects
|
||||
def handle_debug_message(_), do: :noop
|
||||
|
||||
@impl SideEffects
|
||||
def handle_unknown_error(_), do: :noop
|
||||
|
||||
@impl SideEffects
|
||||
def handle_calibration_error(), do: :noop
|
||||
|
||||
@impl SideEffects
|
||||
def handle_invalid_command(), do: :noop
|
||||
|
||||
@impl SideEffects
|
||||
def handle_no_configuration(), do: :noop
|
||||
|
||||
@impl SideEffects
|
||||
def handle_stall_detected(), do: :noop
|
||||
end
|
||||
|
|
|
@ -6,6 +6,10 @@ defmodule FarmbotFirmwareTest do
|
|||
GenServer.call(pid, cmd, :infinity)
|
||||
end
|
||||
|
||||
def try_cast(pid, cmd) do
|
||||
GenServer.cast(pid, cmd)
|
||||
end
|
||||
|
||||
def firmware_server do
|
||||
arg = [transport: FarmbotFirmware.StubTransport]
|
||||
{:ok, pid} = FarmbotFirmware.start_link(arg, [])
|
||||
|
@ -15,6 +19,47 @@ defmodule FarmbotFirmwareTest do
|
|||
pid
|
||||
end
|
||||
|
||||
test "various reports" do
|
||||
pid = firmware_server()
|
||||
|
||||
reports = [
|
||||
{:report_begin, []},
|
||||
{:report_busy, []},
|
||||
{:report_emergency_lock, []},
|
||||
{:report_error, [:calibration_error]},
|
||||
{:report_error, [:emergency_lock]},
|
||||
{:report_error, [:invalid_command]},
|
||||
{:report_error, [:no_config]},
|
||||
{:report_error, [:no_error]},
|
||||
{:report_error, [:other]},
|
||||
{:report_error, [:stall_detected]},
|
||||
{:report_error, [:timeout]},
|
||||
{:report_error, []},
|
||||
{:report_home_complete, [:x]},
|
||||
{:report_invalid, []},
|
||||
{:report_load, 23.0},
|
||||
{:report_retry, []},
|
||||
{:report_success, []},
|
||||
{:report_axis_timeout, [:x]},
|
||||
{:report_debug_message, ["Hello"]},
|
||||
{:report_axis_state, [x: :idle]},
|
||||
{:report_encoders_raw, [x: 1.4, y: 2.3, z: 3.2]},
|
||||
{:report_encoders_scaled, [{:x, 100.0}, {:y, 200.0}, {:z, 400.0}]},
|
||||
{:report_end_stops, [xa: 1, xb: 2, ya: 3, yb: 4, za: 5, zb: 6]},
|
||||
{:report_parameter_value, [{:param_version, 1.2}]},
|
||||
{:report_pin_value, [{:p, 1}, {:v, 2}, {:m, 0}, {:q, 3}]},
|
||||
{:report_position_change, [{:x, 200.0}]},
|
||||
{:report_position, [x: 1.4, y: 2.3, z: 3.2, s: 4.1]},
|
||||
{:report_software_version, ["6.5.0.G"]}
|
||||
]
|
||||
|
||||
Enum.map(reports, fn report ->
|
||||
assert :ok = try_cast(pid, {:x, report})
|
||||
end)
|
||||
|
||||
Process.sleep(1000)
|
||||
end
|
||||
|
||||
test "various command()s" do
|
||||
pid = firmware_server()
|
||||
|
||||
|
@ -26,31 +71,24 @@ defmodule FarmbotFirmwareTest do
|
|||
# in runtime behavior.
|
||||
#
|
||||
# Approach with caution.
|
||||
{:command_movement,
|
||||
[x: 0.0, y: 0.0, z: 0.0, a: 400.0, b: 400.0, c: 400.0]},
|
||||
{:command_movement,
|
||||
[x: 0.0, y: 0.0, z: 10.0, a: 400.0, b: 400.0, c: 400.0]},
|
||||
{:parameter_write, [movement_home_up_y: 0.0]},
|
||||
{:parameter_write, [movement_home_up_y: 1.0]},
|
||||
{:parameter_write, [movement_invert_2_endpoints_y: 1.0]},
|
||||
{:parameter_write, [movement_stop_at_home_x: 0.0]},
|
||||
{:parameter_write, [movement_stop_at_home_x: 1.0]},
|
||||
{:pin_mode_write, [p: 13, m: 1]},
|
||||
{"1", {:position_write_zero, [:x]}},
|
||||
{"23", {:parameter_write, [movement_invert_2_endpoints_y: 1.0]}},
|
||||
{"24", {:parameter_write, [movement_stop_at_home_x: 0.0]}},
|
||||
{"40", {:pin_write, [p: 13, v: 0, m: 0]}},
|
||||
{"49", {:pin_mode_write, [p: 13, m: 1]}},
|
||||
{"55", {:pin_mode_write, [p: 13, m: 1]}},
|
||||
{"59", {:pin_mode_write, [p: 13, m: 1]}},
|
||||
{"94", {:parameter_write, [movement_home_up_y: 1.0]}},
|
||||
{"94", {:pin_write, [p: 13, v: 1, m: 0]}},
|
||||
{"98", {:parameter_write, [movement_home_up_y: 0.0]}},
|
||||
{"99", {:parameter_write, [movement_stop_at_home_x: 1.0]}},
|
||||
{nil, {:command_emergency_lock, []}},
|
||||
{nil, {:command_emergency_lock, []}},
|
||||
{nil,
|
||||
{:command_movement,
|
||||
[x: 0.0, y: 0.0, z: 0.0, a: 400.0, b: 400.0, c: 400.0]}},
|
||||
{nil,
|
||||
{:command_movement,
|
||||
[x: 0.0, y: 0.0, z: 10.0, a: 400.0, b: 400.0, c: 400.0]}},
|
||||
{nil, {:command_emergency_lock, []}}
|
||||
{:pin_write, [p: 13, v: 0, m: 0]},
|
||||
{:pin_write, [p: 13, v: 1, m: 0]},
|
||||
{:position_write_zero, [:x]}
|
||||
# {:command_emergency_lock, []},
|
||||
]
|
||||
|
||||
Enum.map(cmds, fn {tag, cmd} ->
|
||||
assert {:ok, tag} = try_command(pid, {tag, cmd})
|
||||
Enum.map(cmds, fn cmd ->
|
||||
assert {:ok, "2"} = try_command(pid, {"2", cmd})
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue