simplified serial again
parent
837c2d39a3
commit
567c0efb81
|
@ -28,7 +28,6 @@ defmodule Farmbot.CeleryScript.Command.ConfigUpdate do
|
|||
if param_int do
|
||||
Logger.info ">> is updating #{param_str}: #{val}"
|
||||
"F22 P#{param_int} V#{val}" |> UartHan.write
|
||||
Process.sleep(500)
|
||||
# HACK read the param back because sometimes the firmware decides
|
||||
# our param sets arent important enough to keep
|
||||
read_param(%{label: param_str}, [])
|
||||
|
@ -48,9 +47,11 @@ defmodule Farmbot.CeleryScript.Command.ConfigUpdate do
|
|||
|
||||
@spec filter_params([{binary, any}], map) :: [{binary, any}]
|
||||
defp filter_params(blah, current) do
|
||||
Enum.filter(blah, fn({param_str, val}) ->
|
||||
result = Enum.filter(blah, fn({param_str, val}) ->
|
||||
current[param_str] != val
|
||||
end)
|
||||
# Im sorry about this
|
||||
if Enum.empty?(result), do: blah, else: result
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -151,35 +151,6 @@ defmodule Farmbot.Configurator.Router do
|
|||
end
|
||||
end
|
||||
|
||||
get "/image/latest" do
|
||||
list_images = fn() ->
|
||||
"/tmp/images"
|
||||
|> File.ls!
|
||||
|> Enum.reduce("", fn(image, acc) ->
|
||||
acc <> "<img src=\"/image/#{image}\">"
|
||||
end)
|
||||
end
|
||||
html =
|
||||
~s"""
|
||||
<html>
|
||||
<body>
|
||||
<form action=/image/capture>
|
||||
<input type="submit" value="Capture">
|
||||
</form>
|
||||
#{list_images.()}
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
conn |> send_resp(200, html)
|
||||
end
|
||||
|
||||
get "/image/capture" do
|
||||
Farmbot.CeleryScript.Command.take_photo %{}, []
|
||||
conn
|
||||
|> put_resp_header("location", "/image/latest")
|
||||
|> send_resp(302, "OK")
|
||||
end
|
||||
|
||||
# anything that doesn't match a rest end point gets the index.
|
||||
match _, do: conn |> send_resp(404, "not found")
|
||||
|
||||
|
@ -194,7 +165,7 @@ defmodule Farmbot.Configurator.Router do
|
|||
:done ->
|
||||
blerp |> send_resp(200, "OK")
|
||||
{:error, reason} ->
|
||||
blerp |> send_resp(400, IO.inspect(reason))
|
||||
blerp |> send_resp(400, inspect(reason))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,12 +5,6 @@ defmodule Farmbot.Serial.Gcode.Parser do
|
|||
|
||||
@spec parse_code(binary) :: {binary, tuple}
|
||||
|
||||
# ????
|
||||
def parse_code("R0 Q" <> tag), do: {tag, :idle}
|
||||
def parse_code("R1 Q" <> tag), do: {tag, :received}
|
||||
def parse_code("R2 Q" <> tag), do: {tag, :done}
|
||||
def parse_code("R3 Q" <> tag), do: {tag, :error}
|
||||
def parse_code("R4 Q" <> tag), do: {tag, :busy}
|
||||
# / ???
|
||||
def parse_code("R00 Q" <> tag), do: {tag, :idle}
|
||||
def parse_code("R01 Q" <> tag), do: {tag, :received}
|
||||
|
@ -19,7 +13,7 @@ defmodule Farmbot.Serial.Gcode.Parser do
|
|||
def parse_code("R04 Q" <> tag), do: {tag, :busy}
|
||||
|
||||
# TODO(Connor) Fix these
|
||||
def parse_code("R05" <> _r), do: :dont_handle_me # Dont care about this.
|
||||
def parse_code("R05" <> _r), do: {nil, :dont_handle_me} # Dont care about this.
|
||||
def parse_code("R06 " <> r), do: parse_report_calibration(r)
|
||||
|
||||
def parse_code("R21 " <> params), do: parse_pvq(params, :report_parameter_value)
|
||||
|
@ -28,8 +22,8 @@ defmodule Farmbot.Serial.Gcode.Parser do
|
|||
def parse_code("R81 " <> params), do: parse_end_stops(params)
|
||||
def parse_code("R82 " <> params), do: parse_report_current_position(params)
|
||||
def parse_code("R83 " <> v), do: parse_version(v)
|
||||
def parse_code("R99 " <> message) do {:debug_message, message} end
|
||||
def parse_code("Command" <> _), do: :dont_handle_me # I think this is a bug
|
||||
def parse_code("R99 " <> message) do {nil, {:debug_message, message}} end
|
||||
def parse_code("Command" <> _), do: {nil, :dont_handle_me} # I think this is a bug
|
||||
def parse_code(code) do {:unhandled_gcode, code} end
|
||||
|
||||
@spec parse_report_calibration(binary)
|
||||
|
|
|
@ -10,8 +10,6 @@ defmodule Farmbot.Serial.Handler do
|
|||
alias Farmbot.BotState
|
||||
alias Farmbot.Lib.Maths
|
||||
|
||||
@race_fix 5000
|
||||
|
||||
@typedoc """
|
||||
Handler pid or name
|
||||
"""
|
||||
|
@ -22,31 +20,20 @@ defmodule Farmbot.Serial.Handler do
|
|||
"""
|
||||
@type nerves :: handler
|
||||
|
||||
@typedoc """
|
||||
The current command in the buffer being worked on.
|
||||
"""
|
||||
@type current :: %{
|
||||
reply: nil | term,
|
||||
handshake: binary,
|
||||
timeout: reference | nil,
|
||||
from: {pid, reference}
|
||||
}
|
||||
|
||||
@typedoc """
|
||||
State of the GenServer
|
||||
"""
|
||||
@type state :: %{
|
||||
nerves: nerves,
|
||||
tty: binary,
|
||||
queue: :queue.queue,
|
||||
current: nil | :no_firm | current
|
||||
}
|
||||
@type state :: {:hey, :fixme}
|
||||
|
||||
@doc """
|
||||
Starts a UART GenServer
|
||||
"""
|
||||
def start_link(nerves, tty) do
|
||||
GenServer.start_link(__MODULE__, {nerves, tty})
|
||||
def start_link(nerves, tty, opts) when is_pid(nerves) and is_binary(tty) do
|
||||
GenServer.start_link(__MODULE__, {nerves, tty}, opts)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Starts a UART GenServer
|
||||
"""
|
||||
def start_link(tty, opts) when is_binary(tty) do
|
||||
GenServer.start_link(__MODULE__, tty, opts)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -55,10 +42,12 @@ defmodule Farmbot.Serial.Handler do
|
|||
@spec available?(handler) :: boolean
|
||||
def available?(handler \\ __MODULE__)
|
||||
|
||||
# If handler is a pid
|
||||
def available?(handler) when is_pid(handler) do
|
||||
GenServer.call(handler, :available?)
|
||||
end
|
||||
|
||||
# if its a name, look it up
|
||||
def available?(handler) do
|
||||
uh = Process.whereis(handler)
|
||||
if uh do
|
||||
|
@ -97,319 +86,167 @@ defmodule Farmbot.Serial.Handler do
|
|||
|
||||
## Private
|
||||
|
||||
def init({nerves, tty}) when is_pid(nerves) and is_binary(tty) do
|
||||
Process.link(nerves)
|
||||
:ok = open_tty(nerves, tty)
|
||||
GenServer.cast(Farmbot.BotState.Hardware, :eff)
|
||||
{:ok, %{nerves: nerves, tty: tty, current: nil}}
|
||||
end
|
||||
|
||||
def init(tty) when is_binary(tty) do
|
||||
{:ok, nerves} = UART.start_link()
|
||||
init({nerves, tty})
|
||||
end
|
||||
|
||||
@spec open_tty(nerves, binary) :: :ok
|
||||
defp open_tty(nerves, tty) do
|
||||
# Open the tty
|
||||
:ok = UART.open(nerves, tty)
|
||||
|
||||
# configure framing
|
||||
UART.configure(nerves,
|
||||
:ok = UART.configure(nerves,
|
||||
framing: {UART.Framing.Line, separator: "\r\n"},
|
||||
active: true,
|
||||
rx_framing_timeout: 500)
|
||||
|
||||
# Black magic to fix races
|
||||
Process.sleep(@race_fix)
|
||||
|
||||
# Flush the buffers so we start fresh
|
||||
UART.flush(nerves)
|
||||
|
||||
:ok = UART.flush(nerves)
|
||||
:ok
|
||||
end
|
||||
|
||||
@spec init({nerves, binary}) :: {:ok, state} | :ignore
|
||||
def init({nerves, tty}) do
|
||||
Process.link(nerves)
|
||||
Logger.info "Starting serial handler: #{tty}"
|
||||
|
||||
:ok = open_tty(nerves, tty)
|
||||
update_default(self())
|
||||
|
||||
# generate a handshake
|
||||
handshake = generate_handshake()
|
||||
Logger.info "doing handshaking: #{handshake}"
|
||||
|
||||
if do_handshake(nerves, tty, handshake) do
|
||||
UART.write(nerves, "F83 #{handshake}") # ???
|
||||
do_hax()
|
||||
state = %{tty: tty, nerves: nerves, queue: :queue.new(), current: nil}
|
||||
{:ok, state}
|
||||
else
|
||||
Logger.warn "Handshake failed!"
|
||||
state = %{
|
||||
tty: tty, nerves: nerves, queue: :queue.new(), current: :no_firm
|
||||
}
|
||||
{:ok, state}
|
||||
end
|
||||
end
|
||||
|
||||
# Shhhhh
|
||||
@spec do_hax :: no_return
|
||||
defp do_hax, do: GenServer.cast(Farmbot.BotState.Hardware, :eff)
|
||||
|
||||
@spec generate_handshake :: binary
|
||||
defp generate_handshake do
|
||||
random_int = :rand.uniform(99)
|
||||
"Q#{random_int}"
|
||||
end
|
||||
|
||||
@spec do_handshake(nerves, binary, binary, integer) :: boolean
|
||||
defp do_handshake(nerves, tty, handshake, retries \\ 5)
|
||||
|
||||
defp do_handshake(_, _, _, 0) do
|
||||
Logger.info "Could not handshake: to many retries."
|
||||
false
|
||||
end
|
||||
|
||||
defp do_handshake(nerves, tty, handshake, retries) do
|
||||
# Write a command to UART
|
||||
UART.write(nerves, "F83 #{handshake}")
|
||||
|
||||
# Wait for it to respong
|
||||
receive do
|
||||
# if it sends a partial, we are probably out of sync
|
||||
# flush the buffer and try again.
|
||||
{:nreves_uart, ^tty, {:partial, _}} ->
|
||||
UART.flush(nerves)
|
||||
do_handshake(nerves, tty, handshake)
|
||||
|
||||
# Recieved happens before our actual response, just go to the next one
|
||||
# if it exists
|
||||
{:nerves_uart, ^tty, "R01" <> _} -> do_handshake(nerves, tty, handshake)
|
||||
{:nerves_uart, ^tty, "Command:" <> _} -> do_handshake(nerves, tty, handshake)
|
||||
|
||||
# This COULD be our handshake. Check it.
|
||||
{:nerves_uart, ^tty, str} ->
|
||||
# if it contains our handshake, check if its the right command.
|
||||
# flush the buffer and return
|
||||
if String.contains?(str, handshake) do
|
||||
Logger.info "Successfully completed handshake!"
|
||||
"R83 " <> version = String.trim(str, " " <> handshake)
|
||||
Farmbot.BotState.set_fw_version(version)
|
||||
UART.flush(nerves)
|
||||
true
|
||||
else
|
||||
# If not, Move on to the next thing in the buffer.
|
||||
do_handshake(nerves, tty, handshake)
|
||||
end
|
||||
uh ->
|
||||
# if we recieve some other stuff, we have a leak or something.
|
||||
# I think this can be deleted.
|
||||
Logger.warn "Could not handshake: #{inspect uh}"
|
||||
false
|
||||
after
|
||||
# After 2 seconds try again.
|
||||
2_000 ->
|
||||
Logger.warn "Could not handshake: timeout, retrying."
|
||||
do_handshake(nerves, tty, handshake, retries - 1)
|
||||
end
|
||||
end
|
||||
|
||||
@spec update_default(pid) :: :ok | no_return
|
||||
defp update_default(pid) do
|
||||
deregister()
|
||||
# Either way, register this pid as the new one.
|
||||
Process.register(pid, __MODULE__)
|
||||
end
|
||||
|
||||
@spec deregister :: no_return
|
||||
defp deregister do
|
||||
# lookup the old default pid
|
||||
old_pid = Process.whereis(__MODULE__)
|
||||
|
||||
# if one existst, unregister it.
|
||||
if old_pid do
|
||||
Logger.info "Deregistering #{inspect old_pid} from default Serial Handler"
|
||||
Process.unregister(__MODULE__)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_call(:get_state, _, state), do: {:reply, state, state}
|
||||
|
||||
def handle_call(:available?, _from, state) do
|
||||
case state.current do
|
||||
:no_firm -> {:reply, false, state}
|
||||
_ -> {:reply, true, state}
|
||||
end
|
||||
end
|
||||
def handle_call(:available?, _, state), do: {:reply, true, state}
|
||||
|
||||
# A new line to write.
|
||||
def handle_call({:write, str, timeout}, from, state) do
|
||||
# generate a handshake
|
||||
handshake = generate_handshake()
|
||||
# if the queue is empty, write this string now.
|
||||
if :queue.is_empty(state.queue) do
|
||||
ref = Process.send_after(self(), {:timeout, from, handshake}, timeout)
|
||||
current = %{reply: nil, handshake: handshake, timeout: ref, from: from}
|
||||
UART.write(state.nerves, str <> " #{handshake}")
|
||||
{:noreply, %{state | current: current}}
|
||||
else
|
||||
q = :queue.in({str, handshake, from, timeout}, state.queue)
|
||||
{:noreply, %{state | queue: q}}
|
||||
end
|
||||
UART.write(state.nerves, "#{str} #{handshake}")
|
||||
timer = Process.send_after(self(), :timeout, timeout)
|
||||
current = %{status: nil, reply: nil, from: from, q: handshake, timer: timer}
|
||||
{:noreply, %{state | current: current}}
|
||||
end
|
||||
|
||||
def handle_cast({:update_fw, hex_file, pid}, state) do
|
||||
def handle_cast({:update_fw, file, pid}, state) do
|
||||
UART.close(state.nerves)
|
||||
Process.sleep(1000)
|
||||
if String.contains?(state.tty, "tnt") do
|
||||
Logger.warn "Not a real arduino!"
|
||||
send(pid, :done)
|
||||
{:noreply, state}
|
||||
else
|
||||
UART.close(state.nerves)
|
||||
Process.sleep(100)
|
||||
flash_firmware(state.tty, hex_file, pid)
|
||||
Process.sleep(5000)
|
||||
{:ok, new_state} = init({state.nerves, state.tty})
|
||||
{:noreply, new_state}
|
||||
flash_firmware(state.tty, file, pid)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_info({:timeout, from, handshake}, state) do
|
||||
def handle_info(:timeout, state) do
|
||||
current = state.current
|
||||
if current do
|
||||
new_current = maybe_timeout({from, handshake}, current)
|
||||
{:noreply, %{state | current: new_current}}
|
||||
GenServer.reply(current.from, :timeout)
|
||||
{:noreply, %{state | current: nil}}
|
||||
else
|
||||
{:noreply, state}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_info({:nerves_uart, _tty, {:partial, thing}}, state) do
|
||||
Logger.warn ">> got partial gcode: #{thing}"
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info({:nerves_uart, _tty, {:error, :eio}}, state) do
|
||||
Logger.error "ARDUINO DISCONNECTED!"
|
||||
{:noreply, %{state | queue: :queue.new(), current: nil}}
|
||||
end
|
||||
|
||||
# This is when we get a code in from nerves_uart
|
||||
@lint false # this is just a mess sorry
|
||||
def handle_info({:nerves_uart, tty, gcode}, state) do
|
||||
unless tty != state.tty do
|
||||
parsed = Parser.parse_code(gcode)
|
||||
case parsed do
|
||||
# if the code has a handshake and its not done
|
||||
# we just want to handle the code. Nothing special.
|
||||
|
||||
# derp
|
||||
{:debug_message, _message} ->
|
||||
handle_gcode(parsed, state)
|
||||
|
||||
{_hs, :done} ->
|
||||
current = if state.current do
|
||||
# cancel the timer
|
||||
Process.cancel_timer(state.current.timeout)
|
||||
# reply to the client
|
||||
GenServer.reply(state.current.from, state.current.reply)
|
||||
nil
|
||||
else
|
||||
state.current
|
||||
end
|
||||
handle_gcode(:done, %{state | current: current})
|
||||
|
||||
# If its not done,
|
||||
{hs, code} ->
|
||||
current = if (state.current || false) && (state.current.handshake == hs) do
|
||||
%{state.current | reply: code}
|
||||
else
|
||||
state.current
|
||||
end
|
||||
handle_gcode(code, %{state | current: current})
|
||||
|
||||
# anything else just handle the code.
|
||||
_ -> handle_gcode(parsed, state)
|
||||
end
|
||||
def handle_info({:nerves_uart, tty, str}, state) when is_binary(str) do
|
||||
if tty == state.tty do
|
||||
current = str |> Parser.parse_code |> do_handle(state.current)
|
||||
{:noreply, %{state | current: current}}
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_gcode(:dont_handle_me, state), do: {:noreply, state}
|
||||
def handle_info({:nerves_uart, _tty, {:partial, _}}, s), do: {:noreply, s}
|
||||
|
||||
defp handle_gcode(:idle, state) do
|
||||
{:noreply, state}
|
||||
def handle_info({:nerves_uart, tty, {:error, error}}, state) do
|
||||
Logger.error "#{tty} handler exiting!: #{error}"
|
||||
{:stop, error, state}
|
||||
end
|
||||
|
||||
defp handle_gcode(:busy, state) do
|
||||
def terminate(reason, state) do
|
||||
UART.close(state.nerves)
|
||||
GenServer.stop(state.nerves, reason)
|
||||
end
|
||||
|
||||
@spec do_handle({binary, any}, map | nil) :: map | nil
|
||||
defp do_handle({_qcode, parsed}, current) when is_map(current) do
|
||||
case handle_gcode(parsed) do
|
||||
{:status, :done} ->
|
||||
GenServer.reply(current.from, current.reply)
|
||||
Process.cancel_timer(current.timer)
|
||||
nil
|
||||
{:status, status} -> %{current | status: status}
|
||||
{:reply, reply} -> %{current | reply: reply}
|
||||
_ -> current
|
||||
end
|
||||
end
|
||||
|
||||
defp do_handle({_qcode, parsed}, nil) do
|
||||
handle_gcode(parsed)
|
||||
nil
|
||||
end
|
||||
|
||||
@spec generate_handshake :: binary
|
||||
defp generate_handshake, do: "Q#{:rand.uniform(99)}"
|
||||
|
||||
@spec handle_gcode(any) :: {:status, any} | {:reply, any} | nil
|
||||
defp handle_gcode(:idle), do: {:status, :idle}
|
||||
|
||||
defp handle_gcode(:busy) do
|
||||
Logger.info ">>'s arduino is busy.", type: :busy
|
||||
{:noreply, state}
|
||||
{:status, :busy}
|
||||
end
|
||||
|
||||
defp handle_gcode(:done, state) do
|
||||
Process.sleep(100)
|
||||
# when we get done we need to check the queue for moar commands.
|
||||
# if there is more create a new current map, start a new timer etc.
|
||||
defp handle_gcode(:done), do: {:status, :done}
|
||||
|
||||
# if there is nothing in the queue, nothing to do here.
|
||||
if :queue.is_empty(state.queue) do
|
||||
{:noreply, state}
|
||||
# if there is something in the queue
|
||||
else
|
||||
{{str, handshake, from, millis}, q} = :queue.out(state.queue)
|
||||
ref = Process.send_after(self(), {:timeout, from, handshake}, millis)
|
||||
current = %{reply: nil, handshake: handshake, timeout: ref, from: from}
|
||||
UART.write(state.nerves, str <> " Q#{handshake}")
|
||||
{:noreply, %{state | current: current, queue: q}}
|
||||
end
|
||||
defp handle_gcode(:received), do: {:status, :received}
|
||||
|
||||
defp handle_gcode({:debug_message, message}) do
|
||||
Logger.info ">>'s arduino says: #{message}"
|
||||
nil
|
||||
end
|
||||
|
||||
defp handle_gcode(:received, state) do
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
defp handle_gcode({:debug_message, _message}, state) do
|
||||
# Logger.info ">>'s arduino says: #{message}"
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
defp handle_gcode({:report_pin_value, pin, value}, state)
|
||||
defp handle_gcode({:report_pin_value, pin, value} = reply)
|
||||
when is_integer(pin) and is_integer(value) do
|
||||
BotState.set_pin_value(pin, value)
|
||||
{:noreply, state}
|
||||
{:reply, reply}
|
||||
end
|
||||
|
||||
defp handle_gcode({:report_current_position, x_steps,y_steps,z_steps}, state) do
|
||||
defp handle_gcode({:report_current_position, x_steps, y_steps, z_steps} = reply) do
|
||||
BotState.set_pos(
|
||||
Maths.steps_to_mm(x_steps, spm(:x)),
|
||||
Maths.steps_to_mm(y_steps, spm(:y)),
|
||||
Maths.steps_to_mm(z_steps, spm(:z)))
|
||||
{:noreply, state}
|
||||
{:reply, reply}
|
||||
end
|
||||
|
||||
defp handle_gcode({:report_parameter_value, param, value}, state)
|
||||
defp handle_gcode({:report_parameter_value, param, value} = reply)
|
||||
when is_atom(param) and is_integer(value) do
|
||||
unless value == -1 do
|
||||
BotState.set_param(param, value)
|
||||
end
|
||||
{:noreply, state}
|
||||
{:reply, reply}
|
||||
end
|
||||
|
||||
defp handle_gcode({:reporting_end_stops, x1,x2,y1,y2,z1,z2}, state) do
|
||||
defp handle_gcode({:reporting_end_stops, x1,x2,y1,y2,z1,z2} = reply) do
|
||||
BotState.set_end_stops({x1,x2,y1,y2,z1,z2})
|
||||
{:noreply, state}
|
||||
{:reply, reply}
|
||||
end
|
||||
|
||||
defp handle_gcode({:report_software_version, version}, state) do
|
||||
defp handle_gcode({:report_software_version, version} = reply) do
|
||||
BotState.set_fw_version(version)
|
||||
{:noreply, state}
|
||||
{:reply, reply}
|
||||
end
|
||||
|
||||
defp handle_gcode({:unhandled_gcode, code}, state) do
|
||||
defp handle_gcode(:error), do: {:reply, :error}
|
||||
|
||||
defp handle_gcode(:dont_handle_me), do: nil
|
||||
|
||||
defp handle_gcode({:unhandled_gcode, code}) do
|
||||
Logger.warn ">> got an misc gcode #{code}"
|
||||
{:noreply, state}
|
||||
{:reply, code}
|
||||
end
|
||||
|
||||
defp handle_gcode({:error, :ebadf}, state) do
|
||||
{:ok, new_state} = init({state.nerves, state.tty})
|
||||
{:noreply, new_state}
|
||||
end
|
||||
|
||||
defp handle_gcode(parsed, state) do
|
||||
defp handle_gcode(parsed) do
|
||||
Logger.warn "Unhandled message: #{inspect parsed}"
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def terminate(_, _) do
|
||||
Process.unregister(__MODULE__)
|
||||
{:reply, parsed}
|
||||
end
|
||||
|
||||
def flash_firmware(tty, hex_file, pid) do
|
||||
|
@ -423,9 +260,6 @@ defmodule Farmbot.Serial.Handler do
|
|||
"-Uflash:w:#{hex_file}:i"]
|
||||
|
||||
"avrdude" |> System.cmd(params) |> log(pid)
|
||||
# handler = Process.whereis(__MODULE__)
|
||||
# if handler, do: spawn fn() -> GenServer.stop(handler, :normal) end
|
||||
# Farmbot.Serial.Supervisor.open_ttys(Farmbot.Serial.Supervisor, [tty])
|
||||
end
|
||||
|
||||
defp log({_, 0}, pid) do
|
||||
|
@ -433,9 +267,9 @@ defmodule Farmbot.Serial.Handler do
|
|||
send pid, :done
|
||||
end
|
||||
|
||||
defp log(_, pid) do
|
||||
defp log(stuff, pid) do
|
||||
Logger.error "FAILED TO FLASH FIRMWARE!"
|
||||
send pid, :error
|
||||
send pid, {:error, stuff}
|
||||
end
|
||||
|
||||
@spec spm(atom) :: integer
|
||||
|
@ -444,17 +278,4 @@ defmodule Farmbot.Serial.Handler do
|
|||
|> String.to_atom
|
||||
|> Farmbot.BotState.get_config()
|
||||
end
|
||||
|
||||
@spec maybe_timeout({{pid, reference}, binary}, current) :: current
|
||||
defp maybe_timeout({from, handshake}, current) do
|
||||
# if we actually are working on the thing that this timeout was created
|
||||
# for, reply timeout to it.
|
||||
if (current.from == from) and (current.handshake == handshake) do
|
||||
GenServer.reply(from, {:error, :timeout})
|
||||
nil
|
||||
else
|
||||
# this was probably already finished or somthing. /shrug
|
||||
current
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,6 +17,9 @@ defmodule Farmbot.Serial.Supervisor do
|
|||
|
||||
def init([]) do
|
||||
children = [
|
||||
# Here we start a task for opening ttys. Since they can change depending
|
||||
# on who made the arduino, what drivers are running etc, we cant hard
|
||||
# code it.
|
||||
worker(Task, [__MODULE__, :open_ttys, [__MODULE__]], restart: :transient)
|
||||
]
|
||||
supervise(children, strategy: :one_for_all)
|
||||
|
@ -24,20 +27,32 @@ defmodule Farmbot.Serial.Supervisor do
|
|||
|
||||
if Mix.Project.config[:target] != "host" do
|
||||
|
||||
# if runnin on the device, enumerate any uart devices and open them
|
||||
# individually.
|
||||
@spec open_ttys(atom | pid, [binary]) :: :ok | no_return
|
||||
def open_ttys(supervisor, ttys \\ nil) do
|
||||
blah = ttys || UART.enumerate() |> Map.drop(["ttyS0","ttyAMA0"]) |> Map.keys
|
||||
blah |> try_open(supervisor)
|
||||
case blah do
|
||||
[one_tty] ->
|
||||
thing = {one_tty, [name: Farmbot.Serial.Handler]}
|
||||
try_open([thing], supervisor)
|
||||
ttys when is_list(ttys) ->
|
||||
str
|
||||
|> Enum.map(fn(device) -> {device, []} end)
|
||||
|> try_open(supervisor)
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
@tty Application.get_env(:farmbot, :tty, nil)
|
||||
# If running in the host environment the proper tty is expected to be in
|
||||
# the environment
|
||||
@tty System.get_env("ARDUINO_TTY") || Application.get_env(:farmbot, :tty)
|
||||
@spec open_ttys(atom | pid, [binary]) :: :ok | no_return
|
||||
def open_ttys(supervisor, list \\ nil)
|
||||
def open_ttys(supervisor, _) do
|
||||
if @tty do
|
||||
try_open([@tty], supervisor)
|
||||
thing = {@tty, [name: Farmbot.Serial.Handler]}
|
||||
try_open([thing], supervisor)
|
||||
else
|
||||
Logger.warn ">> EXPORT ARDUINO_TTY to initialize arduino in Host mode"
|
||||
:ok
|
||||
|
@ -46,26 +61,28 @@ defmodule Farmbot.Serial.Supervisor do
|
|||
|
||||
end
|
||||
|
||||
@spec try_open([binary], atom | pid) :: :ok | no_return
|
||||
@spec try_open([{binary, [any]}], atom | pid) :: :ok | no_return
|
||||
defp try_open([], _), do: :ok
|
||||
defp try_open([tty | rest], sup) do
|
||||
defp try_open([{tty, opts} | rest], sup) do
|
||||
{:ok, nerves} = UART.start_link()
|
||||
nerves
|
||||
|> UART.open(tty, speed: @baud, active: false)
|
||||
|> bleep(tty, {sup, nerves})
|
||||
|> bleep({tty, opts}, sup, nerves)
|
||||
|
||||
try_open(rest, {sup, nerves})
|
||||
try_open(rest, sup)
|
||||
end
|
||||
|
||||
@spec bleep(any, binary, {atom | pid, atom | pid})
|
||||
@spec bleep(any, binary, atom | pid, atom | pid)
|
||||
:: {:ok, pid} | false | no_return
|
||||
defp bleep(:ok, tty, {sup, nerves}) do
|
||||
worker_spec = worker(Handler, [nerves, tty], [restart: :permanent])
|
||||
defp bleep(:ok, {tty, opts}, sup, nerves) do
|
||||
worker_spec = worker(Handler, [nerves, tty, opts], [restart: :permanent])
|
||||
UART.close(nerves)
|
||||
Process.sleep(1500)
|
||||
{:ok, _pid} = Supervisor.start_child(sup, worker_spec)
|
||||
end
|
||||
|
||||
defp bleep(_resp, _tty, {_, nerves}) do
|
||||
defp bleep(resp, {tty, _opts}, _, nerves) do
|
||||
Logger.error "Could not open #{tty}: #{inspect resp}"
|
||||
GenServer.stop(nerves, :normal)
|
||||
false
|
||||
end
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
defmodule Farmbot.System.NervesCommon.Cell do
|
||||
require Logger
|
||||
|
||||
@ssdp_fields [
|
||||
location: "http://localhost:3000/myservice.json",
|
||||
server: "MyServerName",
|
||||
"cache-control": "max-age=1800"
|
||||
]
|
||||
|
||||
@cell_ssdp_st "urn:nerves-project-org:service:cell:1"
|
||||
@cell_ssdp_server "Nerves"
|
||||
@cell_ssdp_location "/_cell/"
|
||||
|
||||
def setup do
|
||||
config = Application.get_all_env(:nerves_cell)
|
||||
Logger.info "setting up cell"
|
||||
Nerves.SSDPServer.publish usn(config), @cell_ssdp_st, fields(config)
|
||||
end
|
||||
|
||||
defp fields(config) do
|
||||
[ "Server": @cell_ssdp_server,
|
||||
"Location": @cell_ssdp_location,
|
||||
"X-Id": board_id() || "unknown",
|
||||
"X-Version": config[:version],
|
||||
"X-Firmware-Stream": config[:firmware_stream] ]
|
||||
|> field(:"X-Platform", platform(config))
|
||||
|> field(:"X-Tags", config[:tags])
|
||||
|> field(:"X-Target", config[:target])
|
||||
|> field(:"X-Node", node_name())
|
||||
|> field(:"X-Creation-Date", config[:creation_date], &DateTime.to_iso8601/1)
|
||||
end
|
||||
|
||||
# if value truthy, add field with value optionally transformed by fn
|
||||
@spec field(Keyword.t, atom, term, function) :: Keyword.t
|
||||
defp field(fields, key, val, f \\ &(&1)) do
|
||||
if val do
|
||||
Keyword.put fields, key, f.(val)
|
||||
else
|
||||
fields
|
||||
end
|
||||
end
|
||||
|
||||
defp platform(config), do: config[:platform] || config[:app]
|
||||
defp usn(config), do: "uuid:#{board_id() || "unknown"}::#{platform(config)}"
|
||||
|
||||
# return a board ID, or :unknown if the board ID cannot be generated
|
||||
# REVIEW TODO cache in ets, move to library, handle other board types better
|
||||
@lint false
|
||||
defp board_id do
|
||||
try do
|
||||
{raw_id, 0} = System.cmd "boardid", ["-n", "6"]
|
||||
String.strip(raw_id)
|
||||
rescue
|
||||
_ in ErlangError -> nil
|
||||
end
|
||||
end
|
||||
|
||||
# return a node id as a string if valid, else nil
|
||||
defp node_name do
|
||||
if Node.alive? do
|
||||
Node.self
|
||||
|> Atom.to_string
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
|
@ -21,7 +21,6 @@ defmodule Farmbot.System.NervesCommon.Network do
|
|||
Process.sleep(5000)
|
||||
GenEvent.add_handler(event_manager(),
|
||||
Farmbot.System.NervesCommon.EventManager, [])
|
||||
# Farmbot.System.NervesCommon.Cell.start_link
|
||||
{:ok, %{}}
|
||||
end
|
||||
|
||||
|
|
|
@ -23,7 +23,9 @@ defmodule Farmbot.Serial.HandlerTest do
|
|||
nil ->
|
||||
Process.sleep(10)
|
||||
wait_for_serial_available()
|
||||
_ -> Farmbot.CeleryScript.Command.home(%{axis: "all"}, [])
|
||||
_ ->
|
||||
Farmbot.CeleryScript.Command.home(%{axis: "all"}, [])
|
||||
Process.sleep(10)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue