WIP. Moving firmware stuff into handlers.
parent
b26bf4f59f
commit
e940b2c940
|
@ -77,29 +77,6 @@ defmodule FarmbotFirmware do
|
|||
def handle_call({"166", {:parameter_write, [some_param: 100.00]}}, _from, state)
|
||||
|
||||
and reply with `:ok | {:error, term()}`
|
||||
|
||||
# VCR
|
||||
|
||||
This server can save all the input and output gcodes to a text file for
|
||||
further external analysis or playback later.
|
||||
|
||||
## Using VCR mode
|
||||
|
||||
The server can be started in VCR mode by doing:
|
||||
|
||||
FarmbotFirmware.start_link([transport: FarmbotFirmware.StubTransport, vcr_path: "/tmp/vcr.txt"], [])
|
||||
|
||||
or can be started at runtime:
|
||||
|
||||
FarmbotFirmware.enter_vcr_mode(firmware_server, "/tmp/vcr.txt")
|
||||
|
||||
in either case the VCR recording needs to be stopped:
|
||||
|
||||
FarmbotFirmware.exit_vcr_mode(firmware_server)
|
||||
|
||||
VCRs can later be played back:
|
||||
|
||||
FarmbotFirmware.VCR.playback!("/tmp/vcr.txt")
|
||||
"""
|
||||
use GenServer
|
||||
require Logger
|
||||
|
@ -129,7 +106,6 @@ defmodule FarmbotFirmware do
|
|||
:command_queue,
|
||||
:caller_pid,
|
||||
:current,
|
||||
:vcr_fd,
|
||||
:reset,
|
||||
:reset_pid
|
||||
]
|
||||
|
@ -146,7 +122,6 @@ defmodule FarmbotFirmware do
|
|||
command_queue: [{pid(), GCODE.t()}],
|
||||
caller_pid: nil | pid,
|
||||
current: nil | GCODE.t(),
|
||||
vcr_fd: nil | File.io_device(),
|
||||
reset: module(),
|
||||
reset_pid: nil | pid()
|
||||
}
|
||||
|
@ -209,22 +184,6 @@ defmodule FarmbotFirmware do
|
|||
GenServer.call(server, :reset)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sets the Firmware server to record input and output GCODES
|
||||
to a pair of text files.
|
||||
"""
|
||||
def enter_vcr_mode(server \\ __MODULE__, tape_path) do
|
||||
GenServer.call(server, {:enter_vcr_mode, tape_path})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sets the Firmware server to stop recording input and output
|
||||
GCODES.
|
||||
"""
|
||||
def exit_vcr_mode(server \\ __MODULE__) do
|
||||
GenServer.cast(server, :exit_vcr_mode)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Starting the Firmware server requires at least:
|
||||
* `:transport` - a module implementing the Transport GenServer behaviour.
|
||||
|
@ -252,18 +211,6 @@ defmodule FarmbotFirmware do
|
|||
# probably?
|
||||
reset = Keyword.get(args, :reset) || FarmbotFirmware.NullReset
|
||||
|
||||
vcr_fd =
|
||||
case Keyword.get(args, :vcr_path) do
|
||||
nil ->
|
||||
nil
|
||||
|
||||
tape_path ->
|
||||
{:ok, vcr_fd} =
|
||||
File.open(tape_path, [:binary, :append, :exclusive, :write])
|
||||
|
||||
vcr_fd
|
||||
end
|
||||
|
||||
# Add an anon function that transport implementations should call.
|
||||
fw = self()
|
||||
fun = fn {_, _} = code -> GenServer.cast(fw, code) end
|
||||
|
@ -279,8 +226,7 @@ defmodule FarmbotFirmware do
|
|||
reset: reset,
|
||||
reset_pid: nil,
|
||||
command_queue: [],
|
||||
configuration_queue: [],
|
||||
vcr_fd: vcr_fd
|
||||
configuration_queue: []
|
||||
}
|
||||
|
||||
send(self(), :timeout)
|
||||
|
@ -358,7 +304,6 @@ defmodule FarmbotFirmware do
|
|||
}
|
||||
|
||||
_ = side_effects(new_state, :handle_output_gcode, [{state.tag, code}])
|
||||
_ = vcr_write(state, :out, {state.tag, code})
|
||||
|
||||
{:noreply, new_state}
|
||||
|
||||
|
@ -374,7 +319,6 @@ defmodule FarmbotFirmware do
|
|||
:ok ->
|
||||
new_state = %{state | current: code, configuration_queue: rest}
|
||||
_ = side_effects(new_state, :handle_output_gcode, [{state.tag, code}])
|
||||
_ = vcr_write(state, :out, {state.tag, code})
|
||||
{:noreply, new_state}
|
||||
|
||||
error ->
|
||||
|
@ -411,7 +355,6 @@ defmodule FarmbotFirmware do
|
|||
}
|
||||
|
||||
_ = side_effects(new_state, :handle_output_gcode, [{state.tag, code}])
|
||||
_ = vcr_write(state, :out, {state.tag, code})
|
||||
for {pid, _code} <- rest, do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
{:noreply, new_state}
|
||||
|
@ -482,17 +425,7 @@ defmodule FarmbotFirmware do
|
|||
_from,
|
||||
%{status: s} = state
|
||||
) do
|
||||
{:reply, {:error, s}, state}
|
||||
end
|
||||
|
||||
def handle_call({:enter_vcr_mode, tape_path}, _from, state) do
|
||||
with {:ok, vcr_fd} <-
|
||||
File.open(tape_path, [:binary, :append, :exclusive, :write]) do
|
||||
{:reply, :ok, %{state | vcr_fd: vcr_fd}}
|
||||
else
|
||||
error ->
|
||||
{:reply, error, state}
|
||||
end
|
||||
{:reply, {:error, "Can't open transport in #{inspect(s)} state"}, state}
|
||||
end
|
||||
|
||||
def handle_call({tag, {kind, args}}, from, state) do
|
||||
|
@ -565,388 +498,10 @@ defmodule FarmbotFirmware do
|
|||
end
|
||||
end
|
||||
|
||||
def handle_cast(:exit_vcr_mode, state) do
|
||||
state.vcr_fd && File.close(state.vcr_fd)
|
||||
{:noreply, %{state | vcr_fd: nil}}
|
||||
end
|
||||
|
||||
# Extracts tag
|
||||
def handle_cast({tag, {_, _} = code}, state) do
|
||||
_ = side_effects(state, :handle_input_gcode, [{tag, code}])
|
||||
_ = vcr_write(state, :in, {tag, code})
|
||||
handle_report(code, %{state | tag: tag})
|
||||
end
|
||||
|
||||
@doc false
|
||||
@spec handle_report({GCODE.report_kind(), GCODE.args()}, state) ::
|
||||
{:noreply, state()}
|
||||
def handle_report({:report_emergency_lock, []} = code, state) do
|
||||
Logger.info("Emergency lock")
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
for {pid, _code} <- state.command_queue, do: send(pid, code)
|
||||
|
||||
send(self(), :timeout)
|
||||
{:noreply, goto(%{state | current: nil, caller_pid: nil}, :emergency_lock)}
|
||||
end
|
||||
|
||||
# "ARDUINO STARTUP COMPLETE" => goto(:boot, :no_config)
|
||||
def handle_report(
|
||||
{:unknown, [_, "ARDUINO", "STARTUP", "COMPLETE"]},
|
||||
%{status: :boot} = state
|
||||
) do
|
||||
Logger.info("ARDUINO STARTUP COMPLETE (text) transport=#{state.transport}")
|
||||
handle_report({:report_no_config, []}, state)
|
||||
end
|
||||
|
||||
def handle_report(
|
||||
{:report_idle, []},
|
||||
%{status: :boot} = state
|
||||
) do
|
||||
Logger.info("ARDUINO STARTUP COMPLETE (idle) transport=#{state.transport}")
|
||||
handle_report({:report_no_config, []}, state)
|
||||
end
|
||||
|
||||
def handle_report(
|
||||
{:report_debug_message, ["ARDUINO STARTUP COMPLETE"]},
|
||||
%{status: :boot} = state
|
||||
) do
|
||||
Logger.info("ARDUINO STARTUP COMPLETE (r99) transport=#{state.transport}")
|
||||
handle_report({:report_no_config, []}, state)
|
||||
end
|
||||
|
||||
# report_no_config => goto(_, :no_config)
|
||||
def handle_report({:report_no_config, []}, %{status: _} = state) do
|
||||
Logger.warn(":report_no_config received")
|
||||
tag = state.tag || "0"
|
||||
loaded_params = side_effects(state, :load_params, []) || []
|
||||
|
||||
param_commands =
|
||||
Enum.reduce(loaded_params, [], fn {param, val}, acc ->
|
||||
if val, do: acc ++ [{:parameter_write, [{param, val}]}], else: acc
|
||||
end)
|
||||
|
||||
to_process =
|
||||
[{:software_version_read, []} | param_commands] ++
|
||||
[
|
||||
{:parameter_write, [{:param_use_eeprom, 0.0}]},
|
||||
{:parameter_write, [{:param_config_ok, 1.0}]},
|
||||
{:parameter_read_all, []}
|
||||
]
|
||||
|
||||
to_process =
|
||||
if loaded_params[:movement_home_at_boot_z] == 1,
|
||||
do: to_process ++ [{:command_movement_find_home, [:z]}],
|
||||
else: to_process
|
||||
|
||||
to_process =
|
||||
if loaded_params[:movement_home_at_boot_y] == 1,
|
||||
do: to_process ++ [{:command_movement_find_home, [:y]}],
|
||||
else: to_process
|
||||
|
||||
to_process =
|
||||
if loaded_params[:movement_home_at_boot_x] == 1,
|
||||
do: to_process ++ [{:command_movement_find_home, [:x]}],
|
||||
else: to_process
|
||||
|
||||
send(self(), :timeout)
|
||||
|
||||
{:noreply,
|
||||
goto(%{state | tag: tag, configuration_queue: to_process}, :configuration)}
|
||||
end
|
||||
|
||||
def handle_report({:report_debug_message, msg}, state) do
|
||||
side_effects(state, :handle_debug_message, [msg])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report(report, %{status: :boot} = state) do
|
||||
Logger.debug(["still in state: :boot ", inspect(report)])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
# an idle report while there is a current command running
|
||||
# should not count.
|
||||
def handle_report({:report_idle, []}, %{current: c} = state)
|
||||
when is_tuple(c) do
|
||||
if state.caller_pid,
|
||||
do: send(state.caller_pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
# report_idle => goto(_, :idle)
|
||||
def handle_report({:report_idle, []}, %{status: _} = state) do
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_busy, [false])
|
||||
side_effects(state, :handle_idle, [true])
|
||||
send(self(), :timeout)
|
||||
{:noreply, goto(%{state | caller_pid: nil, current: nil}, :idle)}
|
||||
end
|
||||
|
||||
def handle_report({:report_begin, []} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
{:noreply, goto(state, :begin)}
|
||||
end
|
||||
|
||||
def handle_report({:report_success, []} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
new_state = %{state | current: nil, caller_pid: nil}
|
||||
side_effects(state, :handle_busy, [false])
|
||||
send(self(), :timeout)
|
||||
{:noreply, goto(new_state, :idle)}
|
||||
end
|
||||
|
||||
def handle_report({:report_busy, []} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_busy, [true])
|
||||
{:noreply, goto(state, :busy)}
|
||||
end
|
||||
|
||||
def handle_report(
|
||||
{:report_error, _} = code,
|
||||
%{status: :configuration} = state
|
||||
) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_busy, [false])
|
||||
{:stop, {:error, state.current}, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_error, _} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_busy, [false])
|
||||
send(self(), :timeout)
|
||||
{:noreply, %{state | caller_pid: nil, current: nil}}
|
||||
end
|
||||
|
||||
def handle_report(
|
||||
{:report_invalid, []} = code,
|
||||
%{status: :configuration} = state
|
||||
) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
{:stop, {:error, state.current}, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_invalid, []} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
send(self(), :timeout)
|
||||
{:noreply, %{state | caller_pid: nil, current: nil}}
|
||||
end
|
||||
|
||||
def handle_report(
|
||||
{:report_retry, []} = code,
|
||||
%{status: :configuration} = state
|
||||
) do
|
||||
Logger.warn("Retrying configuration command: #{inspect(code)}")
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_retry, []} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_parameter_value, param} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_parameter_value, [param])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_calibration_parameter_value, param} = _code, state) do
|
||||
to_process = [{:parameter_write, param}]
|
||||
side_effects(state, :handle_parameter_value, [param])
|
||||
side_effects(state, :handle_parameter_calibration_value, [param])
|
||||
send(self(), :timeout)
|
||||
|
||||
{:noreply,
|
||||
goto(
|
||||
%{state | tag: state.tag, configuration_queue: to_process},
|
||||
:configuration
|
||||
)}
|
||||
end
|
||||
|
||||
# report_parameters_complete => goto(:configuration, :idle)
|
||||
def handle_report(
|
||||
{:report_parameters_complete, []},
|
||||
%{status: status} = state
|
||||
)
|
||||
when status in [:begin, :configuration] do
|
||||
{:noreply, goto(state, :idle)}
|
||||
end
|
||||
|
||||
def handle_report(_, %{status: :no_config} = state) do
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_position, position} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_position, [position])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_load, load} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_load, [load])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_axis_state, axis_state} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_axis_state, [axis_state])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_axis_timeout, [axis]} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_axis_timeout, [axis])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report(
|
||||
{:report_calibration_state, calibration_state} = code,
|
||||
state
|
||||
) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_calibration_state, [calibration_state])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_home_complete, axis} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_home_complete, axis)
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_position_change, position} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_position_change, [position])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_encoders_scaled, encoders} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_encoders_scaled, [encoders])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_encoders_raw, encoders} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_encoders_raw, [encoders])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_end_stops, end_stops} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_end_stops, [end_stops])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_pin_value, value} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_pin_value, [value])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_software_version, version} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_software_version, [version])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
# NOOP
|
||||
def handle_report({:report_echo, _}, state), do: {:noreply, state}
|
||||
|
||||
def handle_report({_kind, _args} = code, state) do
|
||||
Logger.warn("unknown code for #{state.status}: #{inspect(code)}")
|
||||
{:noreply, state}
|
||||
FarmbotFirmware.ReportHandler.handle_report(code, %{state | tag: tag})
|
||||
end
|
||||
|
||||
@spec goto(state(), status()) :: state()
|
||||
|
@ -994,35 +549,7 @@ defmodule FarmbotFirmware do
|
|||
end
|
||||
|
||||
@spec side_effects(state, atom, GCODE.args()) :: any()
|
||||
defp side_effects(%{side_effects: nil}, _function, _args), do: nil
|
||||
defp side_effects(%{side_effects: nil}, _func, _args), do: nil
|
||||
|
||||
defp side_effects(%{side_effects: m}, function, args),
|
||||
do: apply(m, function, args)
|
||||
|
||||
@spec vcr_write(state, :in | :out, GCODE.t()) :: :ok
|
||||
defp vcr_write(%{vcr_fd: nil}, _direction, _code), do: :ok
|
||||
|
||||
defp vcr_write(state, :in, code), do: vcr_write(state, "<", code)
|
||||
|
||||
defp vcr_write(state, :out, code), do: vcr_write(state, "\n>", code)
|
||||
|
||||
defp vcr_write(state, direction, code) do
|
||||
data = GCODE.encode(code)
|
||||
time = :os.system_time(:second)
|
||||
|
||||
current_data =
|
||||
if state.current do
|
||||
GCODE.encode({state.tag, state.current})
|
||||
else
|
||||
"nil"
|
||||
end
|
||||
|
||||
state_data =
|
||||
"#{state.status} | #{current_data} | #{inspect(state.caller_pid)}"
|
||||
|
||||
IO.write(
|
||||
state.vcr_fd,
|
||||
direction <> " #{time} " <> data <> " state=" <> state_data <> "\n"
|
||||
)
|
||||
end
|
||||
defp side_effects(%{side_effects: m}, func, args), do: apply(m, func, args)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,375 @@
|
|||
defmodule FarmbotFirmware.ReportHandler do
|
||||
require Logger
|
||||
|
||||
@doc false
|
||||
@spec handle_report({GCODE.report_kind(), GCODE.args()}, state) ::
|
||||
{:noreply, state()}
|
||||
def handle_report({:report_emergency_lock, []} = code, state) do
|
||||
Logger.info("Emergency lock")
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
for {pid, _code} <- state.command_queue, do: send(pid, code)
|
||||
|
||||
send(self(), :timeout)
|
||||
{:noreply, goto(%{state | current: nil, caller_pid: nil}, :emergency_lock)}
|
||||
end
|
||||
|
||||
# "ARDUINO STARTUP COMPLETE" => goto(:boot, :no_config)
|
||||
def handle_report(
|
||||
{:unknown, [_, "ARDUINO", "STARTUP", "COMPLETE"]},
|
||||
%{status: :boot} = state
|
||||
) do
|
||||
Logger.info("ARDUINO STARTUP COMPLETE (text) transport=#{state.transport}")
|
||||
handle_report({:report_no_config, []}, state)
|
||||
end
|
||||
|
||||
def handle_report(
|
||||
{:report_idle, []},
|
||||
%{status: :boot} = state
|
||||
) do
|
||||
Logger.info("ARDUINO STARTUP COMPLETE (idle) transport=#{state.transport}")
|
||||
handle_report({:report_no_config, []}, state)
|
||||
end
|
||||
|
||||
def handle_report(
|
||||
{:report_debug_message, ["ARDUINO STARTUP COMPLETE"]},
|
||||
%{status: :boot} = state
|
||||
) do
|
||||
Logger.info("ARDUINO STARTUP COMPLETE (r99) transport=#{state.transport}")
|
||||
handle_report({:report_no_config, []}, state)
|
||||
end
|
||||
|
||||
# report_no_config => goto(_, :no_config)
|
||||
def handle_report({:report_no_config, []}, %{status: _} = state) do
|
||||
Logger.warn(":report_no_config received")
|
||||
tag = state.tag || "0"
|
||||
loaded_params = side_effects(state, :load_params, []) || []
|
||||
|
||||
param_commands =
|
||||
Enum.reduce(loaded_params, [], fn {param, val}, acc ->
|
||||
if val, do: acc ++ [{:parameter_write, [{param, val}]}], else: acc
|
||||
end)
|
||||
|
||||
to_process =
|
||||
[{:software_version_read, []} | param_commands] ++
|
||||
[
|
||||
{:parameter_write, [{:param_use_eeprom, 0.0}]},
|
||||
{:parameter_write, [{:param_config_ok, 1.0}]},
|
||||
{:parameter_read_all, []}
|
||||
]
|
||||
|
||||
to_process =
|
||||
if loaded_params[:movement_home_at_boot_z] == 1,
|
||||
do: to_process ++ [{:command_movement_find_home, [:z]}],
|
||||
else: to_process
|
||||
|
||||
to_process =
|
||||
if loaded_params[:movement_home_at_boot_y] == 1,
|
||||
do: to_process ++ [{:command_movement_find_home, [:y]}],
|
||||
else: to_process
|
||||
|
||||
to_process =
|
||||
if loaded_params[:movement_home_at_boot_x] == 1,
|
||||
do: to_process ++ [{:command_movement_find_home, [:x]}],
|
||||
else: to_process
|
||||
|
||||
send(self(), :timeout)
|
||||
|
||||
{:noreply,
|
||||
goto(%{state | tag: tag, configuration_queue: to_process}, :configuration)}
|
||||
end
|
||||
|
||||
def handle_report({:report_debug_message, msg}, state) do
|
||||
side_effects(state, :handle_debug_message, [msg])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report(report, %{status: :boot} = state) do
|
||||
Logger.debug(["still in state: :boot ", inspect(report)])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
# an idle report while there is a current command running
|
||||
# should not count.
|
||||
def handle_report({:report_idle, []}, %{current: c} = state)
|
||||
when is_tuple(c) do
|
||||
if state.caller_pid,
|
||||
do: send(state.caller_pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
# report_idle => goto(_, :idle)
|
||||
def handle_report({:report_idle, []}, %{status: _} = state) do
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_busy, [false])
|
||||
side_effects(state, :handle_idle, [true])
|
||||
send(self(), :timeout)
|
||||
{:noreply, goto(%{state | caller_pid: nil, current: nil}, :idle)}
|
||||
end
|
||||
|
||||
def handle_report({:report_begin, []} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
{:noreply, goto(state, :begin)}
|
||||
end
|
||||
|
||||
def handle_report({:report_success, []} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
new_state = %{state | current: nil, caller_pid: nil}
|
||||
side_effects(state, :handle_busy, [false])
|
||||
send(self(), :timeout)
|
||||
{:noreply, goto(new_state, :idle)}
|
||||
end
|
||||
|
||||
def handle_report({:report_busy, []} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_busy, [true])
|
||||
{:noreply, goto(state, :busy)}
|
||||
end
|
||||
|
||||
def handle_report(
|
||||
{:report_error, _} = code,
|
||||
%{status: :configuration} = state
|
||||
) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_busy, [false])
|
||||
{:stop, {:error, state.current}, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_error, _} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_busy, [false])
|
||||
send(self(), :timeout)
|
||||
{:noreply, %{state | caller_pid: nil, current: nil}}
|
||||
end
|
||||
|
||||
def handle_report(
|
||||
{:report_invalid, []} = code,
|
||||
%{status: :configuration} = state
|
||||
) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
{:stop, {:error, state.current}, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_invalid, []} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
send(self(), :timeout)
|
||||
{:noreply, %{state | caller_pid: nil, current: nil}}
|
||||
end
|
||||
|
||||
def handle_report(
|
||||
{:report_retry, []} = code,
|
||||
%{status: :configuration} = state
|
||||
) do
|
||||
Logger.warn("Retrying configuration command: #{inspect(code)}")
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_retry, []} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_parameter_value, param} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_parameter_value, [param])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_calibration_parameter_value, param} = _code, state) do
|
||||
to_process = [{:parameter_write, param}]
|
||||
side_effects(state, :handle_parameter_value, [param])
|
||||
side_effects(state, :handle_parameter_calibration_value, [param])
|
||||
send(self(), :timeout)
|
||||
|
||||
{:noreply,
|
||||
goto(
|
||||
%{state | tag: state.tag, configuration_queue: to_process},
|
||||
:configuration
|
||||
)}
|
||||
end
|
||||
|
||||
# report_parameters_complete => goto(:configuration, :idle)
|
||||
def handle_report(
|
||||
{:report_parameters_complete, []},
|
||||
%{status: status} = state
|
||||
)
|
||||
when status in [:begin, :configuration] do
|
||||
{:noreply, goto(state, :idle)}
|
||||
end
|
||||
|
||||
def handle_report(_, %{status: :no_config} = state) do
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_position, position} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_position, [position])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_load, load} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_load, [load])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_axis_state, axis_state} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_axis_state, [axis_state])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_axis_timeout, [axis]} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_axis_timeout, [axis])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report(
|
||||
{:report_calibration_state, calibration_state} = code,
|
||||
state
|
||||
) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_calibration_state, [calibration_state])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_home_complete, axis} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_home_complete, axis)
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_position_change, position} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_position_change, [position])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_encoders_scaled, encoders} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_encoders_scaled, [encoders])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_encoders_raw, encoders} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_encoders_raw, [encoders])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_end_stops, end_stops} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_end_stops, [end_stops])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_pin_value, value} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_pin_value, [value])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_report({:report_software_version, version} = code, state) do
|
||||
if state.caller_pid, do: send(state.caller_pid, {state.tag, code})
|
||||
|
||||
for {pid, _code} <- state.command_queue,
|
||||
do: send(pid, {state.tag, {:report_busy, []}})
|
||||
|
||||
side_effects(state, :handle_software_version, [version])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
# NOOP
|
||||
def handle_report({:report_echo, _}, state), do: {:noreply, state}
|
||||
|
||||
def handle_report({_kind, _args} = code, state) do
|
||||
Logger.warn("unknown code for #{state.status}: #{inspect(code)}")
|
||||
{:noreply, state}
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue