Huge formatting update (sorry)

pull/1115/head
Rick Carlino 2020-01-17 09:58:53 -06:00
parent a13e6c5832
commit 5722bea421
102 changed files with 2281 additions and 681 deletions

View File

@ -77,7 +77,8 @@ defmodule FarmbotCeleryScript.AST do
end
@spec new(atom, map, [map]) :: t()
def new(kind, args, body, comment \\ nil, meta \\ nil) when is_map(args) and is_list(body) do
def new(kind, args, body, comment \\ nil, meta \\ nil)
when is_map(args) and is_list(body) do
%AST{
kind: String.to_atom(to_string(kind)),
args: args,

View File

@ -19,12 +19,16 @@ defmodule FarmbotCeleryScript.AST.Factory do
def read_pin(%AST{} = ast, pin_number, pin_mode) do
ast
|> add_body_node(new(:read_pin, %{pin_number: pin_number, pin_mode: pin_mode}))
|> add_body_node(
new(:read_pin, %{pin_number: pin_number, pin_mode: pin_mode})
)
end
def set_pin_io_mode(%AST{} = ast, pin_number, pin_io_mode) do
ast
|> add_body_node(new(:set_pin_io_mode, %{pin_number: pin_number, pin_io_mode: pin_io_mode}))
|> add_body_node(
new(:set_pin_io_mode, %{pin_number: pin_number, pin_io_mode: pin_io_mode})
)
end
def dump_info(%AST{} = ast) do

View File

@ -155,14 +155,20 @@ defmodule FarmbotCeleryScript.Compiler do
end
end
def send_message(%{args: %{message: msg, message_type: type}, body: channels}, env) do
def send_message(
%{args: %{message: msg, message_type: type}, body: channels},
env
) do
# body gets turned into a list of atoms.
# Example:
# [{kind: "channel", args: {channel_name: "email"}}]
# is turned into:
# [:email]
channels =
Enum.map(channels, fn %{kind: :channel, args: %{channel_name: channel_name}} ->
Enum.map(channels, fn %{
kind: :channel,
args: %{channel_name: channel_name}
} ->
String.to_atom(channel_name)
end)
@ -218,7 +224,9 @@ defmodule FarmbotCeleryScript.Compiler do
def flash_firmware(%{args: %{package: package_name}}, env) do
quote location: :keep do
FarmbotCeleryScript.SysCalls.flash_firmware(unquote(compile_ast(package_name, env)))
FarmbotCeleryScript.SysCalls.flash_firmware(
unquote(compile_ast(package_name, env))
)
end
end
@ -242,7 +250,9 @@ defmodule FarmbotCeleryScript.Compiler do
def factory_reset(%{args: %{package: package}}, env) do
quote location: :keep do
FarmbotCeleryScript.SysCalls.factory_reset(unquote(compile_ast(package, env)))
FarmbotCeleryScript.SysCalls.factory_reset(
unquote(compile_ast(package, env))
)
end
end

View File

@ -80,7 +80,8 @@ defmodule FarmbotCeleryScript.Compiler.Utils do
var =
quote location: :keep do
{unquote(next_scope_var_name), unquote(Compiler.compile_ast(data_value, env))}
{unquote(next_scope_var_name),
unquote(Compiler.compile_ast(data_value, env))}
end
compile_params_to_function_args(rest, env, [var | acc])
@ -117,12 +118,19 @@ defmodule FarmbotCeleryScript.Compiler.Utils do
parent = Keyword.get(params, :parent, %{x: 100, y: 200, z: 300})
"""
def compile_param_declaration(%{args: %{label: var_name, default_value: default}}, env) do
def compile_param_declaration(
%{args: %{label: var_name, default_value: default}},
env
) do
var_name = IdentifierSanitizer.to_variable(var_name)
quote location: :keep do
unquote({var_name, env, __MODULE__}) =
Keyword.get(params, unquote(var_name), unquote(Compiler.compile_ast(default, env)))
Keyword.get(
params,
unquote(var_name),
unquote(Compiler.compile_ast(default, env))
)
end
end
@ -152,11 +160,15 @@ defmodule FarmbotCeleryScript.Compiler.Utils do
]
}
"""
def compile_param_application(%{args: %{label: var_name, data_value: value}}, env) do
def compile_param_application(
%{args: %{label: var_name, data_value: value}},
env
) do
var_name = IdentifierSanitizer.to_variable(var_name)
quote location: :keep do
unquote({var_name, [], __MODULE__}) = unquote(Compiler.compile_ast(value, env))
unquote({var_name, [], __MODULE__}) =
unquote(Compiler.compile_ast(value, env))
end
end
@ -168,13 +180,16 @@ defmodule FarmbotCeleryScript.Compiler.Utils do
end)
end
def add_sequence_init_and_complete_logs(steps, sequence_name) when is_binary(sequence_name) do
def add_sequence_init_and_complete_logs(steps, sequence_name)
when is_binary(sequence_name) do
# This looks really weird because of the logs before and
# after the compiled steps
List.flatten([
quote do
fn ->
FarmbotCeleryScript.SysCalls.sequence_init_log("Starting #{unquote(sequence_name)}")
FarmbotCeleryScript.SysCalls.sequence_init_log(
"Starting #{unquote(sequence_name)}"
)
end
end,
steps,
@ -201,7 +216,9 @@ defmodule FarmbotCeleryScript.Compiler.Utils do
fn _ ->
[
fn ->
FarmbotCeleryScript.SysCalls.sequence_init_log("Starting #{unquote(sequence_name)}")
FarmbotCeleryScript.SysCalls.sequence_init_log(
"Starting #{unquote(sequence_name)}"
)
end
]
end

View File

@ -4,7 +4,11 @@ defmodule FarmbotCeleryScript.Compiler.Assertion do
@doc "`Assert` is a internal node useful for self testing."
def assertion(
%{
args: %{lua: expression, assertion_type: assertion_type, _then: then_ast},
args: %{
lua: expression,
assertion_type: assertion_type,
_then: then_ast
},
comment: comment
},
env
@ -80,7 +84,10 @@ defmodule FarmbotCeleryScript.Compiler.Assertion do
then_block ++
[
FarmbotCeleryScript.Compiler.compile(%AST{kind: :abort, args: %{}}, [])
FarmbotCeleryScript.Compiler.compile(
%AST{kind: :abort, args: %{}},
[]
)
]
end
end

View File

@ -1,12 +1,17 @@
defmodule FarmbotCeleryScript.Compiler.AxisControl do
alias FarmbotCeleryScript.Compiler
# Compiles move_absolute
def move_absolute(%{args: %{location: location, offset: offset, speed: speed}}, env) do
def move_absolute(
%{args: %{location: location, offset: offset, speed: speed}},
env
) do
quote location: :keep do
# Extract the location arg
with %{x: locx, y: locy, z: locz} = unquote(Compiler.compile_ast(location, env)),
with %{x: locx, y: locy, z: locz} =
unquote(Compiler.compile_ast(location, env)),
# Extract the offset arg
%{x: offx, y: offy, z: offz} = unquote(Compiler.compile_ast(offset, env)) do
%{x: offx, y: offy, z: offz} =
unquote(Compiler.compile_ast(offset, env)) do
# Subtract the location from offset.
# Note: list syntax here for readability.
[x, y, z] = [
@ -18,7 +23,11 @@ defmodule FarmbotCeleryScript.Compiler.AxisControl do
x_str = FarmbotCeleryScript.FormatUtil.format_float(x)
y_str = FarmbotCeleryScript.FormatUtil.format_float(y)
z_str = FarmbotCeleryScript.FormatUtil.format_float(z)
FarmbotCeleryScript.SysCalls.log("Moving to (#{x_str}, #{y_str}, #{z_str})", true)
FarmbotCeleryScript.SysCalls.log(
"Moving to (#{x_str}, #{y_str}, #{z_str})",
true
)
FarmbotCeleryScript.SysCalls.move_absolute(
x,
@ -36,9 +45,12 @@ defmodule FarmbotCeleryScript.Compiler.AxisControl do
with locx when is_number(locx) <- unquote(Compiler.compile_ast(x, env)),
locy when is_number(locy) <- unquote(Compiler.compile_ast(y, env)),
locz when is_number(locz) <- unquote(Compiler.compile_ast(z, env)),
curx when is_number(curx) <- FarmbotCeleryScript.SysCalls.get_current_x(),
cury when is_number(cury) <- FarmbotCeleryScript.SysCalls.get_current_y(),
curz when is_number(curz) <- FarmbotCeleryScript.SysCalls.get_current_z() do
curx when is_number(curx) <-
FarmbotCeleryScript.SysCalls.get_current_x(),
cury when is_number(cury) <-
FarmbotCeleryScript.SysCalls.get_current_y(),
curz when is_number(curz) <-
FarmbotCeleryScript.SysCalls.get_current_z() do
# Combine them
x = locx + curx
y = locy + cury
@ -77,8 +89,13 @@ defmodule FarmbotCeleryScript.Compiler.AxisControl do
# compiles find_home
def find_home(%{args: %{axis: axis}}, env) do
quote location: :keep do
with axis when axis in ["x", "y", "z"] <- unquote(Compiler.compile_ast(axis, env)) do
FarmbotCeleryScript.SysCalls.log("Finding home on the #{String.upcase(axis)} axis", true)
with axis when axis in ["x", "y", "z"] <-
unquote(Compiler.compile_ast(axis, env)) do
FarmbotCeleryScript.SysCalls.log(
"Finding home on the #{String.upcase(axis)} axis",
true
)
FarmbotCeleryScript.SysCalls.find_home(axis)
else
{:error, reason} ->
@ -92,7 +109,8 @@ defmodule FarmbotCeleryScript.Compiler.AxisControl do
quote location: :keep do
FarmbotCeleryScript.SysCalls.log("Going to home on all axes", true)
with speed when is_number(speed) <- unquote(Compiler.compile_ast(speed, env)),
with speed when is_number(speed) <-
unquote(Compiler.compile_ast(speed, env)),
:ok <- FarmbotCeleryScript.SysCalls.home("z", speed),
:ok <- FarmbotCeleryScript.SysCalls.home("y", speed) do
FarmbotCeleryScript.SysCalls.home("x", speed)
@ -103,9 +121,15 @@ defmodule FarmbotCeleryScript.Compiler.AxisControl do
# compiles home
def home(%{args: %{axis: axis, speed: speed}}, env) do
quote location: :keep do
with axis when axis in ["x", "y", "z"] <- unquote(Compiler.compile_ast(axis, env)),
speed when is_number(speed) <- unquote(Compiler.compile_ast(speed, env)) do
FarmbotCeleryScript.SysCalls.log("Going to home on the #{String.upcase(axis)} axis", true)
with axis when axis in ["x", "y", "z"] <-
unquote(Compiler.compile_ast(axis, env)),
speed when is_number(speed) <-
unquote(Compiler.compile_ast(speed, env)) do
FarmbotCeleryScript.SysCalls.log(
"Going to home on the #{String.upcase(axis)} axis",
true
)
FarmbotCeleryScript.SysCalls.home(axis, speed)
else
{:error, reason} ->
@ -129,8 +153,13 @@ defmodule FarmbotCeleryScript.Compiler.AxisControl do
# compiles zero
def zero(%{args: %{axis: axis}}, env) do
quote location: :keep do
with axis when axis in ["x", "y", "z"] <- unquote(Compiler.compile_ast(axis, env)) do
FarmbotCeleryScript.SysCalls.log("Zeroing the #{String.upcase(axis)} axis", true)
with axis when axis in ["x", "y", "z"] <-
unquote(Compiler.compile_ast(axis, env)) do
FarmbotCeleryScript.SysCalls.log(
"Zeroing the #{String.upcase(axis)} axis",
true
)
FarmbotCeleryScript.SysCalls.zero(axis)
else
{:error, reason} ->
@ -157,8 +186,13 @@ defmodule FarmbotCeleryScript.Compiler.AxisControl do
# compiles calibrate
def calibrate(%{args: %{axis: axis}}, env) do
quote location: :keep do
with axis when axis in ["x", "y", "z"] <- unquote(Compiler.compile_ast(axis, env)) do
FarmbotCeleryScript.SysCalls.log("Calibrating the #{String.upcase(axis)} axis", true)
with axis when axis in ["x", "y", "z"] <-
unquote(Compiler.compile_ast(axis, env)) do
FarmbotCeleryScript.SysCalls.log(
"Calibrating the #{String.upcase(axis)} axis",
true
)
FarmbotCeleryScript.SysCalls.calibrate(axis)
else
{:error, reason} ->

View File

@ -42,7 +42,15 @@ defmodule FarmbotCeleryScript.Compiler.DataControl do
end
def resource_update(
%{args: %{resource_type: kind, resource_id: id, label: label, value: value}, body: body},
%{
args: %{
resource_type: kind,
resource_id: id,
label: label,
value: value
},
body: body
},
env
) do
initial = %{label => value}

View File

@ -13,7 +13,8 @@ defmodule FarmbotCeleryScript.Compiler.Execute do
loop_parameter_appl_ast =
Enum.find_value(parameter_applications, fn
# check if this parameter_application is a iterable type
%{kind: :parameter_application, args: %{data_value: %{kind: kind}}} = iterable
%{kind: :parameter_application, args: %{data_value: %{kind: kind}}} =
iterable
when kind in @iterables ->
iterable
@ -38,7 +39,10 @@ defmodule FarmbotCeleryScript.Compiler.Execute do
%FarmbotCeleryScript.AST{kind: :sequence} = celery_ast ->
celery_args =
celery_ast.args
|> Map.put(:sequence_name, celery_ast.args[:name] || celery_ast.meta[:sequence_name])
|> Map.put(
:sequence_name,
celery_ast.args[:name] || celery_ast.meta[:sequence_name]
)
|> Map.put(:locals, %{
celery_ast.args.locals
| body: celery_ast.args.locals.body ++ unquote(param_appls)
@ -53,13 +57,20 @@ defmodule FarmbotCeleryScript.Compiler.Execute do
end
end
def compile_execute(%{args: %{sequence_id: id}, body: parameter_applications}, env) do
def compile_execute(
%{args: %{sequence_id: id}, body: parameter_applications},
env
) do
quote location: :keep do
# We have to lookup the sequence by it's id.
case FarmbotCeleryScript.SysCalls.get_sequence(unquote(id)) do
%FarmbotCeleryScript.AST{} = ast ->
# compile the ast
env = unquote(compile_params_to_function_args(parameter_applications, env))
env =
unquote(
compile_params_to_function_args(parameter_applications, env)
)
FarmbotCeleryScript.Compiler.compile(ast, env)
error ->

View File

@ -30,7 +30,10 @@ defmodule FarmbotCeleryScript.Compiler.Farmware do
kvs =
Enum.map(pairs, fn %{kind: :pair, args: %{label: key, value: value}} ->
quote location: :keep do
FarmbotCeleryScript.SysCalls.set_user_env(unquote(key), unquote(value))
FarmbotCeleryScript.SysCalls.set_user_env(
unquote(key),
unquote(value)
)
end
end)

View File

@ -4,7 +4,15 @@ defmodule FarmbotCeleryScript.Compiler.If do
# Compiles an if statement.
def unquote(:_if)(
%{args: %{_then: then_ast, _else: else_ast, lhs: lhs_ast, op: op, rhs: rhs}},
%{
args: %{
_then: then_ast,
_else: else_ast,
lhs: lhs_ast,
op: op,
rhs: rhs
}
},
env
) do
rhs = Compiler.compile_ast(rhs, env)
@ -30,7 +38,10 @@ defmodule FarmbotCeleryScript.Compiler.If do
"pin" <> pin ->
quote [location: :keep],
do: FarmbotCeleryScript.SysCalls.read_cached_pin(unquote(String.to_integer(pin)))
do:
FarmbotCeleryScript.SysCalls.read_cached_pin(
unquote(String.to_integer(pin))
)
# Named pin has two intents here
# in this case we want to read the named pin.

View File

@ -1,7 +1,10 @@
defmodule FarmbotCeleryScript.Compiler.PinControl do
alias FarmbotCeleryScript.Compiler
# compiles write_pin
def write_pin(%{args: %{pin_number: num, pin_mode: mode, pin_value: value}}, env) do
def write_pin(
%{args: %{pin_number: num, pin_mode: mode, pin_value: value}},
env
) do
quote location: :keep do
pin = unquote(Compiler.compile_ast(num, env))
mode = unquote(Compiler.compile_ast(mode, env))
@ -23,7 +26,10 @@ defmodule FarmbotCeleryScript.Compiler.PinControl do
end
# compiles set_servo_angle
def set_servo_angle(%{args: %{pin_number: pin_number, pin_value: pin_value}}, env) do
def set_servo_angle(
%{args: %{pin_number: pin_number, pin_value: pin_value}},
env
) do
quote location: :keep do
pin = unquote(Compiler.compile_ast(pin_number, env))
angle = unquote(Compiler.compile_ast(pin_value, env))
@ -33,7 +39,10 @@ defmodule FarmbotCeleryScript.Compiler.PinControl do
end
# compiles set_pin_io_mode
def set_pin_io_mode(%{args: %{pin_number: pin_number, pin_io_mode: mode}}, env) do
def set_pin_io_mode(
%{args: %{pin_number: pin_number, pin_io_mode: mode}},
env
) do
quote location: :keep do
pin = unquote(Compiler.compile_ast(pin_number, env))
mode = unquote(Compiler.compile_ast(mode, env))

View File

@ -10,7 +10,8 @@ defmodule FarmbotCeleryScript.Compiler.Sequence do
loop_parameter_appl_ast =
Enum.find_value(params_or_iterables, fn
# check if this parameter_application is a iterable type
%{kind: :parameter_application, args: %{data_value: %{kind: kind}}} = iterable
%{kind: :parameter_application, args: %{data_value: %{kind: kind}}} =
iterable
when kind in @iterables ->
iterable
@ -25,22 +26,38 @@ defmodule FarmbotCeleryScript.Compiler.Sequence do
def compile_sequence_iterable(
loop_parameter_appl_ast,
%{args: %{locals: %{body: params} = locals} = sequence_args, meta: sequence_meta} =
sequence_ast,
%{
args: %{locals: %{body: params} = locals} = sequence_args,
meta: sequence_meta
} = sequence_ast,
env
) do
sequence_name = sequence_meta[:sequence_name] || sequence_args[:sequence_name]
sequence_name =
sequence_meta[:sequence_name] || sequence_args[:sequence_name]
# remove the iterable from the parameter applications,
# since it will be injected after this.
_params =
Enum.reduce(params, [], fn
# Remove point_group from parameter appls
%{kind: :parameter_application, args: %{data_value: %{kind: :point_group}}}, acc -> acc
%{
kind: :parameter_application,
args: %{data_value: %{kind: :point_group}}
},
acc ->
acc
# Remove every_point from parameter appls
%{kind: :parameter_application, args: %{data_value: %{kind: :every_point}}}, acc -> acc
%{
kind: :parameter_application,
args: %{data_value: %{kind: :every_point}}
},
acc ->
acc
# Everything else gets added back
ast, acc -> acc ++ [ast]
ast, acc ->
acc ++ [ast]
end)
# will be a point_group or every_point node
@ -59,7 +76,8 @@ defmodule FarmbotCeleryScript.Compiler.Sequence do
total = Enum.count(point_group.point_ids)
# Map over all the points returned by `get_point_group/1`
{body, _} =
Enum.reduce(point_group.point_ids, {[], 1}, fn point_id, {acc, index} ->
Enum.reduce(point_group.point_ids, {[], 1}, fn point_id,
{acc, index} ->
# check if it's an every_point node first, if not fall back go generic pointer
pointer_type = group_ast.args[:every_point_type] || "GenericPointer"
@ -83,7 +101,10 @@ defmodule FarmbotCeleryScript.Compiler.Sequence do
%{name: name, x: x, y: y, z: z} ->
pos = FarmbotCeleryScript.FormatUtil.format_coord(x, y, z)
"unnamed iterable sequence [#{index} / #{total}] - #{name} #{pos}"
"unnamed iterable sequence [#{index} / #{total}] - #{name} #{
pos
}"
_ ->
"unknown iterable [#{index} / #{total}]"
@ -113,7 +134,10 @@ defmodule FarmbotCeleryScript.Compiler.Sequence do
end
end
def compile_sequence(%{args: %{locals: %{body: params}} = args, body: block, meta: meta}, env) do
def compile_sequence(
%{args: %{locals: %{body: params}} = args, body: block, meta: meta},
env
) do
# Sort the args.body into two arrays.
# The `params` side gets turned into
# a keyword list. These `params` are passed in from a previous sequence.

View File

@ -2,7 +2,10 @@ defmodule FarmbotCeleryScript.Compiler.VariableDeclaration do
alias FarmbotCeleryScript.{Compiler, Compiler.IdentifierSanitizer}
@doc "Compiles a variable asignment"
def variable_declaration(%{args: %{label: var_name, data_value: data_value_ast}}, env) do
def variable_declaration(
%{args: %{label: var_name, data_value: data_value_ast}},
env
) do
# Compiles the `data_value`
# and assigns the result to a variable named `label`
# Example:
@ -26,7 +29,8 @@ defmodule FarmbotCeleryScript.Compiler.VariableDeclaration do
var_name = IdentifierSanitizer.to_variable(var_name)
quote location: :keep do
unquote({var_name, [], nil}) = unquote(Compiler.compile_ast(data_value_ast, env))
unquote({var_name, [], nil}) =
unquote(Compiler.compile_ast(data_value_ast, env))
end
end
end

View File

@ -18,14 +18,18 @@ defmodule FarmbotCeleryScript.Corpus do
@corpus_tag tag
# Load and decode each arg in the json into an Arg struct
@args Enum.map(args, fn %{"name" => name, "allowed_values" => allowed_values} = a ->
@args Enum.map(args, fn %{"name" => name, "allowed_values" => allowed_values} =
a ->
%Arg{name: name, allowed_values: allowed_values, doc: a["doc"]}
end)
# Load and decode each node in the json into a Node struct.
# This also expands the `allowed_args` into their respective Arg relationship.
@nodes Enum.map(@nodes, fn %{"name" => name, "allowed_args" => aa, "allowed_body_types" => abt} =
n ->
@nodes Enum.map(@nodes, fn %{
"name" => name,
"allowed_args" => aa,
"allowed_body_types" => abt
} = n ->
allowed_args =
Enum.map(aa, fn arg_name ->
Enum.find(@args, fn
@ -34,7 +38,12 @@ defmodule FarmbotCeleryScript.Corpus do
end) || Mix.raise("Unknown CeleryScript argument: #{arg_name}")
end)
%Node{name: name, allowed_args: allowed_args, allowed_body_types: abt, doc: n["doc"]}
%Node{
name: name,
allowed_args: allowed_args,
allowed_body_types: abt,
doc: n["doc"]
}
end)
# Struct should never be created manually.

View File

@ -67,7 +67,12 @@ defmodule FarmbotCeleryScript.Scheduler do
Calls are executed in a first in first out buffer, with things being added
by `execute/2` taking priority.
"""
@spec schedule(GenServer.server(), AST.t() | [Compiler.compiled()], DateTime.t(), map()) ::
@spec schedule(
GenServer.server(),
AST.t() | [Compiler.compiled()],
DateTime.t(),
map()
) ::
{:ok, reference()}
def schedule(scheduler_pid \\ __MODULE__, celery_script, at, data)
@ -171,7 +176,9 @@ defmodule FarmbotCeleryScript.Scheduler do
# now is late, but less than the grace period late
diff_ms when diff_ms >= 0 when diff_ms <= @grace_period_ms ->
Logger.info("Next execution is ready for execution: #{Timex.from_now(at)}")
Logger.info(
"Next execution is ready for execution: #{Timex.from_now(at)}"
)
state
|> execute_next()
@ -179,8 +186,15 @@ defmodule FarmbotCeleryScript.Scheduler do
end
end
def handle_info({:step_complete, {scheduled_at, executed_at, pid}, result}, state) do
send(pid, {FarmbotCeleryScript, {:scheduled_execution, scheduled_at, executed_at, result}})
def handle_info(
{:step_complete, {scheduled_at, executed_at, pid}, result},
state
) do
send(
pid,
{FarmbotCeleryScript,
{:scheduled_execution, scheduled_at, executed_at, result}}
)
state
|> pop_next()
@ -194,7 +208,9 @@ defmodule FarmbotCeleryScript.Scheduler do
scheduler_pid = self()
scheduled_pid =
spawn(fn -> StepRunner.step(scheduler_pid, {at, DateTime.utc_now(), pid}, compiled) end)
spawn(fn ->
StepRunner.step(scheduler_pid, {at, DateTime.utc_now(), pid}, compiled)
end)
%{state | scheduled_pid: scheduled_pid}
end
@ -291,7 +307,8 @@ defmodule FarmbotCeleryScript.Scheduler do
%{state | monitors: monitors}
end
@spec add(state(), compiled_ast(), DateTime.t(), data :: map(), pid()) :: state()
@spec add(state(), compiled_ast(), DateTime.t(), data :: map(), pid()) ::
state()
defp add(state, compiled, at, data, pid) do
%{state | compiled: [{compiled, at, data, pid} | state.compiled]}
|> index_next()

View File

@ -21,7 +21,11 @@ defmodule FarmbotCeleryScript.SysCalls do
@type resource_id :: integer()
@callback calibrate(axis) :: ok_or_error
@callback change_ownership(email :: String.t(), secret :: binary(), server :: String.t()) ::
@callback change_ownership(
email :: String.t(),
secret :: binary(),
server :: String.t()
) ::
ok_or_error
@callback check_update() :: ok_or_error
@callback coordinate(x :: number, y :: number, z :: number) ::
@ -48,26 +52,39 @@ defmodule FarmbotCeleryScript.SysCalls do
%{x: number(), y: number(), z: number()} | error()
@callback home(axis, speed :: number()) :: ok_or_error
@callback install_first_party_farmware() :: ok_or_error
@callback move_absolute(x :: number(), y :: number(), z :: number(), speed :: number()) ::
@callback move_absolute(
x :: number(),
y :: number(),
z :: number(),
speed :: number()
) ::
ok_or_error
# ?
@callback named_pin(named_pin_type :: String.t(), resource_id) :: map() | integer | error()
@callback named_pin(named_pin_type :: String.t(), resource_id) ::
map() | integer | error()
@callback nothing() :: any()
@callback point(point_type :: String.t(), resource_id) :: number() | error()
@callback power_off() :: ok_or_error
@callback read_pin(pin_num :: number(), pin_mode :: number()) :: number | error()
@callback read_pin(pin_num :: number(), pin_mode :: number()) ::
number | error()
@callback read_cached_pin(pin_num :: number()) :: number | error()
@callback toggle_pin(pin_num :: number()) :: ok_or_error
@callback read_status() :: ok_or_error
@callback reboot() :: ok_or_error
@callback resource_update(String.t(), resource_id, map()) :: ok_or_error
@callback send_message(type :: String.t(), message :: String.t(), [atom]) :: ok_or_error
@callback send_message(type :: String.t(), message :: String.t(), [atom]) ::
ok_or_error
@callback set_servo_angle(pin :: number(), value :: number()) :: ok_or_error
@callback set_pin_io_mode(pin :: number(), mode :: number()) :: ok_or_error
@callback set_user_env(env_name :: String.t(), env_value :: String.t()) :: ok_or_error
@callback set_user_env(env_name :: String.t(), env_value :: String.t()) ::
ok_or_error
@callback sync() :: ok_or_error
@callback wait(millis :: number()) :: ok_or_error
@callback write_pin(pin_num :: number(), pin_mode :: number(), pin_value :: number) ::
@callback write_pin(
pin_num :: number(),
pin_mode :: number(),
pin_value :: number
) ::
ok_or_error
@callback zero(axis) :: ok_or_error
@ -77,7 +94,9 @@ defmodule FarmbotCeleryScript.SysCalls do
@callback eval_assertion(comment :: String.t(), expression :: String.t()) ::
true | false | error()
@callback get_point_group(String.t() | resource_id) :: %{required(:point_ids) => [resource_id]}
@callback get_point_group(String.t() | resource_id) :: %{
required(:point_ids) => [resource_id]
}
def get_point_group(sys_calls \\ @sys_calls, point_group_id) do
point_group_or_error(sys_calls, :get_point_group, [point_group_id])
@ -90,14 +109,18 @@ defmodule FarmbotCeleryScript.SysCalls do
def format_lhs(_sys_calls, "z"), do: "current z position"
def format_lhs(_sys_calls, "pin" <> num), do: "Pin #{num} value"
def format_lhs(sys_calls, %{kind: :named_pin, args: %{pin_type: type, pin_id: pin_id}}) do
def format_lhs(sys_calls, %{
kind: :named_pin,
args: %{pin_type: type, pin_id: pin_id}
}) do
case named_pin(sys_calls, type, pin_id) do
%{label: label} -> label
{:error, _reason} -> "unknown left hand side"
end
end
def eval_assertion(sys_calls \\ @sys_calls, comment, expression) when is_binary(expression) do
def eval_assertion(sys_calls \\ @sys_calls, comment, expression)
when is_binary(expression) do
case sys_calls.eval_assertion(comment, expression) do
true ->
true
@ -121,11 +144,13 @@ defmodule FarmbotCeleryScript.SysCalls do
apply(@sys_calls, :log, [message, force?])
end
def sequence_init_log(sys_calls \\ @sys_calls, message) when is_binary(message) do
def sequence_init_log(sys_calls \\ @sys_calls, message)
when is_binary(message) do
apply(sys_calls, :sequence_init_log, [message])
end
def sequence_complete_log(sys_calls \\ @sys_calls, message) when is_binary(message) do
def sequence_complete_log(sys_calls \\ @sys_calls, message)
when is_binary(message) do
apply(sys_calls, :sequence_complete_log, [message])
end
@ -160,11 +185,13 @@ defmodule FarmbotCeleryScript.SysCalls do
ok_or_error(sys_calls, :emergency_unlock, [])
end
def execute_script(sys_calls \\ @sys_calls, package, %{} = env) when is_binary(package) do
def execute_script(sys_calls \\ @sys_calls, package, %{} = env)
when is_binary(package) do
ok_or_error(sys_calls, :execute_script, [package, env])
end
def update_farmware(sys_calls \\ @sys_calls, package) when is_binary(package) do
def update_farmware(sys_calls \\ @sys_calls, package)
when is_binary(package) do
ok_or_error(sys_calls, :update_farmware, [package])
end
@ -336,8 +363,14 @@ defmodule FarmbotCeleryScript.SysCalls do
defp coord_or_error(sys_calls, fun, args) do
case apply(sys_calls, fun, args) do
%{x: x, y: y, z: z} = coord when is_number(x) when is_number(y) when is_number(z) -> coord
error -> or_error(sys_calls, fun, args, error)
%{x: x, y: y, z: z} = coord
when is_number(x)
when is_number(y)
when is_number(z) ->
coord
error ->
or_error(sys_calls, fun, args, error)
end
end
@ -348,7 +381,8 @@ defmodule FarmbotCeleryScript.SysCalls do
end
end
defp or_error(_sys_calls, _fun, _args, {:error, reason}) when is_binary(reason) do
defp or_error(_sys_calls, _fun, _args, {:error, reason})
when is_binary(reason) do
{:error, reason}
end

View File

@ -77,25 +77,29 @@ defmodule FarmbotCeleryScript.SysCalls.Stubs do
def get_sequence(resource_id), do: error(:get_sequence, [resource_id])
@impl true
def get_toolslot_for_tool(resource_id), do: error(:get_toolslot_for_tool, [resource_id])
def get_toolslot_for_tool(resource_id),
do: error(:get_toolslot_for_tool, [resource_id])
@impl true
def home(axis, speed), do: error(:home, [axis, speed])
@impl true
def install_first_party_farmware(), do: error(:install_first_party_farmware, [])
def install_first_party_farmware(),
do: error(:install_first_party_farmware, [])
@impl true
def move_absolute(x, y, z, speed), do: error(:move_absolute, [x, y, z, speed])
@impl true
def named_pin(named_pin_type, resource_id), do: error(:named_pin, [named_pin_type, resource_id])
def named_pin(named_pin_type, resource_id),
do: error(:named_pin, [named_pin_type, resource_id])
@impl true
def nothing(), do: error(:nothing, [])
@impl true
def point(point_type, resource_id), do: error(:point, [point_type, resource_id])
def point(point_type, resource_id),
do: error(:point, [point_type, resource_id])
@impl true
def get_point_group(id_or_type), do: error(:get_point_group, [id_or_type])
@ -123,7 +127,8 @@ defmodule FarmbotCeleryScript.SysCalls.Stubs do
do: error(:resource_update, [kind, resource_id, data])
@impl true
def send_message(type, message, channels), do: error(:send_message, [type, message, channels])
def send_message(type, message, channels),
do: error(:send_message, [type, message, channels])
@impl true
def set_servo_angle(pin, value), do: error(:set_servo_angle, [pin, value])
@ -132,7 +137,8 @@ defmodule FarmbotCeleryScript.SysCalls.Stubs do
def set_pin_io_mode(pin, mode), do: error(:set_pin_io_mode, [pin, mode])
@impl true
def set_user_env(env_name, env_value), do: error(:set_user_env, [env_name, env_value])
def set_user_env(env_name, env_value),
do: error(:set_user_env, [env_name, env_value])
@impl true
def sync(), do: error(:sync, [])
@ -148,7 +154,8 @@ defmodule FarmbotCeleryScript.SysCalls.Stubs do
def zero(axis), do: error(:zero, [axis])
@impl true
def eval_assertion(comment, expression), do: error(:eval_assertion, [comment, expression])
def eval_assertion(comment, expression),
do: error(:eval_assertion, [comment, expression])
defp error(fun, _args) do
msg = """

View File

@ -1,7 +1,12 @@
defmodule FarmbotCeleryScript.MixProject do
use Mix.Project
@version Path.join([__DIR__, "..", "VERSION"]) |> File.read!() |> String.trim()
@elixir_version Path.join([__DIR__, "..", "ELIXIR_VERSION"]) |> File.read!() |> String.trim()
@version Path.join([__DIR__, "..", "VERSION"])
|> File.read!()
|> String.trim()
@elixir_version Path.join([__DIR__, "..", "ELIXIR_VERSION"])
|> File.read!()
|> String.trim()
def project do
[
@ -39,7 +44,11 @@ defmodule FarmbotCeleryScript.MixProject do
end
def elixirc_paths(:test),
do: ["lib", Path.expand("./test/support"), Path.expand("../test/support/celery_script")]
do: [
"lib",
Path.expand("./test/support"),
Path.expand("../test/support/celery_script")
]
def elixirc_paths(_), do: ["lib"]
@ -57,7 +66,8 @@ defmodule FarmbotCeleryScript.MixProject do
{:jason, "~> 1.1"},
{:timex, "~> 3.4"},
{:excoveralls, "~> 0.10", only: [:test], targets: [:host]},
{:dialyxir, "~> 1.0.0-rc.3", only: [:dev], targets: [:host], runtime: false},
{:dialyxir, "~> 1.0.0-rc.3",
only: [:dev], targets: [:host], runtime: false},
{:ex_doc, "~> 0.21.2", only: [:dev], targets: [:host], runtime: false}
]
end

View File

@ -34,7 +34,10 @@ defmodule FarmbotCeleryScript.CompilerTest do
# The compiler expects the `env` argument to be already sanatized.
# When supplying the env for this test, we need to make sure the
# `provided_by_caller` variable name is sanatized
sanatized_env = [{IdentifierSanitizer.to_variable("provided_by_caller"), 900}]
sanatized_env = [
{IdentifierSanitizer.to_variable("provided_by_caller"), 900}
]
[body_item] = Compiler.compile(sequence, sanatized_env)
assert body_item.() == 900
@ -48,7 +51,9 @@ defmodule FarmbotCeleryScript.CompilerTest do
}
]
compiled_celery_env = Compiler.Utils.compile_params_to_function_args(celery_env, [])
compiled_celery_env =
Compiler.Utils.compile_params_to_function_args(celery_env, [])
[body_item] = Compiler.compile(sequence, compiled_celery_env)
assert body_item.() == 600
end
@ -79,7 +84,10 @@ defmodule FarmbotCeleryScript.CompilerTest do
identifier_ast = AST.Factory.new("identifier", label: label)
parameter_application_ast =
AST.Factory.new("parameter_application", label: label, data_value: value_ast)
AST.Factory.new("parameter_application",
label: label,
data_value: value_ast
)
celery_ast = %AST{
kind: :sequence,

View File

@ -5,7 +5,10 @@ defmodule FarmbotCeleryScript.SchedulerTest do
setup do
{:ok, shim} = TestSysCalls.checkout()
{:ok, sch} = Scheduler.start_link([registry_name: :"#{:random.uniform()}"], [])
{:ok, sch} =
Scheduler.start_link([registry_name: :"#{:random.uniform()}"], [])
[shim: shim, sch: sch]
end
@ -26,6 +29,7 @@ defmodule FarmbotCeleryScript.SchedulerTest do
scheduled_time = DateTime.utc_now() |> DateTime.add(100, :millisecond)
{:ok, _} = Scheduler.schedule(sch, ast, scheduled_time, %{})
# Hack to force the scheduler to checkup instead of waiting the normal 15 seconds
send(sch, :checkup)
assert_receive {:read_pin, [9, 0]}, 1000

View File

@ -10,12 +10,16 @@ defmodule FarmbotCeleryScript.SysCallsTest do
test "point", %{shim: shim} do
:ok = shim_fun_ok(shim, %{x: 100, y: 200, z: 300})
assert %{x: 100, y: 200, z: 300} = SysCalls.point(TestSysCalls, "Peripheral", 1)
assert %{x: 100, y: 200, z: 300} =
SysCalls.point(TestSysCalls, "Peripheral", 1)
assert_receive {:point, ["Peripheral", 1]}
:ok = shim_fun_error(shim, "point error")
assert {:error, "point error"} == SysCalls.point(TestSysCalls, "Peripheral", 1)
assert {:error, "point error"} ==
SysCalls.point(TestSysCalls, "Peripheral", 1)
end
test "move_absolute", %{shim: shim} do
@ -25,7 +29,8 @@ defmodule FarmbotCeleryScript.SysCallsTest do
:ok = shim_fun_error(shim, "move failed!")
assert {:error, "move failed!"} == SysCalls.move_absolute(TestSysCalls, 1, 2, 3, 4)
assert {:error, "move failed!"} ==
SysCalls.move_absolute(TestSysCalls, 1, 2, 3, 4)
end
test "get current positions", %{shim: shim} do
@ -48,8 +53,12 @@ defmodule FarmbotCeleryScript.SysCallsTest do
test "write_pin", %{shim: shim} do
:ok = shim_fun_ok(shim)
assert :ok = SysCalls.write_pin(TestSysCalls, 1, 0, 1)
assert :ok = SysCalls.write_pin(TestSysCalls, %{type: "boxled", id: 4}, 0, 1)
assert :ok = SysCalls.write_pin(TestSysCalls, %{type: "boxled", id: 3}, 1, 123)
assert :ok =
SysCalls.write_pin(TestSysCalls, %{type: "boxled", id: 4}, 0, 1)
assert :ok =
SysCalls.write_pin(TestSysCalls, %{type: "boxled", id: 3}, 1, 123)
assert_receive {:write_pin, [1, 0, 1]}
assert_receive {:write_pin, [%{type: "boxled", id: 4}, 0, 1]}
@ -57,7 +66,8 @@ defmodule FarmbotCeleryScript.SysCallsTest do
:ok = shim_fun_error(shim, "firmware error")
assert {:error, "firmware error"} == SysCalls.write_pin(TestSysCalls, 1, 0, 1)
assert {:error, "firmware error"} ==
SysCalls.write_pin(TestSysCalls, 1, 0, 1)
end
test "read_pin", %{shim: shim} do
@ -86,10 +96,14 @@ defmodule FarmbotCeleryScript.SysCallsTest do
# BoxLed is on the GPIO
:ok = shim_fun_ok(shim, %{type: "BoxLed", id: 3})
assert %{type: "BoxLed", id: 3} == SysCalls.named_pin(TestSysCalls, "BoxLed", 3)
assert %{type: "BoxLed", id: 3} ==
SysCalls.named_pin(TestSysCalls, "BoxLed", 3)
:ok = shim_fun_ok(shim, %{type: "BoxLed", id: 4})
assert %{type: "BoxLed", id: 4} == SysCalls.named_pin(TestSysCalls, "BoxLed", 4)
assert %{type: "BoxLed", id: 4} ==
SysCalls.named_pin(TestSysCalls, "BoxLed", 4)
assert_receive {:named_pin, ["Peripheral", 5]}
assert_receive {:named_pin, ["Sensor", 1999]}
@ -104,13 +118,20 @@ defmodule FarmbotCeleryScript.SysCallsTest do
test "send_message", %{shim: shim} do
:ok = shim_fun_ok(shim)
assert :ok = SysCalls.send_message(TestSysCalls, "success", "hello world", ["email"])
assert :ok =
SysCalls.send_message(TestSysCalls, "success", "hello world", [
"email"
])
assert_receive {:send_message, ["success", "hello world", ["email"]]}
:ok = shim_fun_error(shim, "email machine broke")
assert {:error, "email machine broke"} ==
SysCalls.send_message(TestSysCalls, "error", "goodbye world", ["email"])
SysCalls.send_message(TestSysCalls, "error", "goodbye world", [
"email"
])
end
test "find_home", %{shim: shim} do
@ -130,7 +151,8 @@ defmodule FarmbotCeleryScript.SysCallsTest do
:ok = shim_fun_error(shim, "not installed")
assert {:error, "not installed"} == SysCalls.execute_script(TestSysCalls, "take-photo", %{})
assert {:error, "not installed"} ==
SysCalls.execute_script(TestSysCalls, "take-photo", %{})
end
test "set_servo_angle errors", %{shim: shim} do
@ -141,7 +163,9 @@ defmodule FarmbotCeleryScript.SysCallsTest do
arg1 = [40, -5]
:ok = shim_fun_error(shim, "boom")
assert {:error, "boom"} == SysCalls.set_servo_angle(TestSysCalls, "set_servo_angle", arg1)
assert {:error, "boom"} ==
SysCalls.set_servo_angle(TestSysCalls, "set_servo_angle", arg1)
end
test "get_sequence", %{shim: shim} do
@ -156,7 +180,8 @@ defmodule FarmbotCeleryScript.SysCallsTest do
:ok = shim_fun_error(shim, "sequence not found")
assert {:error, "sequence not found"} == SysCalls.get_sequence(TestSysCalls, 123)
assert {:error, "sequence not found"} ==
SysCalls.get_sequence(TestSysCalls, 123)
end
def shim_fun_ok(shim, val \\ :ok) do

View File

@ -83,8 +83,11 @@ defmodule FarmbotCeleryScriptTest do
:read_pin, _ -> {:error, "failed to read pin!"}
end)
assert {:error, "failed to read pin!"} = FarmbotCeleryScript.execute(execute_ast, execute_ast)
assert_receive {:step_complete, ^execute_ast, {:error, "failed to read pin!"}}
assert {:error, "failed to read pin!"} =
FarmbotCeleryScript.execute(execute_ast, execute_ast)
assert_receive {:step_complete, ^execute_ast,
{:error, "failed to read pin!"}}
end
test "regular exceptions still occur" do
@ -103,7 +106,9 @@ defmodule FarmbotCeleryScriptTest do
:read_pin, _ -> raise("big oops")
end)
assert {:error, "big oops"} == FarmbotCeleryScript.execute(execute_ast, execute_ast)
assert {:error, "big oops"} ==
FarmbotCeleryScript.execute(execute_ast, execute_ast)
assert_receive {:step_complete, ^execute_ast, {:error, "big oops"}}
end
end

View File

@ -1,28 +1,34 @@
use Mix.Config
config :farmbot_core, FarmbotCore.AssetWorker.FarmbotCore.Asset.FarmEvent, checkup_time_ms: 10_000
config :farmbot_core, FarmbotCore.AssetWorker.FarmbotCore.Asset.FarmEvent,
checkup_time_ms: 10_000
config :farmbot_core, FarmbotCore.AssetWorker.FarmbotCore.Asset.RegimenInstance,
checkup_time_ms: 10_000
config :farmbot_core, FarmbotCore.AssetWorker.FarmbotCore.Asset.FarmwareInstallation,
error_retry_time_ms: 30_000,
install_dir: "/tmp/farmware"
config :farmbot_core,
FarmbotCore.AssetWorker.FarmbotCore.Asset.FarmwareInstallation,
error_retry_time_ms: 30_000,
install_dir: "/tmp/farmware"
config :farmbot_core, FarmbotCore.FarmwareRuntime, runtime_dir: "/tmp/farmware_runtime"
config :farmbot_core, FarmbotCore.FarmwareRuntime,
runtime_dir: "/tmp/farmware_runtime"
config :farmbot_core, FarmbotCore.AssetWorker.FarmbotCore.Asset.PinBinding,
gpio_handler: FarmbotCore.PinBindingWorker.StubGPIOHandler,
error_retry_time_ms: 30_000
config :farmbot_core, Elixir.FarmbotCore.AssetWorker.FarmbotCore.Asset.PublicKey,
ssh_handler: FarmbotCore.PublicKeyHandler.StubSSHHandler
config :farmbot_core,
Elixir.FarmbotCore.AssetWorker.FarmbotCore.Asset.PublicKey,
ssh_handler: FarmbotCore.PublicKeyHandler.StubSSHHandler
config :farmbot_core, FarmbotCore.AssetMonitor, checkup_time_ms: 30_000
config :farmbot_core, FarmbotCore.Leds, gpio_handler: FarmbotCore.Leds.StubHandler
config :farmbot_core, FarmbotCore.Leds,
gpio_handler: FarmbotCore.Leds.StubHandler
config :farmbot_core, FarmbotCore.JSON, json_parser: FarmbotCore.JSON.JasonParser
config :farmbot_core, FarmbotCore.JSON,
json_parser: FarmbotCore.JSON.JasonParser
config :farmbot_core, FarmbotCore.BotState.FileSystem,
root_dir: "/tmp/farmbot",
@ -36,7 +42,10 @@ config :farmbot_core, FarmbotCore.EctoMigrator,
default_ntp_server_1: "0.pool.ntp.org",
default_ntp_server_2: "1.pool.ntp.org",
default_currently_on_beta:
String.contains?(to_string(:os.cmd('git rev-parse --abbrev-ref HEAD')), "beta")
String.contains?(
to_string(:os.cmd('git rev-parse --abbrev-ref HEAD')),
"beta"
)
config :farmbot_core, FarmbotCore.FirmwareTTYDetector, expected_names: []

View File

@ -2,7 +2,11 @@ use Mix.Config
config :ecto, json_library: FarmbotCore.JSON
config :farmbot_core,
ecto_repos: [FarmbotCore.Config.Repo, FarmbotCore.Logger.Repo, FarmbotCore.Asset.Repo]
ecto_repos: [
FarmbotCore.Config.Repo,
FarmbotCore.Logger.Repo,
FarmbotCore.Asset.Repo
]
config :farmbot_core, FarmbotCore.Config.Repo,
adapter: Sqlite.Ecto2,

View File

@ -4,7 +4,8 @@ use Mix.Config
# To ensure other timers have time to timeout
config :farmbot_core, FarmbotCore.AssetMonitor, checkup_time_ms: 500
config :farmbot_core, FarmbotCore.AssetWorker.FarmbotCore.Asset.FarmEvent, checkup_time_ms: 1000
config :farmbot_core, FarmbotCore.AssetWorker.FarmbotCore.Asset.FarmEvent,
checkup_time_ms: 1000
config :farmbot_core, FarmbotCore.AssetWorker.FarmbotCore.Asset.RegimenInstance,
checkup_time_ms: 1000

View File

@ -1,9 +1,15 @@
defmodule FarmbotCore.MixProject do
use Mix.Project
@target System.get_env("MIX_TARGET") || "host"
@version Path.join([__DIR__, "..", "VERSION"]) |> File.read!() |> String.trim()
@branch System.cmd("git", ~w"rev-parse --abbrev-ref HEAD") |> elem(0) |> String.trim()
@elixir_version Path.join([__DIR__, "..", "ELIXIR_VERSION"]) |> File.read!() |> String.trim()
@version Path.join([__DIR__, "..", "VERSION"])
|> File.read!()
|> String.trim()
@branch System.cmd("git", ~w"rev-parse --abbrev-ref HEAD")
|> elem(0)
|> String.trim()
@elixir_version Path.join([__DIR__, "..", "ELIXIR_VERSION"])
|> File.read!()
|> String.trim()
defp commit do
System.cmd("git", ~w"rev-parse --verify HEAD") |> elem(0) |> String.trim()
@ -59,7 +65,8 @@ defmodule FarmbotCore.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:farmbot_celery_script, path: "../farmbot_celery_script", env: Mix.env()},
{:farmbot_celery_script,
path: "../farmbot_celery_script", env: Mix.env()},
{:farmbot_firmware, path: "../farmbot_firmware", env: Mix.env()},
{:farmbot_telemetry, path: "../farmbot_telemetry", env: Mix.env()},
{:elixir_make, "~> 0.6", runtime: false},
@ -68,7 +75,8 @@ defmodule FarmbotCore.MixProject do
{:jason, "~> 1.1"},
{:muontrap, "~> 0.5"},
{:excoveralls, "~> 0.10", only: [:test], targets: [:host]},
{:dialyxir, "~> 1.0.0-rc.3", only: [:dev], targets: [:host], runtime: false},
{:dialyxir, "~> 1.0.0-rc.3",
only: [:dev], targets: [:host], runtime: false},
{:ex_doc, "~> 0.21.2", only: [:dev], targets: [:host], runtime: false}
]
end

View File

@ -8,13 +8,29 @@ defmodule FarmbotCore.Asset.Repo.Migrations.CreateRegimenInstancesTable do
add(:epoch, :utc_datetime)
add(:next, :utc_datetime)
add(:next_sequence_id, :id)
add(:regimen_id, references("regimens", type: :binary_id, column: :local_id))
add(:farm_event_id, references("farm_events", type: :binary_id, column: :local_id))
add(
:regimen_id,
references("regimens", type: :binary_id, column: :local_id)
)
add(
:farm_event_id,
references("farm_events", type: :binary_id, column: :local_id)
)
add(:monitor, :boolean, default: true)
timestamps(inserted_at: :created_at, type: :utc_datetime)
end
create(unique_index("persistent_regimens", [:local_id, :regimen_id, :farm_event_id]))
create(
unique_index("persistent_regimens", [
:local_id,
:regimen_id,
:farm_event_id
])
)
create(unique_index("persistent_regimens", :started_at))
create(unique_index("persistent_regimens", :epoch))
end

View File

@ -9,6 +9,8 @@ defmodule FarmbotCore.Asset.Repo.Migrations.ResyncFirmwareConfig do
end
# will resync the firmware params
execute("UPDATE firmware_configs SET updated_at = \"1970-11-07 16:52:31.618000\"")
execute(
"UPDATE firmware_configs SET updated_at = \"1970-11-07 16:52:31.618000\""
)
end
end

View File

@ -5,7 +5,9 @@ defmodule FarmbotCore.Config.Repo.Migrations.SeedGroups do
import Ecto.Query, only: [from: 2]
@group_names ["authorization", "hardware_params", "settings"]
@default_server Application.get_env(:farmbot_core, Farmbot.EctoMigrator)[:default_server] ||
@default_server Application.get_env(:farmbot_core, Farmbot.EctoMigrator)[
:default_server
] ||
"https://my.farm.bot"
def change do
@ -23,14 +25,18 @@ defmodule FarmbotCore.Config.Repo.Migrations.SeedGroups do
defp populate_config_values do
for name <- @group_names do
[group_id] = from(g in Group, where: g.group_name == ^name, select: g.id) |> Repo.all()
[group_id] =
from(g in Group, where: g.group_name == ^name, select: g.id)
|> Repo.all()
populate_config_values(name, group_id)
end
end
defp populate_config_values("authorization", group_id) do
create_value(StringValue, @default_server) |> create_config(group_id, "server")
create_value(StringValue, @default_server)
|> create_config(group_id, "server")
create_value(StringValue, nil) |> create_config(group_id, "email")
create_value(StringValue, nil) |> create_config(group_id, "password")
create_value(StringValue, nil) |> create_config(group_id, "token")
@ -38,17 +44,32 @@ defmodule FarmbotCore.Config.Repo.Migrations.SeedGroups do
defp populate_config_values("settings", group_id) do
create_value(BoolValue, true) |> create_config(group_id, "os_auto_update")
create_value(BoolValue, true) |> create_config(group_id, "ignore_external_logs")
create_value(BoolValue, true)
|> create_config(group_id, "ignore_external_logs")
create_value(BoolValue, true) |> create_config(group_id, "first_boot")
create_value(BoolValue, true) |> create_config(group_id, "first_sync")
create_value(StringValue, "A") |> create_config(group_id, "current_repo")
create_value(BoolValue, true) |> create_config(group_id, "first_party_farmware")
create_value(BoolValue, true)
|> create_config(group_id, "first_party_farmware")
create_value(BoolValue, false) |> create_config(group_id, "auto_sync")
create_value(StringValue, nil) |> create_config(group_id, "firmware_hardware")
create_value(StringValue, nil)
|> create_config(group_id, "firmware_hardware")
create_value(StringValue, nil) |> create_config(group_id, "timezone")
create_value(StringValue, "{}") |> create_config(group_id, "user_env")
fpf_url = Application.get_env(:farmbot_core, :farmware)[:first_part_farmware_manifest_url]
create_value(StringValue, fpf_url) |> create_config(group_id, "first_party_farmware_url")
fpf_url =
Application.get_env(:farmbot_core, :farmware)[
:first_part_farmware_manifest_url
]
create_value(StringValue, fpf_url)
|> create_config(group_id, "first_party_farmware_url")
end
defp populate_config_values("hardware_params", group_id) do
@ -56,90 +77,250 @@ defmodule FarmbotCore.Config.Repo.Migrations.SeedGroups do
create_value(FloatValue, nil) |> create_config(group_id, "param_test")
create_value(FloatValue, nil) |> create_config(group_id, "param_config_ok")
create_value(FloatValue, nil) |> create_config(group_id, "param_use_eeprom")
create_value(FloatValue, nil) |> create_config(group_id, "param_e_stop_on_mov_err")
create_value(FloatValue, nil) |> create_config(group_id, "param_mov_nr_retry")
create_value(FloatValue, nil) |> create_config(group_id, "movement_timeout_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_timeout_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_timeout_z")
create_value(FloatValue, nil) |> create_config(group_id, "movement_keep_active_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_keep_active_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_keep_active_z")
create_value(FloatValue, nil) |> create_config(group_id, "movement_home_at_boot_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_home_at_boot_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_home_at_boot_z")
create_value(FloatValue, nil) |> create_config(group_id, "movement_invert_endpoints_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_invert_endpoints_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_invert_endpoints_z")
create_value(FloatValue, nil) |> create_config(group_id, "movement_enable_endpoints_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_enable_endpoints_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_enable_endpoints_z")
create_value(FloatValue, nil) |> create_config(group_id, "movement_invert_motor_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_invert_motor_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_invert_motor_z")
create_value(FloatValue, nil) |> create_config(group_id, "movement_secondary_motor_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_secondary_motor_invert_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_steps_acc_dec_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_steps_acc_dec_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_steps_acc_dec_z")
create_value(FloatValue, nil) |> create_config(group_id, "movement_stop_at_home_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_stop_at_home_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_stop_at_home_z")
create_value(FloatValue, nil) |> create_config(group_id, "movement_home_up_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_home_up_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_home_up_z")
create_value(FloatValue, nil) |> create_config(group_id, "movement_step_per_mm_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_step_per_mm_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_step_per_mm_z")
create_value(FloatValue, nil) |> create_config(group_id, "movement_min_spd_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_min_spd_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_min_spd_z")
create_value(FloatValue, nil) |> create_config(group_id, "movement_home_spd_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_home_spd_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_home_spd_z")
create_value(FloatValue, nil) |> create_config(group_id, "movement_max_spd_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_max_spd_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_max_spd_z")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_enabled_x")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_enabled_y")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_enabled_z")
create_value(FloatValue, nil)
|> create_config(group_id, "param_e_stop_on_mov_err")
create_value(FloatValue, nil)
|> create_config(group_id, "param_mov_nr_retry")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_timeout_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_timeout_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_timeout_z")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_keep_active_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_keep_active_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_keep_active_z")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_home_at_boot_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_home_at_boot_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_home_at_boot_z")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_invert_endpoints_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_invert_endpoints_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_invert_endpoints_z")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_enable_endpoints_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_enable_endpoints_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_enable_endpoints_z")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_invert_motor_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_invert_motor_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_invert_motor_z")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_secondary_motor_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_secondary_motor_invert_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_steps_acc_dec_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_steps_acc_dec_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_steps_acc_dec_z")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_stop_at_home_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_stop_at_home_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_stop_at_home_z")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_home_up_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_home_up_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_home_up_z")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_step_per_mm_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_step_per_mm_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_step_per_mm_z")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_min_spd_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_min_spd_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_min_spd_z")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_home_spd_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_home_spd_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_home_spd_z")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_max_spd_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_max_spd_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_max_spd_z")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_enabled_x")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_enabled_y")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_enabled_z")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_type_x")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_type_y")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_type_z")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_missed_steps_max_x")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_missed_steps_max_y")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_missed_steps_max_z")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_scaling_x")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_scaling_y")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_scaling_z")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_missed_steps_decay_x")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_missed_steps_decay_y")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_missed_steps_decay_z")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_use_for_pos_x")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_use_for_pos_y")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_use_for_pos_z")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_missed_steps_max_x")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_missed_steps_max_y")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_missed_steps_max_z")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_scaling_x")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_scaling_y")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_scaling_z")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_missed_steps_decay_x")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_missed_steps_decay_y")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_missed_steps_decay_z")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_use_for_pos_x")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_use_for_pos_y")
create_value(FloatValue, nil)
|> create_config(group_id, "encoder_use_for_pos_z")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_invert_x")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_invert_y")
create_value(FloatValue, nil) |> create_config(group_id, "encoder_invert_z")
create_value(FloatValue, nil) |> create_config(group_id, "movement_axis_nr_steps_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_axis_nr_steps_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_axis_nr_steps_z")
create_value(FloatValue, nil) |> create_config(group_id, "movement_stop_at_max_x")
create_value(FloatValue, nil) |> create_config(group_id, "movement_stop_at_max_y")
create_value(FloatValue, nil) |> create_config(group_id, "movement_stop_at_max_z")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_1_pin_nr")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_1_time_out")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_1_active_state")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_2_pin_nr")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_2_time_out")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_2_active_state")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_3_pin_nr")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_3_time_out")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_3_active_state")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_4_pin_nr")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_4_time_out")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_4_active_state")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_5_pin_nr")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_5_time_out")
create_value(FloatValue, nil) |> create_config(group_id, "pin_guard_5_active_state")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_axis_nr_steps_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_axis_nr_steps_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_axis_nr_steps_z")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_stop_at_max_x")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_stop_at_max_y")
create_value(FloatValue, nil)
|> create_config(group_id, "movement_stop_at_max_z")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_1_pin_nr")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_1_time_out")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_1_active_state")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_2_pin_nr")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_2_time_out")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_2_active_state")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_3_pin_nr")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_3_time_out")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_3_active_state")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_4_pin_nr")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_4_time_out")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_4_active_state")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_5_pin_nr")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_5_time_out")
create_value(FloatValue, nil)
|> create_config(group_id, "pin_guard_5_active_state")
end
end

View File

@ -3,12 +3,22 @@ defmodule FarmbotCore.Config.Repo.Migrations.AddFirmwareIoLog do
import FarmbotCore.Config.MigrationHelpers
@default_firmware_io_logs Application.get_env(:farmbot_core, Farmbot.EctoMigrator)[
:default_firmware_io_logs
] || false
@default_firmware_io_logs Application.get_env(
:farmbot_core,
Farmbot.EctoMigrator
)[:default_firmware_io_logs] || false
def change do
create_settings_config("firmware_input_log", :bool, @default_firmware_io_logs)
create_settings_config("firmware_output_log", :bool, @default_firmware_io_logs)
create_settings_config(
"firmware_input_log",
:bool,
@default_firmware_io_logs
)
create_settings_config(
"firmware_output_log",
:bool,
@default_firmware_io_logs
)
end
end

View File

@ -2,14 +2,20 @@ defmodule FarmbotCore.Config.Repo.Migrations.AddBetaState do
use Ecto.Migration
import FarmbotCore.Config.MigrationHelpers
@default_currently_on_beta Application.get_env(:farmbot_core, FarmbotCore.EctoMigrator)[
:default_currently_on_beta
]
@default_currently_on_beta Application.get_env(
:farmbot_core,
FarmbotCore.EctoMigrator
)[:default_currently_on_beta]
if is_nil(@default_currently_on_beta),
do: Mix.raise("Missing application env config: `:default_currently_on_beta`")
do:
Mix.raise("Missing application env config: `:default_currently_on_beta`")
def change do
create_settings_config("currently_on_beta", :bool, @default_currently_on_beta)
create_settings_config(
"currently_on_beta",
:bool,
@default_currently_on_beta
)
end
end

View File

@ -2,12 +2,14 @@ defmodule FarmbotCore.Config.Repo.Migrations.AddNtpAndDnsConfigs do
use Ecto.Migration
import FarmbotCore.Config.MigrationHelpers
@default_ntp_server_1 Application.get_env(:farmbot_core, FarmbotCore.EctoMigrator)[
:default_ntp_server_1
]
@default_ntp_server_2 Application.get_env(:farmbot_core, FarmbotCore.EctoMigrator)[
:default_ntp_server_2
]
@default_ntp_server_1 Application.get_env(
:farmbot_core,
FarmbotCore.EctoMigrator
)[:default_ntp_server_1]
@default_ntp_server_2 Application.get_env(
:farmbot_core,
FarmbotCore.EctoMigrator
)[:default_ntp_server_2]
@default_dns_name Application.get_env(:farmbot_core, FarmbotCore.EctoMigrator)[
:default_dns_name
]
@ -30,8 +32,18 @@ defmodule FarmbotCore.Config.Repo.Migrations.AddNtpAndDnsConfigs do
do: raise(@config_error)
def change do
create_settings_config("default_ntp_server_1", :string, @default_ntp_server_1)
create_settings_config("default_ntp_server_2", :string, @default_ntp_server_2)
create_settings_config(
"default_ntp_server_1",
:string,
@default_ntp_server_1
)
create_settings_config(
"default_ntp_server_2",
:string,
@default_ntp_server_2
)
create_settings_config("default_dns_name", :string, @default_dns_name)
end
end

View File

@ -5,7 +5,9 @@ defmodule FarmbotCore.Config.Repo.Migrations.MigrateSecret do
def change do
group =
FarmbotCore.Config.Repo.one!(
from(g in FarmbotCore.Config.Group, where: g.group_name == "authorization")
from(g in FarmbotCore.Config.Group,
where: g.group_name == "authorization"
)
)
pass_ref =
@ -17,17 +19,23 @@ defmodule FarmbotCore.Config.Repo.Migrations.MigrateSecret do
sec_ref =
FarmbotCore.Config.Repo.one!(
from(c in FarmbotCore.Config.Config, where: c.key == "secret" and c.group_id == ^group.id)
from(c in FarmbotCore.Config.Config,
where: c.key == "secret" and c.group_id == ^group.id
)
)
pass =
FarmbotCore.Config.Repo.one!(
from(s in FarmbotCore.Config.StringValue, where: s.id == ^pass_ref.string_value_id)
from(s in FarmbotCore.Config.StringValue,
where: s.id == ^pass_ref.string_value_id
)
)
sec =
FarmbotCore.Config.Repo.one!(
from(s in FarmbotCore.Config.StringValue, where: s.id == ^sec_ref.string_value_id)
from(s in FarmbotCore.Config.StringValue,
where: s.id == ^sec_ref.string_value_id
)
)
if pass.value do

View File

@ -2,10 +2,20 @@ defmodule FarmbotCore.Config.Repo.Migrations.SetFirmwareFlashTrue do
use Ecto.Migration
def up do
FarmbotCore.Config.update_config_value(:bool, "settings", "firmware_needs_flash", true)
FarmbotCore.Config.update_config_value(
:bool,
"settings",
"firmware_needs_flash",
true
)
end
def down do
FarmbotCore.Config.update_config_value(:bool, "settings", "firmware_needs_flash", true)
FarmbotCore.Config.update_config_value(
:bool,
"settings",
"firmware_needs_flash",
true
)
end
end

View File

@ -31,8 +31,16 @@ defmodule FarmbotCore.Asset.CommandTest do
test "update farm_event" do
id = id()
regimen_id = id()
:ok = Command.update("FarmEvent", id, %{id: id, executable_type: "Sequence", monitor: false})
:ok = Command.update("Regimen", regimen_id, %{id: regimen_id, monitor: false})
:ok =
Command.update("FarmEvent", id, %{
id: id,
executable_type: "Sequence",
monitor: false
})
:ok =
Command.update("Regimen", regimen_id, %{id: regimen_id, monitor: false})
:ok =
Command.update("FarmEvent", id, %{
@ -49,7 +57,12 @@ defmodule FarmbotCore.Asset.CommandTest do
id = id()
:ok =
Command.update("FarmEvent", id, %{id: id, executable_id: id(), name: "abc", monitor: false})
Command.update("FarmEvent", id, %{
id: id,
executable_id: id(),
name: "abc",
monitor: false
})
:ok = Command.update("FarmEvent", id, nil)
refute Asset.get_farm_event(id)

View File

@ -7,7 +7,10 @@ defmodule FarmbotCore.AssetTest do
describe "regimen instances" do
test "creates a regimen instance" do
seq = sequence()
reg = regimen(%{regimen_items: [%{time_offset: 100, sequence_id: seq.id}]})
reg =
regimen(%{regimen_items: [%{time_offset: 100, sequence_id: seq.id}]})
event = regimen_event(reg)
assert %RegimenInstance{} = Asset.new_regimen_instance!(event)
end

View File

@ -4,13 +4,20 @@ defmodule FarmbotCore.BotState.FileSystemTest do
describe "serializer" do
test "arrays not aloud" do
assert_raise RuntimeError, "Arrays can not be serialized to filesystem nodes", fn ->
FileSystem.serialize_state(%{key: [:value, :nope]}, "/")
end
assert_raise RuntimeError,
"Arrays can not be serialized to filesystem nodes",
fn ->
FileSystem.serialize_state(%{key: [:value, :nope]}, "/")
end
end
test "serializes a map to the filesystem" do
root_dir = Path.join([System.tmp_dir!(), Ecto.UUID.generate(), "-farmbot-map-serializer"])
root_dir =
Path.join([
System.tmp_dir!(),
Ecto.UUID.generate(),
"-farmbot-map-serializer"
])
fixture = %{
a_string: "hello",
@ -54,11 +61,21 @@ defmodule FarmbotCore.BotState.FileSystemTest do
describe "server" do
test "serializes state to fs" do
root_dir = Path.join([System.tmp_dir!(), Ecto.UUID.generate(), "-farmbot-bot-state"])
root_dir =
Path.join([
System.tmp_dir!(),
Ecto.UUID.generate(),
"-farmbot-bot-state"
])
{:ok, bot_state_pid} = BotState.start_link([], [])
{:ok, _pid} =
FileSystem.start_link(root_dir: root_dir, bot_state: bot_state_pid, sleep_time: 0)
FileSystem.start_link(
root_dir: root_dir,
bot_state: bot_state_pid,
sleep_time: 0
)
_ = BotState.subscribe(bot_state_pid)
:ok = BotState.set_pin_value(bot_state_pid, 1, 1)

View File

@ -47,13 +47,17 @@ defmodule FarmbotCore.BotStateNGTest do
assert orig.informational_settings.update_available == false
mut1 =
BotStateNG.changeset(orig, %{informational_settings: %{update_available: true}})
BotStateNG.changeset(orig, %{
informational_settings: %{update_available: true}
})
|> Ecto.Changeset.apply_changes()
assert mut1.informational_settings.update_available == true
mut2 =
BotStateNG.changeset(orig, %{informational_settings: %{update_available: false}})
BotStateNG.changeset(orig, %{
informational_settings: %{update_available: false}
})
|> Ecto.Changeset.apply_changes()
assert mut2.informational_settings.update_available == false
@ -83,7 +87,9 @@ defmodule FarmbotCore.BotStateNGTest do
orig = BotStateNG.new()
mut =
BotStateNG.changeset(orig, %{informational_settings: %{memory_usage: 512}})
BotStateNG.changeset(orig, %{
informational_settings: %{memory_usage: 512}
})
|> Ecto.Changeset.apply_changes()
assert mut.informational_settings.memory_usage == 512
@ -93,7 +99,9 @@ defmodule FarmbotCore.BotStateNGTest do
orig = BotStateNG.new()
mut =
BotStateNG.changeset(orig, %{informational_settings: %{scheduler_usage: 10}})
BotStateNG.changeset(orig, %{
informational_settings: %{scheduler_usage: 10}
})
|> Ecto.Changeset.apply_changes()
assert mut.informational_settings.scheduler_usage == 10

View File

@ -257,7 +257,9 @@ defmodule FarmbotFirmware do
nil
tape_path ->
{:ok, vcr_fd} = File.open(tape_path, [:binary, :append, :exclusive, :write])
{:ok, vcr_fd} =
File.open(tape_path, [:binary, :append, :exclusive, :write])
vcr_fd
end
@ -293,14 +295,24 @@ defmodule FarmbotFirmware do
end
def handle_info(:timeout, %{status: :transport_boot, reset_pid: nil} = state) do
case GenServer.start_link(state.reset, state.transport_args, name: state.reset) do
case GenServer.start_link(state.reset, state.transport_args,
name: state.reset
) do
{:ok, pid} ->
Logger.debug("Firmware reset #{state.reset} started. #{inspect(state.transport_args)}")
Logger.debug(
"Firmware reset #{state.reset} started. #{
inspect(state.transport_args)
}"
)
{:noreply, %{state | reset_pid: pid}}
# TODO(Rick): I have no idea what's going on here.
{:error, {:already_started, pid}} ->
Logger.debug("Firmware reset complete. #{inspect(state.transport_args)}")
Logger.debug(
"Firmware reset complete. #{inspect(state.transport_args)}"
)
{:noreply, %{state | reset_pid: pid}}
error ->
@ -320,7 +332,9 @@ defmodule FarmbotFirmware do
ref = Process.monitor(pid)
Logger.debug(
"Firmware Transport #{state.transport} started. #{inspect(state.transport_args)}"
"Firmware Transport #{state.transport} started. #{
inspect(state.transport_args)
}"
)
state = goto(%{state | transport_pid: pid, transport_ref: ref}, :boot)
@ -336,11 +350,22 @@ defmodule FarmbotFirmware do
# @spec handle_info(:timeout, state) :: {:noreply, state}
def handle_info(
:timeout,
%{command_queue: [{pid, {tag, {:command_emergency_lock, []} = code}} | _]} = state
%{
command_queue: [
{pid, {tag, {:command_emergency_lock, []} = code}} | _
]
} = state
) do
case GenServer.call(state.transport_pid, {tag, code}) do
:ok ->
new_state = %{state | tag: tag, current: code, command_queue: [], caller_pid: pid}
new_state = %{
state
| tag: tag,
current: code,
command_queue: [],
caller_pid: pid
}
_ = side_effects(new_state, :handle_output_gcode, [{state.tag, code}])
_ = vcr_write(state, :out, {state.tag, code})
@ -367,18 +392,33 @@ defmodule FarmbotFirmware do
end
def handle_info(:timeout, %{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, []}})
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, []}})
# Logger.debug "Got checkup message when current command still executing"
{:noreply, state}
end
def handle_info(:timeout, %{command_queue: [{pid, {tag, code}} | rest]} = state) do
for {pid, _code} <- state.command_queue, do: send(pid, {state.tag, {:report_busy, []}})
def handle_info(
:timeout,
%{command_queue: [{pid, {tag, code}} | rest]} = state
) do
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_busy, []}})
case GenServer.call(state.transport_pid, {tag, code}) do
:ok ->
new_state = %{state | tag: tag, current: code, command_queue: rest, caller_pid: pid}
new_state = %{
state
| tag: tag,
current: code,
command_queue: rest,
caller_pid: pid
}
_ = 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, []}})
@ -401,7 +441,8 @@ defmodule FarmbotFirmware do
# Closing the transport will purge the buffer of queued commands in both
# the `configuration_queue` and in the `command_queue`.
def handle_call(:close_transport, _from, %{status: s} = state) when s != :transport_boot do
def handle_call(:close_transport, _from, %{status: s} = state)
when s != :transport_boot do
true = Process.demonitor(state.transport_ref)
:ok = GenServer.stop(state.transport_pid, :normal)
@ -442,12 +483,17 @@ defmodule FarmbotFirmware do
{:reply, :ok, next_state}
end
def handle_call({:open_transport, _module, _args}, _from, %{status: s} = state) do
def handle_call(
{:open_transport, _module, _args},
_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
with {:ok, vcr_fd} <-
File.open(tape_path, [:binary, :append, :exclusive, :write]) do
{:reply, :ok, %{state | vcr_fd: vcr_fd}}
else
error ->
@ -460,23 +506,37 @@ defmodule FarmbotFirmware do
end
@doc false
@spec handle_command(GCODE.t(), GenServer.from(), state()) :: {:reply, term(), state()}
@spec handle_command(GCODE.t(), GenServer.from(), state()) ::
{:reply, term(), state()}
# EmergencyLock should be ran immediately
def handle_command({tag, {:command_emergency_lock, []}} = code, {pid, _ref}, state) do
if state.caller_pid, do: send(state.caller_pid, {state.tag, {:report_emergency_lock, []}})
def handle_command(
{tag, {:command_emergency_lock, []}} = code,
{pid, _ref},
state
) do
if state.caller_pid,
do: send(state.caller_pid, {state.tag, {:report_emergency_lock, []}})
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_emergency_lock, []}})
send(self(), :timeout)
{:reply, {:ok, tag}, %{state | command_queue: [{pid, code}], configuration_queue: []}}
{:reply, {:ok, tag},
%{state | command_queue: [{pid, code}], configuration_queue: []}}
end
# EmergencyUnLock should be ran immediately
def handle_command({tag, {:command_emergency_unlock, []}} = code, {pid, _ref}, state) do
def handle_command(
{tag, {:command_emergency_unlock, []}} = code,
{pid, _ref},
state
) do
send(self(), :timeout)
{:reply, {:ok, tag}, %{state | command_queue: [{pid, code}], configuration_queue: []}}
{:reply, {:ok, tag},
%{state | command_queue: [{pid, code}], configuration_queue: []}}
end
# If not in an acceptable state, return an error immediately.
@ -518,7 +578,8 @@ defmodule FarmbotFirmware do
end
@doc false
@spec handle_report({GCODE.report_kind(), GCODE.args()}, state) :: {:noreply, state()}
@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})
@ -588,7 +649,9 @@ defmodule FarmbotFirmware do
else: to_process
send(self(), :timeout)
{:noreply, goto(%{state | tag: tag, configuration_queue: to_process}, :configuration)}
{:noreply,
goto(%{state | tag: tag, configuration_queue: to_process}, :configuration)}
end
def handle_report({:report_debug_message, msg}, state) do
@ -603,15 +666,21 @@ defmodule FarmbotFirmware do
# 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, []}})
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, []}})
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])
@ -621,14 +690,18 @@ defmodule FarmbotFirmware do
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, []}})
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, []}})
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])
@ -638,15 +711,22 @@ defmodule FarmbotFirmware do
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, []}})
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
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, []}})
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_busy, []}})
side_effects(state, :handle_busy, [false])
{:stop, {:error, state.current}, state}
@ -654,43 +734,59 @@ defmodule FarmbotFirmware do
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, []}})
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
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, []}})
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, []}})
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
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, []}})
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, []}})
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_busy, []}})
side_effects(state, :handle_parameter_value, [param])
{:noreply, state}
@ -701,11 +797,19 @@ defmodule FarmbotFirmware do
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)}
{: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)
def handle_report(
{:report_parameters_complete, []},
%{status: status} = state
)
when status in [:begin, :configuration] do
{:noreply, goto(state, :idle)}
end
@ -716,7 +820,9 @@ defmodule FarmbotFirmware do
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, []}})
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_busy, []}})
side_effects(state, :handle_position, [position])
{:noreply, state}
@ -724,7 +830,9 @@ defmodule FarmbotFirmware do
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, []}})
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_busy, []}})
side_effects(state, :handle_load, [load])
{:noreply, state}
@ -732,7 +840,9 @@ defmodule FarmbotFirmware do
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, []}})
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_busy, []}})
side_effects(state, :handle_axis_state, [axis_state])
{:noreply, state}
@ -740,15 +850,22 @@ defmodule FarmbotFirmware do
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, []}})
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
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, []}})
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_busy, []}})
side_effects(state, :handle_calibration_state, [calibration_state])
{:noreply, state}
@ -756,7 +873,9 @@ defmodule FarmbotFirmware do
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, []}})
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_busy, []}})
side_effects(state, :handle_home_complete, axis)
{:noreply, state}
@ -764,7 +883,9 @@ defmodule FarmbotFirmware do
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, []}})
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_busy, []}})
side_effects(state, :handle_position_change, [position])
{:noreply, state}
@ -772,7 +893,9 @@ defmodule FarmbotFirmware do
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, []}})
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_busy, []}})
side_effects(state, :handle_encoders_scaled, [encoders])
{:noreply, state}
@ -780,7 +903,9 @@ defmodule FarmbotFirmware do
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, []}})
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_busy, []}})
side_effects(state, :handle_encoders_raw, [encoders])
{:noreply, state}
@ -788,7 +913,9 @@ defmodule FarmbotFirmware do
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, []}})
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_busy, []}})
side_effects(state, :handle_end_stops, [end_stops])
{:noreply, state}
@ -796,7 +923,9 @@ defmodule FarmbotFirmware do
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, []}})
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_busy, []}})
side_effects(state, :handle_pin_value, [value])
{:noreply, state}
@ -804,7 +933,9 @@ defmodule FarmbotFirmware do
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, []}})
for {pid, _code} <- state.command_queue,
do: send(pid, {state.tag, {:report_busy, []}})
side_effects(state, :handle_software_version, [version])
{:noreply, state}
@ -861,7 +992,9 @@ defmodule FarmbotFirmware do
@spec side_effects(state, atom, GCODE.args()) :: any()
defp side_effects(%{side_effects: nil}, _function, _args), do: nil
defp side_effects(%{side_effects: m}, function, args), do: apply(m, function, args)
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
@ -881,7 +1014,12 @@ defmodule FarmbotFirmware do
"nil"
end
state_data = "#{state.status} | #{current_data} | #{inspect(state.caller_pid)}"
IO.write(state.vcr_fd, direction <> " #{time} " <> data <> " state=" <> state_data <> "\n")
state_data =
"#{state.status} | #{current_data} | #{inspect(state.caller_pid)}"
IO.write(
state.vcr_fd,
direction <> " #{time} " <> data <> " state=" <> state_data <> "\n"
)
end
end

View File

@ -10,7 +10,10 @@ defmodule FarmbotFirmware.Command do
@spec command(GenServer.server(), GCODE.t() | {GCODE.kind(), GCODE.args()}) ::
:ok
| {:error,
:invalid_command | :firmware_error | :emergency_lock | FarmbotFirmware.status()}
:invalid_command
| :firmware_error
| :emergency_lock
| FarmbotFirmware.status()}
def command(firmware_server \\ FarmbotFirmware, code)
def command(firmware_server, {_tag, {_, _}} = code) do
@ -70,7 +73,9 @@ defmodule FarmbotFirmware.Command do
wait_for_command_result(tag, code, retries, err)
after
30_000 ->
raise("Firmware command: #{GCODE.encode(code)} failed to respond within 30 seconds")
raise(
"Firmware command: #{GCODE.encode(code)} failed to respond within 30 seconds"
)
end
end

View File

@ -62,7 +62,8 @@ defmodule FarmbotFirmware.GCODE do
| :software_version_read
| :position_write_zero
@type emergency_commands :: :command_emergency_lock | :command_emergency_unlock
@type emergency_commands ::
:command_emergency_lock | :command_emergency_unlock
@typedoc "Kind is an atom of the \"name\" of a command. Example: `:write_parameter`"
@type kind() :: report_kind | command_kind | read_write_kind | :unknown

View File

@ -13,10 +13,15 @@ defmodule FarmbotFirmware.GCODE.Decoder do
def do_decode("R04", []), do: {:report_busy, []}
def do_decode("R05", xyz), do: {:report_axis_state, decode_axis_state(xyz)}
def do_decode("R06", xyz), do: {:report_calibration_state, decode_calibration_state(xyz)}
def do_decode("R06", xyz),
do: {:report_calibration_state, decode_calibration_state(xyz)}
def do_decode("R07", []), do: {:report_retry, []}
def do_decode("R08", args), do: {:report_echo, decode_echo(Enum.join(args, " "))}
def do_decode("R08", args),
do: {:report_echo, decode_echo(Enum.join(args, " "))}
def do_decode("R09", []), do: {:report_invalid, []}
def do_decode("R11", []), do: {:report_home_complete, [:x]}
@ -30,14 +35,19 @@ defmodule FarmbotFirmware.GCODE.Decoder do
def do_decode("R20", []), do: {:report_parameters_complete, []}
def do_decode("R21", pv), do: {:report_parameter_value, decode_pv(pv)}
def do_decode("R23", pv), do: {:report_calibration_parameter_value, decode_pv(pv)}
def do_decode("R23", pv),
do: {:report_calibration_parameter_value, decode_pv(pv)}
def do_decode("R41", pv), do: {:report_pin_value, decode_ints(pv)}
def do_decode("R71", []), do: {:report_axis_timeout, [:x]}
def do_decode("R72", []), do: {:report_axis_timeout, [:y]}
def do_decode("R73", []), do: {:report_axis_timeout, [:z]}
def do_decode("R81", xxyyzz), do: {:report_end_stops, decode_end_stops(xxyyzz)}
def do_decode("R81", xxyyzz),
do: {:report_end_stops, decode_end_stops(xxyyzz)}
def do_decode("R82", xyzs), do: {:report_position, decode_floats(xyzs)}
def do_decode("R83", [version]), do: {:report_software_version, [version]}
@ -48,7 +58,9 @@ defmodule FarmbotFirmware.GCODE.Decoder do
def do_decode("R87", []), do: {:report_emergency_lock, []}
def do_decode("R88", []), do: {:report_no_config, []}
def do_decode("R89", xyz), do: {:report_load, decode_floats(xyz)}
def do_decode("R99", debug), do: {:report_debug_message, [Enum.join(debug, " ")]}
def do_decode("R99", debug),
do: {:report_debug_message, [Enum.join(debug, " ")]}
def do_decode("G00", xyzs), do: {:command_movement, decode_floats(xyzs)}
def do_decode("G28", []), do: {:comand_movement_home, [:x, :y, :z]}
@ -62,7 +74,10 @@ defmodule FarmbotFirmware.GCODE.Decoder do
def do_decode("F16", []), do: {:command_movement_calibrate, [:z]}
def do_decode("F20", []), do: {:parameter_read_all, []}
def do_decode("F21", [param_id]), do: {:parameter_read, [Param.decode(param_id)]}
def do_decode("F21", [param_id]),
do: {:parameter_read, [Param.decode(param_id)]}
def do_decode("F22", pv), do: {:parameter_write, decode_pv(pv)}
def do_decode("F23", pv), do: {:calibration_parameter_write, decode_pv(pv)}
@ -152,7 +167,10 @@ defmodule FarmbotFirmware.GCODE.Decoder do
defp decode_end_stops(list, acc \\ [])
defp decode_end_stops(
[<<arg::binary-1, "A", val0::binary>>, <<arg::binary-1, "B", val1::binary>> | rest],
[
<<arg::binary-1, "A", val0::binary>>,
<<arg::binary-1, "B", val1::binary>> | rest
],
acc
) do
dc = String.downcase(arg)

View File

@ -15,7 +15,9 @@ defmodule FarmbotFirmware.GCODE.Encoder do
def do_encode(:report_busy, []), do: "R04"
def do_encode(:report_axis_state, xyz), do: "R05 " <> encode_axis_state(xyz)
def do_encode(:report_calibration_state, xyz), do: "R06 " <> encode_calibration_state(xyz)
def do_encode(:report_calibration_state, xyz),
do: "R06 " <> encode_calibration_state(xyz)
def do_encode(:report_retry, []), do: "R07"
def do_encode(:report_echo, [echo]), do: "R08 * #{echo} *"
@ -25,21 +27,31 @@ defmodule FarmbotFirmware.GCODE.Encoder do
def do_encode(:report_home_complete, [:y]), do: "R12"
def do_encode(:report_home_complete, [:z]), do: "R13"
def do_encode(:report_position_change, [x: _] = arg), do: "R15 " <> encode_floats(arg)
def do_encode(:report_position_change, [y: _] = arg), do: "R16 " <> encode_floats(arg)
def do_encode(:report_position_change, [z: _] = arg), do: "R16 " <> encode_floats(arg)
def do_encode(:report_position_change, [x: _] = arg),
do: "R15 " <> encode_floats(arg)
def do_encode(:report_position_change, [y: _] = arg),
do: "R16 " <> encode_floats(arg)
def do_encode(:report_position_change, [z: _] = arg),
do: "R16 " <> encode_floats(arg)
def do_encode(:report_parameters_complete, []), do: "R20"
def do_encode(:report_parameter_value, pv), do: "R21 " <> encode_pv(pv)
def do_encode(:report_calibration_parameter_value, pv), do: "R23 " <> encode_pv(pv)
def do_encode(:report_calibration_parameter_value, pv),
do: "R23 " <> encode_pv(pv)
def do_encode(:report_pin_value, pv), do: "R41 " <> encode_ints(pv)
def do_encode(:report_axis_timeout, [:x]), do: "R71"
def do_encode(:report_axis_timeout, [:y]), do: "R72"
def do_encode(:report_axis_timeout, [:z]), do: "R73"
def do_encode(:report_end_stops, xxyyzz), do: "R81 " <> encode_end_stops(xxyyzz)
def do_encode(:report_end_stops, xxyyzz),
do: "R81 " <> encode_end_stops(xxyyzz)
def do_encode(:report_position, xyzs), do: "R82 " <> encode_floats(xyzs)
def do_encode(:report_software_version, [version]), do: "R83 " <> version
@ -54,9 +66,15 @@ defmodule FarmbotFirmware.GCODE.Encoder do
def do_encode(:command_movement, xyzs), do: "G00 " <> encode_floats(xyzs)
def do_encode(:command_movement_home, [:x, :y, :z]), do: "G28"
def do_encode(:command_movement_home, [:x]), do: "G00 " <> encode_floats(x: 0.0)
def do_encode(:command_movement_home, [:y]), do: "G00 " <> encode_floats(y: 0.0)
def do_encode(:command_movement_home, [:z]), do: "G00 " <> encode_floats(z: 0.0)
def do_encode(:command_movement_home, [:x]),
do: "G00 " <> encode_floats(x: 0.0)
def do_encode(:command_movement_home, [:y]),
do: "G00 " <> encode_floats(y: 0.0)
def do_encode(:command_movement_home, [:z]),
do: "G00 " <> encode_floats(z: 0.0)
def do_encode(:command_movement_find_home, [:x]), do: "F11"
def do_encode(:command_movement_find_home, [:y]), do: "F12"
@ -67,7 +85,9 @@ defmodule FarmbotFirmware.GCODE.Encoder do
def do_encode(:command_movement_calibrate, [:z]), do: "F16"
def do_encode(:parameter_read_all, []), do: "F20"
def do_encode(:parameter_read, [parameter]), do: "F21 P#{Param.encode(parameter)}"
def do_encode(:parameter_read, [parameter]),
do: "F21 P#{Param.encode(parameter)}"
def do_encode(:parameter_write, pv), do: "F22 " <> encode_pv(pv)
def do_encode(:calibration_parameter_write, pv), do: "F23 " <> encode_pv(pv)

View File

@ -3,23 +3,28 @@ defmodule FarmbotFirmware.PackageUtils do
def find_hex_file(package)
def find_hex_file("arduino") do
Application.app_dir(:farmbot_firmware, ["priv", "arduino_firmware.hex"]) |> assert_exists()
Application.app_dir(:farmbot_firmware, ["priv", "arduino_firmware.hex"])
|> assert_exists()
end
def find_hex_file("farmduino") do
Application.app_dir(:farmbot_firmware, ["priv", "farmduino.hex"]) |> assert_exists()
Application.app_dir(:farmbot_firmware, ["priv", "farmduino.hex"])
|> assert_exists()
end
def find_hex_file("farmduino_k14") do
Application.app_dir(:farmbot_firmware, ["priv", "farmduino_k14.hex"]) |> assert_exists()
Application.app_dir(:farmbot_firmware, ["priv", "farmduino_k14.hex"])
|> assert_exists()
end
def find_hex_file("farmduino_k15") do
Application.app_dir(:farmbot_firmware, ["priv", "farmduino_k15.hex"]) |> assert_exists()
Application.app_dir(:farmbot_firmware, ["priv", "farmduino_k15.hex"])
|> assert_exists()
end
def find_hex_file("express_k10") do
Application.app_dir(:farmbot_firmware, ["priv", "express_k10.hex"]) |> assert_exists()
Application.app_dir(:farmbot_firmware, ["priv", "express_k10.hex"])
|> assert_exists()
end
def find_hex_file(hardware) when is_binary(hardware),

View File

@ -9,7 +9,8 @@ defmodule FarmbotFirmware.Request do
@spec request(GenServer.server(), GCODE.t()) ::
{:ok, GCODE.t()}
| {:error, :invalid_command | :firmware_error | FarmbotFirmware.status()}
| {:error,
:invalid_command | :firmware_error | FarmbotFirmware.status()}
def request(firmware_server \\ FarmbotFirmware, code)
def request(firmware_server, {_tag, {kind, _}} = code) do

View File

@ -17,7 +17,14 @@ defmodule FarmbotFirmware.SideEffects do
@callback handle_encoders_raw(x: float(), y: float(), z: float()) :: any()
@callback handle_parameter_value([{Param.t(), float()}]) :: any()
@callback handle_parameter_calibration_value([{Param.t(), float()}]) :: any()
@callback handle_end_stops(xa: 0 | 1, xb: 0 | 1, ya: 0 | 1, yb: 0 | 1, za: 0 | 1, zb: 0 | 1) ::
@callback handle_end_stops(
xa: 0 | 1,
xb: 0 | 1,
ya: 0 | 1,
yb: 0 | 1,
za: 0 | 1,
zb: 0 | 1
) ::
any()
@callback handle_emergency_lock() :: any()
@callback handle_emergency_unlock() :: any()
@ -26,7 +33,8 @@ defmodule FarmbotFirmware.SideEffects do
@callback handle_busy(boolean()) :: any()
@callback handle_idle(boolean()) :: any()
@type axis_state :: :stop | :idle | :begin | :crawl | :decelerate | :accelerate
@type axis_state ::
:stop | :idle | :begin | :crawl | :decelerate | :accelerate
@callback handle_axis_state([{axis(), axis_state}]) :: any()
@callback handle_axis_timeout(axis()) :: any()

View File

@ -30,7 +30,10 @@ defmodule FarmbotFirmware.StubTransport do
end
def handle_info(:timeout, %{status: :boot} = state) do
state.handle_gcode.(GCODE.new(:report_debug_message, ["ARDUINO STARTUP COMPLETE"]))
state.handle_gcode.(
GCODE.new(:report_debug_message, ["ARDUINO STARTUP COMPLETE"])
)
{:noreply, goto(state, :no_config), 0}
end
@ -79,7 +82,8 @@ defmodule FarmbotFirmware.StubTransport do
end
def handle_call(
{tag, {:parameter_write, [{:param_config_ok = param, 1.0 = value}]}} = code,
{tag, {:parameter_write, [{:param_config_ok = param, 1.0 = value}]}} =
code,
_from,
state
) do
@ -94,7 +98,11 @@ defmodule FarmbotFirmware.StubTransport do
{:reply, :ok, goto(new_state, :idle), {:continue, resp_codes}}
end
def handle_call({tag, {:parameter_write, [{param, value}]}} = code, _from, state) do
def handle_call(
{tag, {:parameter_write, [{param, value}]}} = code,
_from,
state
) do
new_state = %{state | params: Keyword.put(state.params, param, value)}
resp_codes = [
@ -132,7 +140,11 @@ defmodule FarmbotFirmware.StubTransport do
{:reply, :ok, state, {:continue, resp_codes}}
end
def handle_call({tag, {:position_write_zero, [:x, :y, :z]}} = code, _from, state) do
def handle_call(
{tag, {:position_write_zero, [:x, :y, :z]}} = code,
_from,
state
) do
position = [
x: 0.0,
y: 0.0,
@ -166,7 +178,11 @@ defmodule FarmbotFirmware.StubTransport do
{:reply, :ok, state, {:continue, resp_codes}}
end
def handle_call({tag, {:command_movement_calibrate, [axis]}} = code, _from, state) do
def handle_call(
{tag, {:command_movement_calibrate, [axis]}} = code,
_from,
state
) do
position = [x: 0.0, y: 0.0, z: 0.0]
state = %{state | position: position}
param_nr_steps = :"movement_axis_nr_steps_#{axis}"
@ -182,8 +198,12 @@ defmodule FarmbotFirmware.StubTransport do
GCODE.new(:report_calibration_state, [:idle]),
GCODE.new(:report_calibration_state, [:home]),
GCODE.new(:report_calibration_state, [:end]),
GCODE.new(:report_calibration_parameter_value, [{param_nr_steps, param_nr_steps_val}]),
GCODE.new(:report_calibration_parameter_value, [{param_endpoints, param_endpoints_val}]),
GCODE.new(:report_calibration_parameter_value, [
{param_nr_steps, param_nr_steps_val}
]),
GCODE.new(:report_calibration_parameter_value, [
{param_endpoints, param_endpoints_val}
]),
GCODE.new(:report_position, state.position),
GCODE.new(:report_success, [], tag)
]
@ -203,7 +223,11 @@ defmodule FarmbotFirmware.StubTransport do
end
# Everything under this clause should be blocked if emergency_locked
def handle_call({_tag, {_, _}} = code, _from, %{status: :emergency_lock} = state) do
def handle_call(
{_tag, {_, _}} = code,
_from,
%{status: :emergency_lock} = state
) do
Logger.error("Stub Transport emergency lock")
resp_codes = [
@ -291,7 +315,11 @@ defmodule FarmbotFirmware.StubTransport do
{:reply, :ok, state, {:continue, resp_codes}}
end
def handle_call({tag, {:command_movement_home, [:x, :y, :z]}} = code, _from, state) do
def handle_call(
{tag, {:command_movement_home, [:x, :y, :z]}} = code,
_from,
state
) do
position = [
x: 0.0,
y: 0.0,
@ -326,7 +354,11 @@ defmodule FarmbotFirmware.StubTransport do
{:reply, :ok, state, {:continue, resp_codes}}
end
def handle_call({tag, {:command_movement_find_home, [axis]}} = code, _from, state) do
def handle_call(
{tag, {:command_movement_find_home, [axis]}} = code,
_from,
state
) do
position = Keyword.put(state.position, axis, 0.0) |> ensure_order()
state = %{state | position: position}
@ -342,7 +374,9 @@ defmodule FarmbotFirmware.StubTransport do
end
def handle_call({tag, {_, _}} = code, _from, state) do
Logger.error("STUB HANDLER: unknown code: #{inspect(code)} for state: #{state.status}")
Logger.error(
"STUB HANDLER: unknown code: #{inspect(code)} for state: #{state.status}"
)
resp_codes = [
GCODE.new(:report_echo, [GCODE.encode(code)]),

View File

@ -15,7 +15,15 @@ defmodule FarmbotFirmware.UARTTransport do
handle_gcode = Keyword.fetch!(args, :handle_gcode)
reset = Keyword.get(args, :reset)
{:ok, uart} = uart_adapter().start_link()
{:ok, %{uart: uart, device: device, open: false, handle_gcode: handle_gcode, reset: reset}, 0}
{:ok,
%{
uart: uart,
device: device,
open: false,
handle_gcode: handle_gcode,
reset: reset
}, 0}
end
def terminate(_, %{uart: uart}) do

View File

@ -1,7 +1,12 @@
defmodule FarmbotFirmware.MixProject do
use Mix.Project
@version Path.join([__DIR__, "..", "VERSION"]) |> File.read!() |> String.trim()
@elixir_version Path.join([__DIR__, "..", "ELIXIR_VERSION"]) |> File.read!() |> String.trim()
@version Path.join([__DIR__, "..", "VERSION"])
|> File.read!()
|> String.trim()
@elixir_version Path.join([__DIR__, "..", "ELIXIR_VERSION"])
|> File.read!()
|> String.trim()
defp arduino_commit do
opts = [cd: Path.join("c_src", "farmbot-arduino-firmware")]
@ -51,7 +56,8 @@ defmodule FarmbotFirmware.MixProject do
{:farmbot_telemetry, path: "../farmbot_telemetry", env: Mix.env()},
{:circuits_uart, "~> 1.4.0"},
{:excoveralls, "~> 0.10", only: [:test], targets: [:host]},
{:dialyxir, "~> 1.0.0-rc.3", only: [:dev], targets: [:host], runtime: false},
{:dialyxir, "~> 1.0.0-rc.3",
only: [:dev], targets: [:host], runtime: false},
{:mox, "~> 0.5.1", only: :test},
{:ex_doc, "~> 0.21.2", only: [:dev], targets: [:host], runtime: false}
]

View File

@ -67,7 +67,9 @@ defmodule FarmbotFirmware.UARTTransportTest do
{:error, "Simulated UART failure. This is OK"}
end)
{:noreply, state2, retry_timeout} = UARTTransport.handle_info(:timeout, state)
{:noreply, state2, retry_timeout} =
UARTTransport.handle_info(:timeout, state)
assert retry_timeout == 5000
assert state.open == state2.open
end
@ -77,7 +79,9 @@ defmodule FarmbotFirmware.UARTTransportTest do
provided_reason = "Simulated failure (circuits UART)"
info = {:circuits_uart, nil, {:error, provided_reason}}
{:stop, {:uart_error, reason}, state2} = UARTTransport.handle_info(info, state)
{:stop, {:uart_error, reason}, state2} =
UARTTransport.handle_info(info, state)
assert reason == provided_reason
assert state == state2
end

View File

@ -6,7 +6,8 @@ defmodule FarmbotFirmwareTest do
pid =
start_supervised!({
FarmbotFirmware,
transport: FarmbotFirmware.StubTransport, side_effects: FarmbotCore.FirmwareSideEffects
transport: FarmbotFirmware.StubTransport,
side_effects: FarmbotCore.FirmwareSideEffects
})
# WIP

View File

@ -44,7 +44,10 @@ defmodule FarmbotFirmware.GCODETest do
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, [:invalid_command]}} = GCODE.decode("R03 V14")
assert {nil, {:report_error, [:invalid_command]}} =
GCODE.decode("R03 V14")
assert {nil, {:report_error, [:no_config]}} = GCODE.decode("R03 V15")
assert {"100", {:report_error, [:no_error]}} = GCODE.decode("R03 Q100")
@ -63,149 +66,323 @@ defmodule FarmbotFirmware.GCODETest do
test "axis state" do
assert {nil, {:report_axis_state, [x: :idle]}} = GCODE.decode("R05 X0")
assert {nil, {:report_axis_state, [x: :begin]}} = GCODE.decode("R05 X1")
assert {nil, {:report_axis_state, [x: :accelerate]}} = GCODE.decode("R05 X2")
assert {nil, {:report_axis_state, [x: :accelerate]}} =
GCODE.decode("R05 X2")
assert {nil, {:report_axis_state, [x: :cruise]}} = GCODE.decode("R05 X3")
assert {nil, {:report_axis_state, [x: :decelerate]}} = GCODE.decode("R05 X4")
assert {nil, {:report_axis_state, [x: :decelerate]}} =
GCODE.decode("R05 X4")
assert {nil, {:report_axis_state, [x: :stop]}} = GCODE.decode("R05 X5")
assert {nil, {:report_axis_state, [x: :crawl]}} = GCODE.decode("R05 X6")
assert {"12", {:report_axis_state, [x: :idle]}} = GCODE.decode("R05 X0 Q12")
assert {"12", {:report_axis_state, [x: :begin]}} = GCODE.decode("R05 X1 Q12")
assert {"12", {:report_axis_state, [x: :accelerate]}} = GCODE.decode("R05 X2 Q12")
assert {"12", {:report_axis_state, [x: :cruise]}} = GCODE.decode("R05 X3 Q12")
assert {"12", {:report_axis_state, [x: :decelerate]}} = GCODE.decode("R05 X4 Q12")
assert {"12", {:report_axis_state, [x: :stop]}} = GCODE.decode("R05 X5 Q12")
assert {"12", {:report_axis_state, [x: :crawl]}} = GCODE.decode("R05 X6 Q12")
assert {"12", {:report_axis_state, [x: :idle]}} =
GCODE.decode("R05 X0 Q12")
assert {"12", {:report_axis_state, [x: :begin]}} =
GCODE.decode("R05 X1 Q12")
assert {"12", {:report_axis_state, [x: :accelerate]}} =
GCODE.decode("R05 X2 Q12")
assert {"12", {:report_axis_state, [x: :cruise]}} =
GCODE.decode("R05 X3 Q12")
assert {"12", {:report_axis_state, [x: :decelerate]}} =
GCODE.decode("R05 X4 Q12")
assert {"12", {:report_axis_state, [x: :stop]}} =
GCODE.decode("R05 X5 Q12")
assert {"12", {:report_axis_state, [x: :crawl]}} =
GCODE.decode("R05 X6 Q12")
assert "R05 X0" = GCODE.encode({nil, {:report_axis_state, [x: :idle]}})
assert "R05 X1" = GCODE.encode({nil, {:report_axis_state, [x: :begin]}})
assert "R05 X2" = GCODE.encode({nil, {:report_axis_state, [x: :accelerate]}})
assert "R05 X2" =
GCODE.encode({nil, {:report_axis_state, [x: :accelerate]}})
assert "R05 X3" = GCODE.encode({nil, {:report_axis_state, [x: :cruise]}})
assert "R05 X4" = GCODE.encode({nil, {:report_axis_state, [x: :decelerate]}})
assert "R05 X4" =
GCODE.encode({nil, {:report_axis_state, [x: :decelerate]}})
assert "R05 X5" = GCODE.encode({nil, {:report_axis_state, [x: :stop]}})
assert "R05 X6" = GCODE.encode({nil, {:report_axis_state, [x: :crawl]}})
assert "R05 X0 Q12" = GCODE.encode({"12", {:report_axis_state, [x: :idle]}})
assert "R05 X1 Q12" = GCODE.encode({"12", {:report_axis_state, [x: :begin]}})
assert "R05 X2 Q12" = GCODE.encode({"12", {:report_axis_state, [x: :accelerate]}})
assert "R05 X3 Q12" = GCODE.encode({"12", {:report_axis_state, [x: :cruise]}})
assert "R05 X4 Q12" = GCODE.encode({"12", {:report_axis_state, [x: :decelerate]}})
assert "R05 X5 Q12" = GCODE.encode({"12", {:report_axis_state, [x: :stop]}})
assert "R05 X6 Q12" = GCODE.encode({"12", {:report_axis_state, [x: :crawl]}})
assert "R05 X0 Q12" =
GCODE.encode({"12", {:report_axis_state, [x: :idle]}})
assert "R05 X1 Q12" =
GCODE.encode({"12", {:report_axis_state, [x: :begin]}})
assert "R05 X2 Q12" =
GCODE.encode({"12", {:report_axis_state, [x: :accelerate]}})
assert "R05 X3 Q12" =
GCODE.encode({"12", {:report_axis_state, [x: :cruise]}})
assert "R05 X4 Q12" =
GCODE.encode({"12", {:report_axis_state, [x: :decelerate]}})
assert "R05 X5 Q12" =
GCODE.encode({"12", {:report_axis_state, [x: :stop]}})
assert "R05 X6 Q12" =
GCODE.encode({"12", {:report_axis_state, [x: :crawl]}})
assert {nil, {:report_axis_state, [y: :idle]}} = GCODE.decode("R05 Y0")
assert {nil, {:report_axis_state, [y: :begin]}} = GCODE.decode("R05 Y1")
assert {nil, {:report_axis_state, [y: :accelerate]}} = GCODE.decode("R05 Y2")
assert {nil, {:report_axis_state, [y: :accelerate]}} =
GCODE.decode("R05 Y2")
assert {nil, {:report_axis_state, [y: :cruise]}} = GCODE.decode("R05 Y3")
assert {nil, {:report_axis_state, [y: :decelerate]}} = GCODE.decode("R05 Y4")
assert {nil, {:report_axis_state, [y: :decelerate]}} =
GCODE.decode("R05 Y4")
assert {nil, {:report_axis_state, [y: :stop]}} = GCODE.decode("R05 Y5")
assert {nil, {:report_axis_state, [y: :crawl]}} = GCODE.decode("R05 Y6")
assert {"13", {:report_axis_state, [y: :idle]}} = GCODE.decode("R05 Y0 Q13")
assert {"13", {:report_axis_state, [y: :begin]}} = GCODE.decode("R05 Y1 Q13")
assert {"13", {:report_axis_state, [y: :accelerate]}} = GCODE.decode("R05 Y2 Q13")
assert {"13", {:report_axis_state, [y: :cruise]}} = GCODE.decode("R05 Y3 Q13")
assert {"13", {:report_axis_state, [y: :decelerate]}} = GCODE.decode("R05 Y4 Q13")
assert {"13", {:report_axis_state, [y: :stop]}} = GCODE.decode("R05 Y5 Q13")
assert {"13", {:report_axis_state, [y: :crawl]}} = GCODE.decode("R05 Y6 Q13")
assert {"13", {:report_axis_state, [y: :idle]}} =
GCODE.decode("R05 Y0 Q13")
assert {"13", {:report_axis_state, [y: :begin]}} =
GCODE.decode("R05 Y1 Q13")
assert {"13", {:report_axis_state, [y: :accelerate]}} =
GCODE.decode("R05 Y2 Q13")
assert {"13", {:report_axis_state, [y: :cruise]}} =
GCODE.decode("R05 Y3 Q13")
assert {"13", {:report_axis_state, [y: :decelerate]}} =
GCODE.decode("R05 Y4 Q13")
assert {"13", {:report_axis_state, [y: :stop]}} =
GCODE.decode("R05 Y5 Q13")
assert {"13", {:report_axis_state, [y: :crawl]}} =
GCODE.decode("R05 Y6 Q13")
assert "R05 Y0" = GCODE.encode({nil, {:report_axis_state, [y: :idle]}})
assert "R05 Y1" = GCODE.encode({nil, {:report_axis_state, [y: :begin]}})
assert "R05 Y2" = GCODE.encode({nil, {:report_axis_state, [y: :accelerate]}})
assert "R05 Y2" =
GCODE.encode({nil, {:report_axis_state, [y: :accelerate]}})
assert "R05 Y3" = GCODE.encode({nil, {:report_axis_state, [y: :cruise]}})
assert "R05 Y4" = GCODE.encode({nil, {:report_axis_state, [y: :decelerate]}})
assert "R05 Y4" =
GCODE.encode({nil, {:report_axis_state, [y: :decelerate]}})
assert "R05 Y5" = GCODE.encode({nil, {:report_axis_state, [y: :stop]}})
assert "R05 Y6" = GCODE.encode({nil, {:report_axis_state, [y: :crawl]}})
assert "R05 Y0 Q13" = GCODE.encode({"13", {:report_axis_state, [y: :idle]}})
assert "R05 Y1 Q13" = GCODE.encode({"13", {:report_axis_state, [y: :begin]}})
assert "R05 Y2 Q13" = GCODE.encode({"13", {:report_axis_state, [y: :accelerate]}})
assert "R05 Y3 Q13" = GCODE.encode({"13", {:report_axis_state, [y: :cruise]}})
assert "R05 Y4 Q13" = GCODE.encode({"13", {:report_axis_state, [y: :decelerate]}})
assert "R05 Y5 Q13" = GCODE.encode({"13", {:report_axis_state, [y: :stop]}})
assert "R05 Y6 Q13" = GCODE.encode({"13", {:report_axis_state, [y: :crawl]}})
assert "R05 Y0 Q13" =
GCODE.encode({"13", {:report_axis_state, [y: :idle]}})
assert "R05 Y1 Q13" =
GCODE.encode({"13", {:report_axis_state, [y: :begin]}})
assert "R05 Y2 Q13" =
GCODE.encode({"13", {:report_axis_state, [y: :accelerate]}})
assert "R05 Y3 Q13" =
GCODE.encode({"13", {:report_axis_state, [y: :cruise]}})
assert "R05 Y4 Q13" =
GCODE.encode({"13", {:report_axis_state, [y: :decelerate]}})
assert "R05 Y5 Q13" =
GCODE.encode({"13", {:report_axis_state, [y: :stop]}})
assert "R05 Y6 Q13" =
GCODE.encode({"13", {:report_axis_state, [y: :crawl]}})
assert {nil, {:report_axis_state, [z: :idle]}} = GCODE.decode("R05 Z0")
assert {nil, {:report_axis_state, [z: :begin]}} = GCODE.decode("R05 Z1")
assert {nil, {:report_axis_state, [z: :accelerate]}} = GCODE.decode("R05 Z2")
assert {nil, {:report_axis_state, [z: :accelerate]}} =
GCODE.decode("R05 Z2")
assert {nil, {:report_axis_state, [z: :cruise]}} = GCODE.decode("R05 Z3")
assert {nil, {:report_axis_state, [z: :decelerate]}} = GCODE.decode("R05 Z4")
assert {nil, {:report_axis_state, [z: :decelerate]}} =
GCODE.decode("R05 Z4")
assert {nil, {:report_axis_state, [z: :stop]}} = GCODE.decode("R05 Z5")
assert {nil, {:report_axis_state, [z: :crawl]}} = GCODE.decode("R05 Z6")
assert {"14", {:report_axis_state, [z: :idle]}} = GCODE.decode("R05 Z0 Q14")
assert {"14", {:report_axis_state, [z: :begin]}} = GCODE.decode("R05 Z1 Q14")
assert {"14", {:report_axis_state, [z: :accelerate]}} = GCODE.decode("R05 Z2 Q14")
assert {"14", {:report_axis_state, [z: :cruise]}} = GCODE.decode("R05 Z3 Q14")
assert {"14", {:report_axis_state, [z: :decelerate]}} = GCODE.decode("R05 Z4 Q14")
assert {"14", {:report_axis_state, [z: :stop]}} = GCODE.decode("R05 Z5 Q14")
assert {"14", {:report_axis_state, [z: :crawl]}} = GCODE.decode("R05 Z6 Q14")
assert {"14", {:report_axis_state, [z: :idle]}} =
GCODE.decode("R05 Z0 Q14")
assert {"14", {:report_axis_state, [z: :begin]}} =
GCODE.decode("R05 Z1 Q14")
assert {"14", {:report_axis_state, [z: :accelerate]}} =
GCODE.decode("R05 Z2 Q14")
assert {"14", {:report_axis_state, [z: :cruise]}} =
GCODE.decode("R05 Z3 Q14")
assert {"14", {:report_axis_state, [z: :decelerate]}} =
GCODE.decode("R05 Z4 Q14")
assert {"14", {:report_axis_state, [z: :stop]}} =
GCODE.decode("R05 Z5 Q14")
assert {"14", {:report_axis_state, [z: :crawl]}} =
GCODE.decode("R05 Z6 Q14")
assert "R05 Z0" = GCODE.encode({nil, {:report_axis_state, [z: :idle]}})
assert "R05 Z1" = GCODE.encode({nil, {:report_axis_state, [z: :begin]}})
assert "R05 Z2" = GCODE.encode({nil, {:report_axis_state, [z: :accelerate]}})
assert "R05 Z2" =
GCODE.encode({nil, {:report_axis_state, [z: :accelerate]}})
assert "R05 Z3" = GCODE.encode({nil, {:report_axis_state, [z: :cruise]}})
assert "R05 Z4" = GCODE.encode({nil, {:report_axis_state, [z: :decelerate]}})
assert "R05 Z4" =
GCODE.encode({nil, {:report_axis_state, [z: :decelerate]}})
assert "R05 Z5" = GCODE.encode({nil, {:report_axis_state, [z: :stop]}})
assert "R05 Z6" = GCODE.encode({nil, {:report_axis_state, [z: :crawl]}})
assert "R05 Z0 Q14" = GCODE.encode({"14", {:report_axis_state, [z: :idle]}})
assert "R05 Z1 Q14" = GCODE.encode({"14", {:report_axis_state, [z: :begin]}})
assert "R05 Z2 Q14" = GCODE.encode({"14", {:report_axis_state, [z: :accelerate]}})
assert "R05 Z3 Q14" = GCODE.encode({"14", {:report_axis_state, [z: :cruise]}})
assert "R05 Z4 Q14" = GCODE.encode({"14", {:report_axis_state, [z: :decelerate]}})
assert "R05 Z5 Q14" = GCODE.encode({"14", {:report_axis_state, [z: :stop]}})
assert "R05 Z6 Q14" = GCODE.encode({"14", {:report_axis_state, [z: :crawl]}})
assert "R05 Z0 Q14" =
GCODE.encode({"14", {:report_axis_state, [z: :idle]}})
assert "R05 Z1 Q14" =
GCODE.encode({"14", {:report_axis_state, [z: :begin]}})
assert "R05 Z2 Q14" =
GCODE.encode({"14", {:report_axis_state, [z: :accelerate]}})
assert "R05 Z3 Q14" =
GCODE.encode({"14", {:report_axis_state, [z: :cruise]}})
assert "R05 Z4 Q14" =
GCODE.encode({"14", {:report_axis_state, [z: :decelerate]}})
assert "R05 Z5 Q14" =
GCODE.encode({"14", {:report_axis_state, [z: :stop]}})
assert "R05 Z6 Q14" =
GCODE.encode({"14", {:report_axis_state, [z: :crawl]}})
end
test "calibration" do
assert {nil, {:report_calibration_state, [x: :idle]}} = GCODE.decode("R06 X0")
assert {nil, {:report_calibration_state, [x: :home]}} = GCODE.decode("R06 X1")
assert {nil, {:report_calibration_state, [x: :end]}} = GCODE.decode("R06 X2")
assert {nil, {:report_calibration_state, [x: :idle]}} =
GCODE.decode("R06 X0")
assert {"1", {:report_calibration_state, [x: :idle]}} = GCODE.decode("R06 X0 Q1")
assert {"1", {:report_calibration_state, [x: :home]}} = GCODE.decode("R06 X1 Q1")
assert {"1", {:report_calibration_state, [x: :end]}} = GCODE.decode("R06 X2 Q1")
assert {nil, {:report_calibration_state, [x: :home]}} =
GCODE.decode("R06 X1")
assert "R06 X0" = GCODE.encode({nil, {:report_calibration_state, [x: :idle]}})
assert "R06 X1" = GCODE.encode({nil, {:report_calibration_state, [x: :home]}})
assert "R06 X2" = GCODE.encode({nil, {:report_calibration_state, [x: :end]}})
assert {nil, {:report_calibration_state, [x: :end]}} =
GCODE.decode("R06 X2")
assert "R06 X0 Q1" = GCODE.encode({"1", {:report_calibration_state, [x: :idle]}})
assert "R06 X1 Q1" = GCODE.encode({"1", {:report_calibration_state, [x: :home]}})
assert "R06 X2 Q1" = GCODE.encode({"1", {:report_calibration_state, [x: :end]}})
assert {"1", {:report_calibration_state, [x: :idle]}} =
GCODE.decode("R06 X0 Q1")
assert {nil, {:report_calibration_state, [y: :idle]}} = GCODE.decode("R06 Y0")
assert {nil, {:report_calibration_state, [y: :home]}} = GCODE.decode("R06 Y1")
assert {nil, {:report_calibration_state, [y: :end]}} = GCODE.decode("R06 Y2")
assert {"1", {:report_calibration_state, [x: :home]}} =
GCODE.decode("R06 X1 Q1")
assert {"1", {:report_calibration_state, [y: :idle]}} = GCODE.decode("R06 Y0 Q1")
assert {"1", {:report_calibration_state, [y: :home]}} = GCODE.decode("R06 Y1 Q1")
assert {"1", {:report_calibration_state, [y: :end]}} = GCODE.decode("R06 Y2 Q1")
assert {"1", {:report_calibration_state, [x: :end]}} =
GCODE.decode("R06 X2 Q1")
assert "R06 Y0" = GCODE.encode({nil, {:report_calibration_state, [y: :idle]}})
assert "R06 Y1" = GCODE.encode({nil, {:report_calibration_state, [y: :home]}})
assert "R06 Y2" = GCODE.encode({nil, {:report_calibration_state, [y: :end]}})
assert "R06 X0" =
GCODE.encode({nil, {:report_calibration_state, [x: :idle]}})
assert "R06 Y0 Q1" = GCODE.encode({"1", {:report_calibration_state, [y: :idle]}})
assert "R06 Y1 Q1" = GCODE.encode({"1", {:report_calibration_state, [y: :home]}})
assert "R06 Y2 Q1" = GCODE.encode({"1", {:report_calibration_state, [y: :end]}})
assert "R06 X1" =
GCODE.encode({nil, {:report_calibration_state, [x: :home]}})
assert {nil, {:report_calibration_state, [z: :idle]}} = GCODE.decode("R06 Z0")
assert {nil, {:report_calibration_state, [z: :home]}} = GCODE.decode("R06 Z1")
assert {nil, {:report_calibration_state, [z: :end]}} = GCODE.decode("R06 Z2")
assert "R06 X2" =
GCODE.encode({nil, {:report_calibration_state, [x: :end]}})
assert {"1", {:report_calibration_state, [z: :idle]}} = GCODE.decode("R06 Z0 Q1")
assert {"1", {:report_calibration_state, [z: :home]}} = GCODE.decode("R06 Z1 Q1")
assert {"1", {:report_calibration_state, [z: :end]}} = GCODE.decode("R06 Z2 Q1")
assert "R06 X0 Q1" =
GCODE.encode({"1", {:report_calibration_state, [x: :idle]}})
assert "R06 Z0" = GCODE.encode({nil, {:report_calibration_state, [z: :idle]}})
assert "R06 Z1" = GCODE.encode({nil, {:report_calibration_state, [z: :home]}})
assert "R06 Z2" = GCODE.encode({nil, {:report_calibration_state, [z: :end]}})
assert "R06 X1 Q1" =
GCODE.encode({"1", {:report_calibration_state, [x: :home]}})
assert "R06 Z0 Q1" = GCODE.encode({"1", {:report_calibration_state, [z: :idle]}})
assert "R06 Z1 Q1" = GCODE.encode({"1", {:report_calibration_state, [z: :home]}})
assert "R06 Z2 Q1" = GCODE.encode({"1", {:report_calibration_state, [z: :end]}})
assert "R06 X2 Q1" =
GCODE.encode({"1", {:report_calibration_state, [x: :end]}})
assert {nil, {:report_calibration_state, [y: :idle]}} =
GCODE.decode("R06 Y0")
assert {nil, {:report_calibration_state, [y: :home]}} =
GCODE.decode("R06 Y1")
assert {nil, {:report_calibration_state, [y: :end]}} =
GCODE.decode("R06 Y2")
assert {"1", {:report_calibration_state, [y: :idle]}} =
GCODE.decode("R06 Y0 Q1")
assert {"1", {:report_calibration_state, [y: :home]}} =
GCODE.decode("R06 Y1 Q1")
assert {"1", {:report_calibration_state, [y: :end]}} =
GCODE.decode("R06 Y2 Q1")
assert "R06 Y0" =
GCODE.encode({nil, {:report_calibration_state, [y: :idle]}})
assert "R06 Y1" =
GCODE.encode({nil, {:report_calibration_state, [y: :home]}})
assert "R06 Y2" =
GCODE.encode({nil, {:report_calibration_state, [y: :end]}})
assert "R06 Y0 Q1" =
GCODE.encode({"1", {:report_calibration_state, [y: :idle]}})
assert "R06 Y1 Q1" =
GCODE.encode({"1", {:report_calibration_state, [y: :home]}})
assert "R06 Y2 Q1" =
GCODE.encode({"1", {:report_calibration_state, [y: :end]}})
assert {nil, {:report_calibration_state, [z: :idle]}} =
GCODE.decode("R06 Z0")
assert {nil, {:report_calibration_state, [z: :home]}} =
GCODE.decode("R06 Z1")
assert {nil, {:report_calibration_state, [z: :end]}} =
GCODE.decode("R06 Z2")
assert {"1", {:report_calibration_state, [z: :idle]}} =
GCODE.decode("R06 Z0 Q1")
assert {"1", {:report_calibration_state, [z: :home]}} =
GCODE.decode("R06 Z1 Q1")
assert {"1", {:report_calibration_state, [z: :end]}} =
GCODE.decode("R06 Z2 Q1")
assert "R06 Z0" =
GCODE.encode({nil, {:report_calibration_state, [z: :idle]}})
assert "R06 Z1" =
GCODE.encode({nil, {:report_calibration_state, [z: :home]}})
assert "R06 Z2" =
GCODE.encode({nil, {:report_calibration_state, [z: :end]}})
assert "R06 Z0 Q1" =
GCODE.encode({"1", {:report_calibration_state, [z: :idle]}})
assert "R06 Z1 Q1" =
GCODE.encode({"1", {:report_calibration_state, [z: :home]}})
assert "R06 Z2 Q1" =
GCODE.encode({"1", {:report_calibration_state, [z: :end]}})
end
test "retry" do
@ -241,14 +418,23 @@ defmodule FarmbotFirmware.GCODETest do
end
test "position change" do
assert {nil, {:report_position_change, [{:x, 200.0}]}} = GCODE.decode("R15 X200")
assert {"33", {:report_position_change, [{:x, 200.0}]}} = GCODE.decode("R15 X200 Q33")
assert {nil, {:report_position_change, [{:x, 200.0}]}} =
GCODE.decode("R15 X200")
assert {nil, {:report_position_change, [{:y, 200.0}]}} = GCODE.decode("R16 Y200")
assert {"33", {:report_position_change, [{:y, 200.0}]}} = GCODE.decode("R17 Y200 Q33")
assert {"33", {:report_position_change, [{:x, 200.0}]}} =
GCODE.decode("R15 X200 Q33")
assert {nil, {:report_position_change, [{:z, 200.0}]}} = GCODE.decode("R15 Z200")
assert {"33", {:report_position_change, [{:z, 200.0}]}} = GCODE.decode("R15 Z200 Q33")
assert {nil, {:report_position_change, [{:y, 200.0}]}} =
GCODE.decode("R16 Y200")
assert {"33", {:report_position_change, [{:y, 200.0}]}} =
GCODE.decode("R17 Y200 Q33")
assert {nil, {:report_position_change, [{:z, 200.0}]}} =
GCODE.decode("R15 Z200")
assert {"33", {:report_position_change, [{:z, 200.0}]}} =
GCODE.decode("R15 Z200 Q33")
end
test "parameter report complete" do
@ -268,7 +454,8 @@ defmodule FarmbotFirmware.GCODETest do
end
test "end stops" do
assert {nil, {:report_end_stops, [xa: 1, xb: 0, ya: 0, yb: 1, za: 1, zb: 0]}} =
assert {nil,
{:report_end_stops, [xa: 1, xb: 0, ya: 0, yb: 1, za: 1, zb: 0]}} =
GCODE.decode("R81 XA1 XB0 YA0 YB1 ZA1 ZB0")
end
@ -279,29 +466,41 @@ defmodule FarmbotFirmware.GCODETest do
assert {"1", {:report_position, [{:x, 100.0}, {:y, 200.0}, {:z, 400.0}]}} =
GCODE.decode("R82 X100 Y200 Z400 Q1")
assert {nil, {:report_position, [{:x, 100.0}, {:z, 12.0}]}} = GCODE.decode("R82 X100 Z12")
assert {nil, {:report_position, [{:x, 100.0}, {:z, 12.0}]}} =
GCODE.decode("R82 X100 Z12")
assert {nil, {:report_position, [{:z, 5.0}]}} = GCODE.decode("R82 Z5")
end
test "version" do
assert {nil, {:report_software_version, ["6.5.0.G"]}} = GCODE.decode("R83 6.5.0.G")
assert {"900", {:report_software_version, ["6.5.0.G"]}} = GCODE.decode("R83 6.5.0.G Q900")
assert {nil, {:report_software_version, ["6.5.0.G"]}} =
GCODE.decode("R83 6.5.0.G")
assert "R83 6.5.0.G" = GCODE.encode({nil, {:report_software_version, ["6.5.0.G"]}})
assert "R83 6.5.0.G Q900" = GCODE.encode({"900", {:report_software_version, ["6.5.0.G"]}})
assert {"900", {:report_software_version, ["6.5.0.G"]}} =
GCODE.decode("R83 6.5.0.G Q900")
assert "R83 6.5.0.G" =
GCODE.encode({nil, {:report_software_version, ["6.5.0.G"]}})
assert "R83 6.5.0.G Q900" =
GCODE.encode({"900", {:report_software_version, ["6.5.0.G"]}})
end
test "encoders" do
assert {nil, {:report_encoders_scaled, [{:x, 100.0}, {:y, 200.0}, {:z, 400.0}]}} =
assert {nil,
{:report_encoders_scaled, [{:x, 100.0}, {:y, 200.0}, {:z, 400.0}]}} =
GCODE.decode("R84 X100 Y200 Z400")
assert {"1", {:report_encoders_scaled, [{:x, 100.0}, {:y, 200.0}, {:z, 400.0}]}} =
assert {"1",
{:report_encoders_scaled, [{:x, 100.0}, {:y, 200.0}, {:z, 400.0}]}} =
GCODE.decode("R84 X100 Y200 Z400 Q1")
assert {nil, {:report_encoders_raw, [{:x, 100.0}, {:y, 200.0}, {:z, 400.0}]}} =
assert {nil,
{:report_encoders_raw, [{:x, 100.0}, {:y, 200.0}, {:z, 400.0}]}} =
GCODE.decode("R85 X100 Y200 Z400")
assert {"1", {:report_encoders_raw, [{:x, 100.0}, {:y, 200.0}, {:z, 400.0}]}} =
assert {"1",
{:report_encoders_raw, [{:x, 100.0}, {:y, 200.0}, {:z, 400.0}]}} =
GCODE.decode("R85 X100 Y200 Z400 Q1")
end
@ -314,8 +513,11 @@ defmodule FarmbotFirmware.GCODETest do
end
test "debug message" do
assert {nil, {:report_debug_message, ["Hello, World!"]}} = GCODE.decode("R99 Hello, World!")
assert "R99 Hello, World!" = GCODE.encode({nil, {:report_debug_message, ["Hello, World!"]}})
assert {nil, {:report_debug_message, ["Hello, World!"]}} =
GCODE.decode("R99 Hello, World!")
assert "R99 Hello, World!" =
GCODE.encode({nil, {:report_debug_message, ["Hello, World!"]}})
end
end
end

View File

@ -1,24 +1,29 @@
use Mix.Config
config :farmbot_core, FarmbotCore.AssetWorker.FarmbotCore.Asset.FarmEvent, checkup_time_ms: 10_000
config :farmbot_core, FarmbotCore.AssetWorker.FarmbotCore.Asset.FarmEvent,
checkup_time_ms: 10_000
config :farmbot_core, FarmbotCore.AssetWorker.FarmbotCore.Asset.RegimenInstance,
checkup_time_ms: 10_000
config :farmbot_core, FarmbotCore.AssetWorker.FarmbotCore.Asset.FarmwareInstallation,
error_retry_time_ms: 30_000,
install_dir: "/tmp/farmware"
config :farmbot_core,
FarmbotCore.AssetWorker.FarmbotCore.Asset.FarmwareInstallation,
error_retry_time_ms: 30_000,
install_dir: "/tmp/farmware"
config :farmbot_core, Elixir.FarmbotCore.AssetWorker.FarmbotCore.Asset.PublicKey,
ssh_handler: FarmbotCore.PublicKeyHandler.StubSSHHandler
config :farmbot_core,
Elixir.FarmbotCore.AssetWorker.FarmbotCore.Asset.PublicKey,
ssh_handler: FarmbotCore.PublicKeyHandler.StubSSHHandler
config :farmbot_core, FarmbotCore.AssetWorker.FarmbotCore.Asset.PinBinding,
gpio_handler: FarmbotCore.PinBindingWorker.StubGPIOHandler,
error_retry_time_ms: 30_000
config :farmbot_core, FarmbotCore.Leds, gpio_handler: FarmbotCore.Leds.StubHandler
config :farmbot_core, FarmbotCore.Leds,
gpio_handler: FarmbotCore.Leds.StubHandler
config :farmbot_core, FarmbotCore.JSON, json_parser: FarmbotCore.JSON.JasonParser
config :farmbot_core, FarmbotCore.JSON,
json_parser: FarmbotCore.JSON.JasonParser
# Customize non-Elixir parts of the firmware. See
# https://hexdocs.pm/nerves/advanced-configuration.html for details.
@ -36,25 +41,37 @@ config :farmbot_core, FarmbotCore.EctoMigrator,
default_ntp_server_2: "1.pool.ntp.org",
default_dns_name: "my.farm.bot",
default_currently_on_beta:
String.contains?(to_string(:os.cmd('git rev-parse --abbrev-ref HEAD')), "beta")
String.contains?(
to_string(:os.cmd('git rev-parse --abbrev-ref HEAD')),
"beta"
)
config :farmbot_celery_script, FarmbotCeleryScript.SysCalls, sys_calls: FarmbotOS.SysCalls
config :farmbot_celery_script, FarmbotCeleryScript.SysCalls,
sys_calls: FarmbotOS.SysCalls
config :farmbot_core, FarmbotCore.BotState.FileSystem,
root_dir: "/tmp/farmbot_state",
sleep_time: 200
config :farmbot_core, FarmbotCore.FarmwareRuntime, runtime_dir: "/tmp/farmware_runtime"
config :farmbot_core, FarmbotCore.FarmwareRuntime,
runtime_dir: "/tmp/farmware_runtime"
config :ecto, json_library: FarmbotCore.JSON
config :farmbot_core,
ecto_repos: [FarmbotCore.Config.Repo, FarmbotCore.Logger.Repo, FarmbotCore.Asset.Repo]
ecto_repos: [
FarmbotCore.Config.Repo,
FarmbotCore.Logger.Repo,
FarmbotCore.Asset.Repo
]
config :farmbot_ext, FarmbotExt.API.Preloader, preloader_impl: FarmbotExt.API.Preloader.HTTP
config :farmbot_ext, FarmbotExt.API.Preloader,
preloader_impl: FarmbotExt.API.Preloader.HTTP
config :farmbot, FarmbotOS.FileSystem, data_path: "/tmp/farmbot"
config :farmbot, FarmbotOS.System, system_tasks: FarmbotOS.Platform.Host.SystemTasks
config :farmbot, FarmbotOS.System,
system_tasks: FarmbotOS.Platform.Host.SystemTasks
config :farmbot, FarmbotOS.Configurator,
data_layer: FarmbotOS.Configurator.ConfigDataLayer,

View File

@ -30,7 +30,11 @@ config :farmbot, FarmbotOS.Init.Supervisor,
]
config :farmbot,
ecto_repos: [FarmbotCore.Config.Repo, FarmbotCore.Logger.Repo, FarmbotCore.Asset.Repo]
ecto_repos: [
FarmbotCore.Config.Repo,
FarmbotCore.Logger.Repo,
FarmbotCore.Asset.Repo
]
config :farmbot_core, FarmbotCore.FirmwareTTYDetector,
expected_names: [

View File

@ -22,7 +22,11 @@ config :farmbot_core, FarmbotCore.Asset.Repo,
database: Path.join(data_path, "asset-#{Mix.env()}.sqlite3")
config :farmbot,
ecto_repos: [FarmbotCore.Config.Repo, FarmbotCore.Logger.Repo, FarmbotCore.Asset.Repo],
ecto_repos: [
FarmbotCore.Config.Repo,
FarmbotCore.Logger.Repo,
FarmbotCore.Asset.Repo
],
platform_children: [
{Farmbot.Platform.Host.Configurator, []}
]

View File

@ -51,7 +51,13 @@ config :mdns_lite,
]
config :shoehorn,
init: [:nerves_runtime, :vintage_net, :nerves_firmware_ssh, :farmbot_core, :farmbot_ext],
init: [
:nerves_runtime,
:vintage_net,
:nerves_firmware_ssh,
:farmbot_core,
:farmbot_ext
],
handler: FarmbotOS.Platform.Target.ShoehornHandler,
app: :farmbot
@ -90,7 +96,8 @@ config :farmbot_core, FarmbotCore.Asset.Repo,
pool_size: 1,
database: Path.join(data_path, "asset-prod.sqlite3")
config :farmbot_telemetry, file: to_charlist(Path.join(data_path, 'farmbot-telemetry.dets'))
config :farmbot_telemetry,
file: to_charlist(Path.join(data_path, 'farmbot-telemetry.dets'))
config :farmbot, FarmbotOS.Platform.Supervisor,
platform_children: [
@ -104,7 +111,8 @@ config :farmbot, FarmbotOS.Platform.Supervisor,
config :farmbot, FarmbotOS.Configurator,
network_layer: FarmbotOS.Platform.Target.Configurator.VintageNetworkLayer
config :farmbot, FarmbotOS.System, system_tasks: FarmbotOS.Platform.Target.SystemTasks
config :farmbot, FarmbotOS.System,
system_tasks: FarmbotOS.Platform.Target.SystemTasks
config :nerves_hub,
client: FarmbotOS.Platform.Target.NervesHubClient,

View File

@ -51,7 +51,13 @@ config :mdns_lite,
]
config :shoehorn,
init: [:nerves_runtime, :vintage_net, :nerves_firmware_ssh, :farmbot_core, :farmbot_ext],
init: [
:nerves_runtime,
:vintage_net,
:nerves_firmware_ssh,
:farmbot_core,
:farmbot_ext
],
handler: FarmbotOS.Platform.Target.ShoehornHandler,
app: :farmbot
@ -90,7 +96,8 @@ config :farmbot_core, FarmbotCore.Asset.Repo,
pool_size: 1,
database: Path.join(data_path, "asset-#{Mix.env()}.sqlite3")
config :farmbot_telemetry, file: to_charlist(Path.join(data_path, 'farmbot-telemetry.dets'))
config :farmbot_telemetry,
file: to_charlist(Path.join(data_path, 'farmbot-telemetry.dets'))
config :farmbot, FarmbotOS.Platform.Supervisor,
platform_children: [
@ -104,7 +111,8 @@ config :farmbot, FarmbotOS.Platform.Supervisor,
config :farmbot, FarmbotOS.Configurator,
network_layer: FarmbotOS.Platform.Target.Configurator.VintageNetworkLayer
config :farmbot, FarmbotOS.System, system_tasks: FarmbotOS.Platform.Target.SystemTasks
config :farmbot, FarmbotOS.System,
system_tasks: FarmbotOS.Platform.Target.SystemTasks
config :nerves_hub,
client: FarmbotOS.Platform.Target.NervesHubClient,

View File

@ -1,8 +1,10 @@
use Mix.Config
config :farmbot_core, FarmbotCore.FirmwareTTYDetector, expected_names: ["ttyUSB0", "ttyAMA0"]
config :farmbot_core, FarmbotCore.FirmwareTTYDetector,
expected_names: ["ttyUSB0", "ttyAMA0"]
config :farmbot_firmware, FarmbotFirmware, reset: FarmbotOS.Platform.Target.FirmwareReset.GPIO
config :farmbot_firmware, FarmbotFirmware,
reset: FarmbotOS.Platform.Target.FirmwareReset.GPIO
config :farmbot, FarmbotOS.Init.Supervisor,
init_children: [

View File

@ -1,10 +1,12 @@
use Mix.Config
config :farmbot_core, FarmbotCore.FirmwareTTYDetector, expected_names: ["ttyUSB0", "ttyAMA0"]
config :farmbot_core, FarmbotCore.FirmwareTTYDetector,
expected_names: ["ttyUSB0", "ttyAMA0"]
config :farmbot_core, FarmbotCore.FirmwareOpenTask, attempt_threshold: 50
config :farmbot_firmware, FarmbotFirmware, reset: FarmbotOS.Platform.Target.FirmwareReset.GPIO
config :farmbot_firmware, FarmbotFirmware,
reset: FarmbotOS.Platform.Target.FirmwareReset.GPIO
config :farmbot, FarmbotOS.Init.Supervisor,
init_children: [

View File

@ -1,6 +1,7 @@
use Mix.Config
config :farmbot_core, FarmbotCore.FirmwareTTYDetector, expected_names: ["ttyUSB0", "ttyACM0"]
config :farmbot_core, FarmbotCore.FirmwareTTYDetector,
expected_names: ["ttyUSB0", "ttyACM0"]
config :farmbot_firmware, FarmbotFirmware, reset: FarmbotFirmware.NullReset

View File

@ -8,7 +8,11 @@ defmodule Avrdude.MuonTrapAdapter do
@doc false
def adapter do
Application.get_env(:farmbot, :muon_trap_adapter, Avrdude.MuonTrapDefaultAdapter)
Application.get_env(
:farmbot,
:muon_trap_adapter,
Avrdude.MuonTrapDefaultAdapter
)
end
def cmd(exe, args, options) do

View File

@ -55,9 +55,29 @@ defmodule FarmbotOS.Configurator.ConfigDataLayer do
_ = Config.input_network_config!(network_params)
# Runtime network configuration
_ = Config.update_config_value(:string, "settings", "default_ntp_server_1", net_config_ntp1)
_ = Config.update_config_value(:string, "settings", "default_ntp_server_2", net_config_ntp2)
_ = Config.update_config_value(:string, "settings", "default_dns_name", net_config_dns_name)
_ =
Config.update_config_value(
:string,
"settings",
"default_ntp_server_1",
net_config_ntp1
)
_ =
Config.update_config_value(
:string,
"settings",
"default_ntp_server_2",
net_config_ntp2
)
_ =
Config.update_config_value(
:string,
"settings",
"default_dns_name",
net_config_dns_name
)
if net_config_ssh_key do
Asset.new_public_key!(%{
@ -68,9 +88,31 @@ defmodule FarmbotOS.Configurator.ConfigDataLayer do
# Farmbot specific auth data
_ = Config.update_config_value(:string, "authorization", "secret", nil)
_ = Config.update_config_value(:string, "authorization", "email", auth_config_email)
_ = Config.update_config_value(:string, "authorization", "password", auth_config_password)
_ = Config.update_config_value(:string, "authorization", "server", auth_config_server)
_ =
Config.update_config_value(
:string,
"authorization",
"email",
auth_config_email
)
_ =
Config.update_config_value(
:string,
"authorization",
"password",
auth_config_password
)
_ =
Config.update_config_value(
:string,
"authorization",
"server",
auth_config_server
)
:ok
end

View File

@ -7,7 +7,10 @@ defmodule FarmbotOS.Configurator.FakeNetworkLayer do
@impl FarmbotOS.Configurator.NetworkLayer
def list_interfaces() do
[{"eth0", %{mac_address: "not real lol"}}, {"wlan0", %{mac_address: "even more not real"}}]
[
{"eth0", %{mac_address: "not real lol"}},
{"wlan0", %{mac_address: "even more not real"}}
]
end
@impl FarmbotOS.Configurator.NetworkLayer

View File

@ -27,7 +27,10 @@ defmodule FarmbotOS.Configurator.LoggerSocket.LoggerBackend do
end
@impl :gen_event
def handle_event({_level, _pid, {Logger, _msg, _timestamp, _meta}} = log, state) do
def handle_event(
{_level, _pid, {Logger, _msg, _timestamp, _meta}} = log,
state
) do
Registry.dispatch(__MODULE__, :dispatch, fn entries ->
for {pid, _} <- entries do
send(pid, log)

View File

@ -10,14 +10,24 @@ defmodule FarmbotOS.Configurator.Router do
plug(Plug.Logger)
plug(Plug.Static, from: {:farmbot, "priv/static"}, at: "/")
plug(Plug.Parsers, parsers: [:urlencoded, :multipart])
plug(Plug.Session, store: :ets, key: "_farmbot_session", table: :configurator_session)
plug(Plug.Session,
store: :ets,
key: "_farmbot_session",
table: :configurator_session
)
plug(:fetch_session)
plug(:match)
plug(:dispatch)
@data_layer Application.get_env(:farmbot, FarmbotOS.Configurator)[:data_layer]
@network_layer Application.get_env(:farmbot, FarmbotOS.Configurator)[:network_layer]
@telemetry_layer Application.get_env(:farmbot, FarmbotOS.Configurator)[:telemetry_layer]
@network_layer Application.get_env(:farmbot, FarmbotOS.Configurator)[
:network_layer
]
@telemetry_layer Application.get_env(:farmbot, FarmbotOS.Configurator)[
:telemetry_layer
]
# Trigger for captive portal for various operating systems
get("/gen_204", do: redir(conn, "/"))
@ -49,7 +59,10 @@ defmodule FarmbotOS.Configurator.Router do
if String.contains?(reason, "CeleryScript request.") do
redir(conn, "/network")
else
render_page(conn, "index", version: version(), last_reset_reason: reason)
render_page(conn, "index",
version: version(),
last_reset_reason: reason
)
end
nil ->
@ -246,7 +259,10 @@ defmodule FarmbotOS.Configurator.Router do
_ ->
conn
|> put_session("__error", "Email, Server, or Password are missing or invalid")
|> put_session(
"__error",
"Email, Server, or Password are missing or invalid"
)
|> redir("/credentials")
end
end
@ -280,20 +296,34 @@ defmodule FarmbotOS.Configurator.Router do
defp render_page(conn, page, info \\ []) do
page
|> template_file()
|> EEx.eval_file(Keyword.merge([version: version()], info), engine: Phoenix.HTML.Engine)
|> EEx.eval_file(Keyword.merge([version: version()], info),
engine: Phoenix.HTML.Engine
)
|> (fn {:safe, contents} -> send_resp(conn, 200, contents) end).()
rescue
e ->
IO.warn("render error", __STACKTRACE__)
send_resp(conn, 500, "Failed to render page: #{page} error: #{Exception.message(e)}")
send_resp(
conn,
500,
"Failed to render page: #{page} error: #{Exception.message(e)}"
)
end
defp render_json(conn, data) do
conn = put_resp_header(conn, "content-type", "application/json")
case FarmbotCore.JSON.encode(data) do
{:ok, json} -> send_resp(conn, 200, json)
_ -> send_resp(conn, 501, FarmbotCore.JSON.encode!(%{error: "failed to render json"}))
{:ok, json} ->
send_resp(conn, 200, json)
_ ->
send_resp(
conn,
501,
FarmbotCore.JSON.encode!(%{error: "failed to render json"})
)
end
end

View File

@ -38,18 +38,25 @@ defmodule FarmbotOS.Configurator.SchedulerSocket do
def websocket_info({FarmbotCeleryScript, {:calendar, calendar}}, state) do
data =
Enum.map(calendar, fn
%Scheduler.Dispatch{data: %FarmEvent{} = farm_event, scheduled_at: datetime} ->
%Scheduler.Dispatch{
data: %FarmEvent{} = farm_event,
scheduled_at: datetime
} ->
json =
Jason.encode!(%{
id: farm_event.local_id,
type: "FarmEvent",
data: Sequence.render(Asset.get_sequence(farm_event.executable_id)),
data:
Sequence.render(Asset.get_sequence(farm_event.executable_id)),
at: datetime
})
{:text, json}
%Scheduler.Dispatch{data: %Sequence{} = sequence, scheduled_at: datetime} ->
%Scheduler.Dispatch{
data: %Sequence{} = sequence,
scheduled_at: datetime
} ->
json =
Jason.encode!(%{
id: sequence.local_id,

View File

@ -13,7 +13,11 @@ defmodule FarmbotOS.Configurator.Supervisor do
@impl Supervisor
def init(_args) do
:ets.new(:configurator_session, [:named_table, :public, read_concurrency: true])
:ets.new(:configurator_session, [
:named_table,
:public,
read_concurrency: true
])
transport_opts = [
num_acceptors: 1

View File

@ -20,11 +20,17 @@ defmodule LoggerBackendEspeak do
end
@impl :gen_event
def handle_event({_level, _pid, {Logger, _msg, _timestamp, _meta}}, %{port: nil} = state) do
def handle_event(
{_level, _pid, {Logger, _msg, _timestamp, _meta}},
%{port: nil} = state
) do
{:ok, state}
end
def handle_event({_level, _pid, {Logger, msg, _timestamp, meta}}, %{port: espeak} = state) do
def handle_event(
{_level, _pid, {Logger, msg, _timestamp, meta}},
%{port: espeak} = state
) do
should_espeak? =
meta[:channels] == :espeak ||
Enum.find(meta[:channels] || [], fn
@ -50,7 +56,9 @@ defmodule LoggerBackendEspeak do
end
def handle_info({:open, exe}, state) do
port = :erlang.open_port({:spawn_executable, to_charlist(exe)}, [:exit_status])
port =
:erlang.open_port({:spawn_executable, to_charlist(exe)}, [:exit_status])
Port.command(port, "\n")
{:ok, %{state | port: port}}
end

View File

@ -56,7 +56,10 @@ defmodule FarmbotOS.Lua do
channels: [:toast]
)
{:error, "failed to parse expression (line:#{line}): #{IO.iodata_to_binary(parse_error)}"}
{:error,
"failed to parse expression (line:#{line}): #{
IO.iodata_to_binary(parse_error)
}"}
error ->
error
@ -88,8 +91,14 @@ defmodule FarmbotOS.Lua do
|> set_table([:get_device], &DataManipulation.get_device/2)
|> set_table([:update_fbos_config], &DataManipulation.update_fbos_config/2)
|> set_table([:get_fbos_config], &DataManipulation.get_fbos_config/2)
|> set_table([:update_firmware_config], &DataManipulation.update_firmware_config/2)
|> set_table([:get_firmware_config], &DataManipulation.get_firmware_config/2)
|> set_table(
[:update_firmware_config],
&DataManipulation.update_firmware_config/2
)
|> set_table(
[:get_firmware_config],
&DataManipulation.get_firmware_config/2
)
|> set_table([:new_farmware_env], &DataManipulation.new_farmware_env/2)
|> set_table([:new_sensor_reading], &DataManipulation.new_sensor_reading/2)
end

View File

@ -56,7 +56,8 @@ defmodule FarmbotOS.Lua.Ext.Firmware do
end
@doc "Moves in a straight line to a location"
def move_absolute([x, y, z], lua) when is_number(x) and is_number(y) and is_number(z) do
def move_absolute([x, y, z], lua)
when is_number(x) and is_number(y) and is_number(z) do
move_absolute([x, y, z, 100], lua)
end
@ -75,7 +76,8 @@ defmodule FarmbotOS.Lua.Ext.Firmware do
move_absolute([table, 100], lua)
end
def move_absolute([table, speed], lua) when is_list(table) and is_number(speed) do
def move_absolute([table, speed], lua)
when is_list(table) and is_number(speed) do
axis_finder = fn
axis, {axis, nil} -> apply(SysCalls, :"get_current_#{axis}", [])
axis, {axis, value} -> value
@ -199,7 +201,8 @@ defmodule FarmbotOS.Lua.Ext.Firmware do
end
end
def coordinate([x, y, z], lua) when is_number(x) and is_number(y) and is_number(z) do
def coordinate([x, y, z], lua)
when is_number(x) and is_number(y) and is_number(z) do
{[[{"x", x}, {"y", y}, {"z", z}]], lua}
end

View File

@ -10,7 +10,9 @@ defmodule FarmbotOS.Platform.Supervisor do
end
def init([]) do
platform_children = Application.get_env(:farmbot, __MODULE__)[:platform_children]
platform_children =
Application.get_env(:farmbot, __MODULE__)[:platform_children]
Supervisor.init(platform_children, strategy: :one_for_all)
end
end

View File

@ -262,18 +262,26 @@ defmodule FarmbotOS.SysCalls do
with {:ok, sync_changeset} <- API.get_changeset(Sync),
:ok <- BotState.set_sync_status("syncing"),
_ <- Leds.green(:really_fast_blink),
sync_changeset <- Reconciler.sync_group(sync_changeset, SyncGroup.group_0()),
sync_changeset <- Reconciler.sync_group(sync_changeset, SyncGroup.group_1()),
sync_changeset <- Reconciler.sync_group(sync_changeset, SyncGroup.group_2()),
sync_changeset <- Reconciler.sync_group(sync_changeset, SyncGroup.group_3()),
_sync_changeset <- Reconciler.sync_group(sync_changeset, SyncGroup.group_4()) do
sync_changeset <-
Reconciler.sync_group(sync_changeset, SyncGroup.group_0()),
sync_changeset <-
Reconciler.sync_group(sync_changeset, SyncGroup.group_1()),
sync_changeset <-
Reconciler.sync_group(sync_changeset, SyncGroup.group_2()),
sync_changeset <-
Reconciler.sync_group(sync_changeset, SyncGroup.group_3()),
_sync_changeset <-
Reconciler.sync_group(sync_changeset, SyncGroup.group_4()) do
FarmbotCore.Logger.success(3, "Synced")
:ok = BotState.set_sync_status("synced")
_ = Leds.green(:solid)
:ok
else
error ->
FarmbotTelemetry.event(:asset_sync, :sync_error, nil, error: inspect(error))
FarmbotTelemetry.event(:asset_sync, :sync_error, nil,
error: inspect(error)
)
:ok = BotState.set_sync_status("sync_error")
_ = Leds.green(:slow_blink)
{:error, inspect(error)}

View File

@ -12,12 +12,21 @@ defmodule FarmbotOS.SysCalls.ChangeOwnership do
case Authorization.authorize_with_secret(email, secret, server) do
{:ok, _token} ->
FarmbotCore.Logger.warn(1, "Farmbot is changing ownership to #{email} - #{server}")
FarmbotCore.Logger.warn(
1,
"Farmbot is changing ownership to #{email} - #{server}"
)
:ok = replace_credentials(email, secret, server)
_ = clean_assets()
_ = clean_farmwares()
FarmbotCore.Logger.warn(1, "Going down for reboot")
Supervisor.start_child(:elixir_sup, {Task, &FarmbotOS.System.soft_restart/0})
Supervisor.start_child(
:elixir_sup,
{Task, &FarmbotOS.System.soft_restart/0}
)
:ok
{:error, _} ->

View File

@ -3,7 +3,14 @@ defmodule FarmbotOS.SysCalls.DumpInfo do
require FarmbotCore.Logger
require FarmbotTelemetry
alias FarmbotCore.{Asset, Asset.DiagnosticDump, Asset.Private, Config, Project}
alias FarmbotCore.{
Asset,
Asset.DiagnosticDump,
Asset.Private,
Config,
Project
}
def dump_info do
FarmbotCore.Logger.busy(1, "Recording diagnostic dump.")
@ -38,7 +45,10 @@ defmodule FarmbotOS.SysCalls.DumpInfo do
:ok
{:error, changeset} ->
FarmbotTelemetry.event(:diagnostic_dump, :record_error, nil, error: inspect(changeset))
FarmbotTelemetry.event(:diagnostic_dump, :record_error, nil,
error: inspect(changeset)
)
{:error, "error creating diagnostic dump: #{inspect(changeset)}"}
end
end

View File

@ -6,7 +6,12 @@ defmodule FarmbotOS.SysCalls.FactoryReset do
def factory_reset("farmbot_os") do
_ = API.put!(API.client(), "/api/device", %{needs_reset: false})
FarmbotOS.System.factory_reset("Factory reset requested by Sequence or frontend", true)
FarmbotOS.System.factory_reset(
"Factory reset requested by Sequence or frontend",
true
)
:ok
end

View File

@ -4,18 +4,29 @@ defmodule FarmbotOS.SysCalls.FlashFirmware do
alias FarmbotCore.{Asset, Asset.Private}
alias FarmbotFirmware
alias FarmbotCore.FirmwareTTYDetector
import FarmbotFirmware.PackageUtils, only: [find_hex_file: 1, package_to_string: 1]
import FarmbotFirmware.PackageUtils,
only: [find_hex_file: 1, package_to_string: 1]
require FarmbotCore.Logger
require Logger
def flash_firmware(package) do
FarmbotCore.Logger.busy(2, "Flashing #{package_to_string(package)} firmware")
FarmbotCore.Logger.busy(
2,
"Flashing #{package_to_string(package)} firmware"
)
with {:ok, hex_file} <- find_hex_file(package),
{:ok, tty} <- find_tty(),
_ <- FarmbotCore.Logger.debug(3, "found tty: #{tty} for firmware flash"),
_ <-
FarmbotCore.Logger.debug(3, "found tty: #{tty} for firmware flash"),
{:ok, fun} <- find_reset_fun(package),
_ <- FarmbotCore.Logger.debug(3, "closing firmware transport before flash"),
_ <-
FarmbotCore.Logger.debug(
3,
"closing firmware transport before flash"
),
:ok <- FarmbotFirmware.close_transport(),
_ <- FarmbotCore.Logger.debug(3, "starting firmware flash"),
{_, 0} <- Avrdude.flash(hex_file, tty, fun) do

View File

@ -119,7 +119,11 @@ defmodule FarmbotOS.SysCalls.Movement do
{:error, "emergency_lock"}
{:error, reason} ->
FarmbotCore.Logger.error(1, "Movement failed. Retrying up to #{retries} more time(s)")
FarmbotCore.Logger.error(
1,
"Movement failed. Retrying up to #{retries} more time(s)"
)
do_move_absolute(x, y, z, speed, retries - 1, [reason | errors])
end
end

View File

@ -22,7 +22,8 @@ defmodule FarmbotOS.SysCalls.PinControl do
def toggle_pin(pin_number) when is_number(pin_number) do
peripheral = Asset.get_peripheral_by_pin(pin_number)
with :ok <- FarmbotFirmware.command({:pin_mode_write, [p: pin_number, m: 1]}) do
with :ok <-
FarmbotFirmware.command({:pin_mode_write, [p: pin_number, m: 1]}) do
case FarmbotFirmware.request({:pin_read, [p: pin_number, m: 0]}) do
{:ok, {_, {:report_pin_value, [p: _, v: 1]}}} ->
do_toggle_pin(peripheral || pin_number, 0)
@ -53,7 +54,10 @@ defmodule FarmbotOS.SysCalls.PinControl do
end
defp do_toggle_pin(%Peripheral{pin: pin_number} = data, value) do
with :ok <- FarmbotFirmware.command({:pin_write, [p: pin_number, v: value, m: 0]}),
with :ok <-
FarmbotFirmware.command(
{:pin_write, [p: pin_number, v: value, m: 0]}
),
value when is_number(value) <- do_read_pin(data, 0) do
:ok
else
@ -63,7 +67,10 @@ defmodule FarmbotOS.SysCalls.PinControl do
end
defp do_toggle_pin(pin_number, value) do
with :ok <- FarmbotFirmware.command({:pin_write, [p: pin_number, v: value, m: 0]}),
with :ok <-
FarmbotFirmware.command(
{:pin_write, [p: pin_number, v: value, m: 0]}
),
value when is_number(value) <- do_read_pin(pin_number, 0) do
:ok
else
@ -121,19 +128,32 @@ defmodule FarmbotOS.SysCalls.PinControl do
# digital peripheral
defp do_read_pin(%Peripheral{pin: pin_number, label: label}, 0) when is_number(pin_number) do
defp do_read_pin(%Peripheral{pin: pin_number, label: label}, 0)
when is_number(pin_number) do
case FarmbotFirmware.request({:pin_read, [p: pin_number, m: 0]}) do
{:ok, {_, {:report_pin_value, [p: _, v: 1]}}} ->
FarmbotCore.Logger.info(2, "The #{label} peripheral value is ON (digital)")
FarmbotCore.Logger.info(
2,
"The #{label} peripheral value is ON (digital)"
)
1
{:ok, {_, {:report_pin_value, [p: _, v: 0]}}} ->
FarmbotCore.Logger.info(2, "The #{label} peripheral value is OFF (digital)")
FarmbotCore.Logger.info(
2,
"The #{label} peripheral value is OFF (digital)"
)
0
# Just in case
{:ok, {_, {:report_pin_value, [p: _, v: value]}}} ->
FarmbotCore.Logger.info(2, "The #{label} peripheral value is #{value} (analog)")
FarmbotCore.Logger.info(
2,
"The #{label} peripheral value is #{value} (analog)"
)
value
{:error, reason} ->
@ -143,10 +163,15 @@ defmodule FarmbotOS.SysCalls.PinControl do
# analog peripheral
defp do_read_pin(%Peripheral{pin: pin_number, label: label}, 1) when is_number(pin_number) do
defp do_read_pin(%Peripheral{pin: pin_number, label: label}, 1)
when is_number(pin_number) do
case FarmbotFirmware.request({:pin_read, [p: pin_number, m: 1]}) do
{:ok, {_, {:report_pin_value, [p: _, v: value]}}} ->
FarmbotCore.Logger.info(2, "The #{label} peripheral value is #{value} (analog)")
FarmbotCore.Logger.info(
2,
"The #{label} peripheral value is #{value} (analog)"
)
value
{:error, reason} ->
@ -156,7 +181,8 @@ defmodule FarmbotOS.SysCalls.PinControl do
# digital sensor
defp do_read_pin(%Sensor{pin: pin_number, label: label}, 0) when is_number(pin_number) do
defp do_read_pin(%Sensor{pin: pin_number, label: label}, 0)
when is_number(pin_number) do
case FarmbotFirmware.request({:pin_read, [p: pin_number, m: 0]}) do
{:ok, {_, {:report_pin_value, [p: _, v: 1]}}} ->
FarmbotCore.Logger.info(2, "The #{label} sensor value is 1 (digital)")
@ -167,7 +193,10 @@ defmodule FarmbotOS.SysCalls.PinControl do
0
{:ok, {_, {:report_pin_value, [p: _, v: value]}}} ->
FarmbotCore.Logger.info(2, "The #{label} sensor value is #{value} (analog)")
FarmbotCore.Logger.info(
2,
"The #{label} sensor value is #{value} (analog)"
)
{:error, reason} ->
{:error, "Firmware error: #{inspect(reason)}"}
@ -176,10 +205,15 @@ defmodule FarmbotOS.SysCalls.PinControl do
# analog sensor
defp do_read_pin(%Sensor{pin: pin_number, label: label}, 1) when is_number(pin_number) do
defp do_read_pin(%Sensor{pin: pin_number, label: label}, 1)
when is_number(pin_number) do
case FarmbotFirmware.request({:pin_read, [p: pin_number, m: 1]}) do
{:ok, {_, {:report_pin_value, [p: _, v: value]}}} ->
FarmbotCore.Logger.info(2, "The #{label} sensor value is #{value} (analog)")
FarmbotCore.Logger.info(
2,
"The #{label} sensor value is #{value} (analog)"
)
value
{:error, reason} ->
@ -188,7 +222,8 @@ defmodule FarmbotOS.SysCalls.PinControl do
end
# Catches unsupplied `mode`
defp do_read_pin(%type{mode: mode} = peripheral, nil) when type in [Peripheral, Sensor] do
defp do_read_pin(%type{mode: mode} = peripheral, nil)
when type in [Peripheral, Sensor] do
do_read_pin(peripheral, mode)
end
@ -284,7 +319,9 @@ defmodule FarmbotOS.SysCalls.PinControl do
end
def do_write_pin(pin_number, mode, value) do
case FarmbotFirmware.command({:pin_write, [p: pin_number, v: value, m: mode]}) do
case FarmbotFirmware.command(
{:pin_write, [p: pin_number, v: value, m: mode]}
) do
:ok ->
:ok

View File

@ -40,8 +40,12 @@ defmodule FarmbotOS.SysCalls.ResourceUpdate do
_ = Private.mark_dirty!(point)
:ok
else
nil -> {:error, "#{type}.#{id} is not currently synced, so it could not be updated"}
{:error, _changeset} -> {:error, "Failed to update #{type}.#{id}"}
nil ->
{:error,
"#{type}.#{id} is not currently synced, so it could not be updated"}
{:error, _changeset} ->
{:error, "Failed to update #{type}.#{id}"}
end
end
@ -54,7 +58,10 @@ defmodule FarmbotOS.SysCalls.ResourceUpdate do
{key, rendered}
_ ->
Logger.warn("failed to render #{key} => #{value} for resource_update")
Logger.warn(
"failed to render #{key} => #{value} for resource_update"
)
{key, value}
end

View File

@ -88,6 +88,7 @@ defmodule Mix.Tasks.Farmbot.Env do
@doc false
def slack_token do
System.get_env("SLACK_TOKEN") || Mix.raise("No $SLACK_TOKEN environment variable.")
System.get_env("SLACK_TOKEN") ||
Mix.raise("No $SLACK_TOKEN environment variable.")
end
end

View File

@ -21,7 +21,11 @@ defmodule Mix.Tasks.Farmbot.InjectSshKey do
[key]
])
{:ok, _} = :rpc.call(farmbot_node, Application, :ensure_all_started, [:nerves_firmware_ssh])
{:ok, _} =
:rpc.call(farmbot_node, Application, :ensure_all_started, [
:nerves_firmware_ssh
])
:ok = :rpc.call(farmbot_node, GenServer, :stop, [Farmbot.Target.SSHConsole])
end
end

View File

@ -17,7 +17,8 @@ defmodule Mix.Tasks.Farmbot.Firmware.Reload do
true = Node.connect(farmbot_node)
for module <- mods do
{:ok, [{^farmbot_node, :loaded, ^module}]} = IEx.Helpers.nl([farmbot_node], module)
{:ok, [{^farmbot_node, :loaded, ^module}]} =
IEx.Helpers.nl([farmbot_node], module)
end
end

View File

@ -20,7 +20,9 @@ defmodule Mix.Tasks.Farmbot.Firmware.Sign do
@dialyzer {[:no_return], [error: 0]}
defp error do
Mix.raise("Usage: mix farmbot.firmware.sign /path/to/private/key [input.fw] [output.fw]")
Mix.raise(
"Usage: mix farmbot.firmware.sign /path/to/private/key [input.fw] [output.fw]"
)
end
defp do_run(private_key_file, input_fw_file, output_fw_file) do
@ -37,6 +39,14 @@ defmodule Mix.Tasks.Farmbot.Firmware.Sign do
"Signing: #{input_fw_file} with: #{private_key_file} to: #{output_fw_file}"
])
System.cmd("fwup", ["-S", "-s", private_key_file, "-i", input_fw_file, "-o", output_fw_file])
System.cmd("fwup", [
"-S",
"-s",
private_key_file,
"-i",
input_fw_file,
"-o",
output_fw_file
])
end
end

View File

@ -2,12 +2,21 @@ defmodule FarmbotOS.MixProject do
use Mix.Project
@all_targets [:rpi3, :rpi]
@version Path.join([__DIR__, "..", "VERSION"]) |> File.read!() |> String.trim()
@branch System.cmd("git", ~w"rev-parse --abbrev-ref HEAD") |> elem(0) |> String.trim()
@commit System.cmd("git", ~w"rev-parse --verify HEAD") |> elem(0) |> String.trim()
@version Path.join([__DIR__, "..", "VERSION"])
|> File.read!()
|> String.trim()
@branch System.cmd("git", ~w"rev-parse --abbrev-ref HEAD")
|> elem(0)
|> String.trim()
@commit System.cmd("git", ~w"rev-parse --verify HEAD")
|> elem(0)
|> String.trim()
System.put_env("NERVES_FW_VCS_IDENTIFIER", @commit)
System.put_env("NERVES_FW_MISC", @branch)
@elixir_version Path.join([__DIR__, "..", "ELIXIR_VERSION"]) |> File.read!() |> String.trim()
@elixir_version Path.join([__DIR__, "..", "ELIXIR_VERSION"])
|> File.read!()
|> String.trim()
System.put_env("NERVES_FW_VCS_IDENTIFIER", @commit)
@ -93,7 +102,8 @@ defmodule FarmbotOS.MixProject do
# Host/test only dependencies.
{:excoveralls, "~> 0.10", only: [:test], targets: [:host]},
{:dialyxir, "~> 1.0.0-rc.3", only: [:dev], targets: [:host], runtime: false},
{:dialyxir, "~> 1.0.0-rc.3",
only: [:dev], targets: [:host], runtime: false},
{:ex_doc, "~> 0.21.2", only: [:dev], targets: [:host], runtime: false},
{:elixir_make, "~> 0.6", runtime: false},
@ -114,7 +124,8 @@ defmodule FarmbotOS.MixProject do
{:vintage_net_direct, "~> 0.7.0", targets: @all_targets},
{:mdns_lite, "~> 0.6.1", targets: @all_targets},
{:busybox, "~> 0.1.4", targets: @all_targets},
{:farmbot_system_rpi3, "1.10.0-farmbot.1", runtime: false, targets: :rpi3},
{:farmbot_system_rpi3, "1.10.0-farmbot.1",
runtime: false, targets: :rpi3},
{:farmbot_system_rpi, "1.10.0-farmbot.1", runtime: false, targets: :rpi}
]
end

View File

@ -45,7 +45,10 @@ defmodule FarmbotOS.Platform.Target.Configurator.CaptivePortal do
VintageNetWiFi.ioctl(ifname, ioctl, args)
end
defp dnsmasq(%{ifname: ifname, source_config: %{dnsmasq: config}} = raw_config, opts) do
defp dnsmasq(
%{ifname: ifname, source_config: %{dnsmasq: config}} = raw_config,
opts
) do
tmpdir = Keyword.fetch!(opts, :tmpdir)
killall = Keyword.fetch!(opts, :bin_killall)
dnsmasq = System.find_executable("dnsmasq")
@ -93,7 +96,8 @@ defmodule FarmbotOS.Platform.Target.Configurator.CaptivePortal do
up_cmds: raw_config.up_cmds ++ up_cmds,
down_cmds: raw_config.down_cmds ++ down_cmds,
cleanup_files:
raw_config.cleanup_files ++ [dnsmasq_conf_path, dnsmasq_lease_file, dnsmasq_pid_file]
raw_config.cleanup_files ++
[dnsmasq_conf_path, dnsmasq_lease_file, dnsmasq_pid_file]
}
updated_raw_config

View File

@ -90,7 +90,11 @@ defmodule FarmbotOS.Platform.Target.Configurator.Validator do
%{method: :dhcp}
end
defp to_vintage_net_wifi(%{security: "NONE", ssid: ssid, regulatory_domain: reg_domain}) do
defp to_vintage_net_wifi(%{
security: "NONE",
ssid: ssid,
regulatory_domain: reg_domain
}) do
%{
networks: [
%{

View File

@ -23,7 +23,12 @@ defmodule FarmbotOS.Platform.Target.Configurator.VintageNetworkLayer do
def scan(ifname) do
{:ok, aps} = do_scan(ifname)
Enum.map(aps, fn %{bssid: bssid, ssid: ssid, signal_percent: signal, flags: flags} ->
Enum.map(aps, fn %{
bssid: bssid,
ssid: ssid,
signal_percent: signal,
flags: flags
} ->
%{
ssid: ssid,
bssid: bssid,

View File

@ -13,7 +13,9 @@ defmodule FarmbotOS.Platform.Target.PinBindingWorker.CircuitsGPIOHandler do
end
def terminate(reason, state) do
Logger.warn("CircuitsGPIOHandler #{state.pin_number} crash: #{inspect(reason)}")
Logger.warn(
"CircuitsGPIOHandler #{state.pin_number} crash: #{inspect(reason)}"
)
end
def init([pin_number, fun]) do
@ -31,7 +33,10 @@ defmodule FarmbotOS.Platform.Target.PinBindingWorker.CircuitsGPIOHandler do
{:noreply, %{state | debounce: nil}}
end
def handle_info({:circuits_gpio, pin, _timestamp, _}, %{debounce: timer} = state)
def handle_info(
{:circuits_gpio, pin, _timestamp, _},
%{debounce: timer} = state
)
when is_reference(timer) do
left = Process.read_timer(timer)
Logger.info("CircuitsGPIOHandler #{pin} still debounced for #{left} ms")
@ -46,5 +51,6 @@ defmodule FarmbotOS.Platform.Target.PinBindingWorker.CircuitsGPIOHandler do
def name(pin_number), do: :"#{__MODULE__}.#{pin_number}"
defp debounce_timer, do: Process.send_after(self(), :timeout, @debounce_timeout_ms)
defp debounce_timer,
do: Process.send_after(self(), :timeout, @debounce_timeout_ms)
end

View File

@ -60,7 +60,17 @@ defmodule FarmbotOS.Platform.Target.Leds.CircuitsHandler do
end
def init([]) do
leds = [:red, :blue, :green, :yellow, :white1, :white2, :white3, :white4, :white5]
leds = [
:red,
:blue,
:green,
:yellow,
:white1,
:white2,
:white3,
:white4,
:white5
]
state =
Map.new(leds, fn color ->
@ -78,7 +88,12 @@ defmodule FarmbotOS.Platform.Target.Leds.CircuitsHandler do
:ok = cancel_timer(state[color].blink_timer)
{:reply, :ok,
update_color(state, color, %{state[color] | state: 0, blink_timer: nil, status: :off})}
update_color(state, color, %{
state[color]
| state: 0,
blink_timer: nil,
status: :off
})}
end
def handle_call({color, :solid}, _from, state) do
@ -86,28 +101,46 @@ defmodule FarmbotOS.Platform.Target.Leds.CircuitsHandler do
:ok = cancel_timer(state[color].blink_timer)
{:reply, :ok,
update_color(state, color, %{state[color] | state: 1, blink_timer: nil, status: :off})}
update_color(state, color, %{
state[color]
| state: 1,
blink_timer: nil,
status: :off
})}
end
def handle_call({color, :slow_blink}, _from, state) do
timer = restart_timer(state[color].blink_timer, color, @slow_blink_speed)
{:reply, :ok,
update_color(state, color, %{state[color] | blink_timer: timer, status: :slow_blink})}
update_color(state, color, %{
state[color]
| blink_timer: timer,
status: :slow_blink
})}
end
def handle_call({color, :fast_blink}, _from, state) do
timer = restart_timer(state[color].blink_timer, color, @fast_blink_speed)
{:reply, :ok,
update_color(state, color, %{state[color] | blink_timer: timer, status: :fast_blink})}
update_color(state, color, %{
state[color]
| blink_timer: timer,
status: :fast_blink
})}
end
def handle_call({color, :really_fast_blink}, _from, state) do
timer = restart_timer(state[color].blink_timer, color, @really_fast_blink_speed)
timer =
restart_timer(state[color].blink_timer, color, @really_fast_blink_speed)
{:reply, :ok,
update_color(state, color, %{state[color] | blink_timer: timer, status: :really_fast_blink})}
update_color(state, color, %{
state[color]
| blink_timer: timer,
status: :really_fast_blink
})}
end
def handle_info({:blink_timer, color}, state) do
@ -116,21 +149,45 @@ defmodule FarmbotOS.Platform.Target.Leds.CircuitsHandler do
%{status: :slow_blink} ->
new_led_state = invert(state[color].state)
:ok = GPIO.write(state[color].ref, new_led_state)
timer = restart_timer(state[color].blink_timer, color, @slow_blink_speed)
n = %{state[color] | state: new_led_state, blink_timer: timer, status: :slow_blink}
timer =
restart_timer(state[color].blink_timer, color, @slow_blink_speed)
n = %{
state[color]
| state: new_led_state,
blink_timer: timer,
status: :slow_blink
}
update_color(state, color, n)
%{status: :fast_blink} ->
new_led_state = invert(state[color].state)
:ok = GPIO.write(state[color].ref, new_led_state)
timer = restart_timer(state[color].blink_timer, color, @fast_blink_speed)
n = %{state[color] | state: new_led_state, blink_timer: timer, status: :fast_blink}
timer =
restart_timer(state[color].blink_timer, color, @fast_blink_speed)
n = %{
state[color]
| state: new_led_state,
blink_timer: timer,
status: :fast_blink
}
update_color(state, color, n)
%{status: :really_fast_blink} ->
new_led_state = invert(state[color].state)
:ok = GPIO.write(state[color].ref, new_led_state)
timer = restart_timer(state[color].blink_timer, color, @really_fast_blink_speed)
timer =
restart_timer(
state[color].blink_timer,
color,
@really_fast_blink_speed
)
n = %{
state[color]

View File

@ -22,7 +22,8 @@ defmodule FarmbotOS.Platform.Target.InfoWorker.Throttle do
@impl GenServer
def handle_info(:timeout, state) do
{throttled_str, 0} = Nerves.Runtime.cmd("vcgencmd", ["get_throttled"], :return)
{throttled_str, 0} =
Nerves.Runtime.cmd("vcgencmd", ["get_throttled"], :return)
throttled =
throttled_str

View File

@ -44,7 +44,8 @@ defmodule FarmbotOS.Platform.Target.InfoWorker.WifiLevel do
end
def handle_info(
{VintageNet, ["interface", _, "addresses"], _old, [%{address: address} | _], _meta},
{VintageNet, ["interface", _, "addresses"], _old,
[%{address: address} | _], _meta},
state
) do
FarmbotCore.BotState.set_private_ip(to_string(:inet.ntoa(address)))
@ -52,7 +53,8 @@ defmodule FarmbotOS.Platform.Target.InfoWorker.WifiLevel do
end
def handle_info(
{VintageNet, ["interface", "wlan0", "wifi", "access_points"], _, new, _meta},
{VintageNet, ["interface", "wlan0", "wifi", "access_points"], _, new,
_meta},
%{ssid: ssid} = state
)
when is_binary(ssid) do

View File

@ -94,10 +94,12 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
def uuid, do: Nerves.Runtime.KV.get_active("nerves_fw_uuid")
@doc "Loads the cert from storage"
def load_cert, do: Nerves.Runtime.KV.get_active("nerves_hub_cert") |> filter_parens()
def load_cert,
do: Nerves.Runtime.KV.get_active("nerves_hub_cert") |> filter_parens()
@doc "Loads the key from storage"
def load_key, do: Nerves.Runtime.KV.get_active("nerves_hub_key") |> filter_parens()
def load_key,
do: Nerves.Runtime.KV.get_active("nerves_hub_key") |> filter_parens()
@doc false
def write_serial(serial_number) do
@ -143,8 +145,14 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
try do
# NervesHub replaces it's own env on startup. Reset it.
# Stop Nerves Hub if it is running.
_ = Supervisor.terminate_child(FarmbotOS.Init.Supervisor, NervesHub.Supervisor)
_ = Supervisor.delete_child(FarmbotOS.Init.Supervisor, NervesHub.Supervisor)
_ =
Supervisor.terminate_child(
FarmbotOS.Init.Supervisor,
NervesHub.Supervisor
)
_ =
Supervisor.delete_child(FarmbotOS.Init.Supervisor, NervesHub.Supervisor)
cert = load_cert()
key = load_key()
@ -162,8 +170,15 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
end
catch
kind, err ->
IO.warn("NervesHub error: #{inspect(kind)} #{inspect(err)}", __STACKTRACE__)
FarmbotCore.Logger.error(1, "OTA service error: #{kind} #{inspect(err)}")
IO.warn(
"NervesHub error: #{inspect(kind)} #{inspect(err)}",
__STACKTRACE__
)
FarmbotCore.Logger.error(
1,
"OTA service error: #{kind} #{inspect(err)}"
)
end
# Start the connection again.
@ -200,7 +215,11 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
@impl GenServer
def terminate(reason, state) do
FarmbotCore.Logger.error(1, "Disconnected from NervesHub AMQP channel: #{inspect(reason)}")
FarmbotCore.Logger.error(
1,
"Disconnected from NervesHub AMQP channel: #{inspect(reason)}"
)
# If a channel was still open, close it.
if state.chan, do: AMQP.Channel.close(state.chan)
end
@ -215,7 +234,10 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
{:ok, chan} <- Channel.open(conn),
:ok <- Basic.qos(chan, global: true),
{:ok, _} <-
Queue.declare(chan, "#{jwt.bot}_nerves_hub", auto_delete: false, durable: true),
Queue.declare(chan, "#{jwt.bot}_nerves_hub",
auto_delete: false,
durable: true
),
:ok <-
Queue.bind(chan, "#{jwt.bot}_nerves_hub", @exchange,
routing_key: "bot.#{jwt.bot}.nerves_hub"
@ -226,7 +248,11 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
else
# happens when no token is configured.
{nil, nil} ->
FarmbotCore.Logger.debug(3, "No credentials yet. Can't connect to OTA Server.")
FarmbotCore.Logger.debug(
3,
"No credentials yet. Can't connect to OTA Server."
)
Process.send_after(self(), :connect_amqp, 15_000)
{:noreply, %{state | conn: nil, chan: nil, jwt: nil}}
@ -242,7 +268,11 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
end
def handle_info(:after_connect_amqp, %{key: nil, cert: nil} = state) do
FarmbotCore.Logger.debug(3, "Connected to NervesHub AMQP channel. Fetching certs.")
FarmbotCore.Logger.debug(
3,
"Connected to NervesHub AMQP channel. Fetching certs."
)
old_device_cert = Asset.get_device_cert(serial_number: serial_number())
tags = detect_deployment_tags()
@ -263,24 +293,41 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
# DO NOT DO THIS. The api will do it behind the scenes
# Asset.update_device!(%{update_channel: detect_update_channel()})
FarmbotCore.Logger.debug(3, "DeviceCert created")
FarmbotCore.Logger.debug(3, "Waiting for cert and key data from AMQP from farmbot api...")
FarmbotCore.Logger.debug(
3,
"Waiting for cert and key data from AMQP from farmbot api..."
)
{:noreply, state}
{:error, reason} ->
FarmbotCore.Logger.error(1, "Failed to create device cert: #{inspect(reason)}")
FarmbotCore.Logger.error(
1,
"Failed to create device cert: #{inspect(reason)}"
)
Process.send_after(self(), :after_connect_amqp, 5000)
{:noreply, state}
end
end
def handle_info(:after_connect_amqp, %{key: _key, cert: _cert} = state) do
FarmbotCore.Logger.debug(3, "Connected to NervesHub AMQP channel. Certs already loaded")
FarmbotCore.Logger.debug(
3,
"Connected to NervesHub AMQP channel. Certs already loaded"
)
send(self(), :connect_nerves_hub)
{:noreply, state}
end
def handle_info(:connect_nerves_hub, %{key: nil, cert: nil} = state) do
FarmbotCore.Logger.debug(3, "Can't connect to OTA Service. Certs not loaded")
FarmbotCore.Logger.debug(
3,
"Can't connect to OTA Service. Certs not loaded"
)
send(self(), :connect_amqp)
{:noreply, state}
end
@ -322,7 +369,8 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
device = state.jwt.bot
["bot", ^device, "nerves_hub"] = String.split(key, ".")
with {:ok, %{"cert" => base64_cert, "key" => base64_key}} <- JSON.decode(payload),
with {:ok, %{"cert" => base64_cert, "key" => base64_key}} <-
JSON.decode(payload),
{:ok, cert} <- Base.decode64(base64_cert),
{:ok, key} <- Base.decode64(base64_key),
:ok <- write_cert(cert),
@ -331,7 +379,11 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
{:noreply, %{state | cert: cert, key: key}}
else
{:error, reason} ->
FarmbotCore.Logger.error(1, "OTA Service failed to configure. #{inspect(reason)}")
FarmbotCore.Logger.error(
1,
"OTA Service failed to configure. #{inspect(reason)}"
)
{:stop, reason, state}
:error ->
@ -340,7 +392,10 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
end
end
def handle_info(:checkup, %{is_applying_update: false, probably_connected: true} = state) do
def handle_info(
:checkup,
%{is_applying_update: false, probably_connected: true} = state
) do
if should_auto_apply_update?() && update_available?() do
FarmbotCore.Logger.busy(1, "Applying OTA update")
spawn_link(fn -> NervesHub.update() end)
@ -364,7 +419,10 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
end
@impl GenServer
def handle_cast({:handle_nerves_hub_error, error}, %{is_applying_update: true} = state) do
def handle_cast(
{:handle_nerves_hub_error, error},
%{is_applying_update: true} = state
) do
FarmbotCore.Logger.error(1, "Error applying OTA: #{inspect(error)}")
{:noreply, state}
end
@ -374,7 +432,10 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
{:noreply, state}
end
def handle_cast({:handle_nerves_hub_fwup_message, {:progress, percent}}, state) do
def handle_cast(
{:handle_nerves_hub_fwup_message, {:progress, percent}},
state
) do
_ = set_ota_progress(percent)
{:noreply, state}
end
@ -397,7 +458,11 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
end
@impl GenServer
def handle_call({:handle_nerves_hub_update_available, %{"firmware_url" => url}}, _from, state) do
def handle_call(
{:handle_nerves_hub_update_available, %{"firmware_url" => url}},
_from,
state
) do
case should_auto_apply_update?() do
true ->
FarmbotCore.Logger.busy(1, "Applying OTA update")
@ -462,9 +527,9 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
%{hour: now_hour} ->
FarmbotCore.Logger.debug(
3,
"current hour: #{now_hour} (utc=#{now.hour}) != ota_hour: #{ota_hour}. auto_update=#{
auto_update
}"
"current hour: #{now_hour} (utc=#{now.hour}) != ota_hour: #{
ota_hour
}. auto_update=#{auto_update}"
)
false
@ -538,7 +603,13 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
end
defp open_connection(email, token, jwt) do
case ConnectionWorker.open_connection(token, email, jwt.bot, jwt.mqtt, jwt.vhost) do
case ConnectionWorker.open_connection(
token,
email,
jwt.bot,
jwt.mqtt,
jwt.vhost
) do
{:ok, conn} ->
Process.link(conn.pid)
Process.monitor(conn.pid)
@ -550,7 +621,11 @@ defmodule FarmbotOS.Platform.Target.NervesHubClient do
err
err ->
FarmbotCore.Logger.error(1, "Error opening AMQP connection for OTA certs #{inspect(err)}")
FarmbotCore.Logger.error(
1,
"Error opening AMQP connection for OTA certs #{inspect(err)}"
)
err
end
end

View File

@ -110,9 +110,15 @@ defmodule FarmbotOS.Platform.Target.Network do
end
end
def handle_info({VintageNet, ["interface", ifname, "type"], _old, type, _meta}, state)
def handle_info(
{VintageNet, ["interface", ifname, "type"], _old, type, _meta},
state
)
when type in [PreSetup, VintageNet.Technology.Null] do
FarmbotCore.Logger.debug(1, "Network interface needs configuration: #{ifname}")
FarmbotCore.Logger.debug(
1,
"Network interface needs configuration: #{ifname}"
)
case Config.get_network_config(ifname) do
%Config.NetworkInterface{} = config ->
@ -129,10 +135,17 @@ defmodule FarmbotOS.Platform.Target.Network do
end
vintage_net_config = to_vintage_net(config)
FarmbotTelemetry.event(:network, :interface_configure, nil, interface: ifname)
FarmbotTelemetry.event(:network, :interface_configure, nil,
interface: ifname
)
configure_result = VintageNet.configure(config.name, vintage_net_config)
FarmbotCore.Logger.success(3, "#{config.name} setup: #{inspect(configure_result)}")
FarmbotCore.Logger.success(
3,
"#{config.name} setup: #{inspect(configure_result)}"
)
state = start_network_not_found_timer(state)
{:noreply, state}
@ -142,14 +155,27 @@ defmodule FarmbotOS.Platform.Target.Network do
end
end
def handle_info({VintageNet, ["interface", ifname, "lower_up"], _old, false, _meta}, state) do
FarmbotCore.Logger.error(1, "Interface #{ifname} disconnected from access point")
FarmbotTelemetry.event(:network, :interface_disconnect, nil, interface: ifname)
def handle_info(
{VintageNet, ["interface", ifname, "lower_up"], _old, false, _meta},
state
) do
FarmbotCore.Logger.error(
1,
"Interface #{ifname} disconnected from access point"
)
FarmbotTelemetry.event(:network, :interface_disconnect, nil,
interface: ifname
)
state = start_network_not_found_timer(state)
{:noreply, state}
end
def handle_info({VintageNet, ["interface", ifname, "lower_up"], _old, true, _meta}, state) do
def handle_info(
{VintageNet, ["interface", ifname, "lower_up"], _old, true, _meta},
state
) do
FarmbotCore.Logger.success(1, "Interface #{ifname} connected access point")
FarmbotTelemetry.event(:network, :interface_connect, nil, interface: ifname)
state = cancel_network_not_found_timer(state)
@ -157,16 +183,22 @@ defmodule FarmbotOS.Platform.Target.Network do
end
def handle_info(
{VintageNet, ["interface", ifname, "connection"], :disconnected, :lan, _meta},
{VintageNet, ["interface", ifname, "connection"], :disconnected, :lan,
_meta},
state
) do
FarmbotCore.Logger.warn(1, "Interface #{ifname} connected to local area network")
FarmbotCore.Logger.warn(
1,
"Interface #{ifname} connected to local area network"
)
FarmbotTelemetry.event(:network, :lan_connect, nil, interface: ifname)
{:noreply, state}
end
def handle_info(
{VintageNet, ["interface", ifname, "connection"], :lan, :internet, _meta},
{VintageNet, ["interface", ifname, "connection"], :lan, :internet,
_meta},
state
) do
FarmbotCore.Logger.warn(1, "Interface #{ifname} connected to internet")
@ -176,10 +208,15 @@ defmodule FarmbotOS.Platform.Target.Network do
end
def handle_info(
{VintageNet, ["interface", ifname, "connection"], :internet, ifstate, _meta},
{VintageNet, ["interface", ifname, "connection"], :internet, ifstate,
_meta},
state
) do
FarmbotCore.Logger.warn(1, "Interface #{ifname} disconnected from the internet: #{ifstate}")
FarmbotCore.Logger.warn(
1,
"Interface #{ifname} disconnected from the internet: #{ifstate}"
)
FarmbotExt.AMQP.ConnectionWorker.close()
FarmbotTelemetry.event(:network, :wan_disconnect, nil, interface: ifname)
@ -192,15 +229,16 @@ defmodule FarmbotOS.Platform.Target.Network do
end
def handle_info(
{VintageNet, ["interface", _, "wifi", "access_points"], _old, _new, _meta},
{VintageNet, ["interface", _, "wifi", "access_points"], _old, _new,
_meta},
state
) do
{:noreply, state}
end
def handle_info(
{VintageNet, ["interface", _ifname, "eap_status"], _old, %{status: :success} = eap_status,
_meta},
{VintageNet, ["interface", _ifname, "eap_status"], _old,
%{status: :success} = eap_status, _meta},
state
) do
FarmbotCore.Logger.debug(3, """
@ -212,7 +250,8 @@ defmodule FarmbotOS.Platform.Target.Network do
end
def handle_info(
{VintageNet, ["interface", _ifname, "eap_status"], _old, %{status: :failure}, _meta},
{VintageNet, ["interface", _ifname, "eap_status"], _old,
%{status: :failure}, _meta},
state
) do
FarmbotCore.Logger.error(1, """
@ -292,7 +331,9 @@ defmodule FarmbotOS.Platform.Target.Network do
# Stored in minutes
minutes = network_not_found_timer_minutes(state)
millis = minutes * 60000
new_timer = Process.send_after(self(), {:network_not_found_timer, minutes}, millis)
new_timer =
Process.send_after(self(), {:network_not_found_timer, minutes}, millis)
FarmbotCore.Logger.warn(
1,
@ -308,13 +349,18 @@ defmodule FarmbotOS.Platform.Target.Network do
defp network_not_found_timer_minutes(%{first_connect?: true}), do: 1
defp network_not_found_timer_minutes(_state) do
Asset.fbos_config(:network_not_found_timer) || @default_network_not_found_timer_minutes
Asset.fbos_config(:network_not_found_timer) ||
@default_network_not_found_timer_minutes
end
def reset_ntp do
FarmbotTelemetry.event(:ntp, :reset)
ntp_server_1 = Config.get_config_value(:string, "settings", "default_ntp_server_1")
ntp_server_2 = Config.get_config_value(:string, "settings", "default_ntp_server_2")
ntp_server_1 =
Config.get_config_value(:string, "settings", "default_ntp_server_1")
ntp_server_2 =
Config.get_config_value(:string, "settings", "default_ntp_server_2")
if ntp_server_1 || ntp_server_2 do
Logger.info("Setting NTP servers: [#{ntp_server_1}, #{ntp_server_2}]")

View File

@ -68,12 +68,17 @@ defmodule FarmbotOS.Platform.Target.RTCWorker do
end
@doc "Gets a NaiveDateTime from the rtc"
@spec get_time_from_rtc(I2C.bus()) :: {:ok, NaiveDateTime.t()} | {:error, term()}
@spec get_time_from_rtc(I2C.bus()) ::
{:ok, NaiveDateTime.t()} | {:error, term()}
def get_time_from_rtc(i2c) do
with {:ok, <<_vl::bits-1, second::bits-7>>} <- I2C.write_read(i2c, 0x51, <<0x02>>, 1),
{:ok, <<_::bits-1, minute::bits-7>>} <- I2C.write_read(i2c, 0x51, <<0x03>>, 1),
{:ok, <<_::bits-2, hour::bits-6>>} <- I2C.write_read(i2c, 0x51, <<0x04>>, 1),
{:ok, <<_::bits-2, day::bits-6>>} <- I2C.write_read(i2c, 0x51, <<0x05>>, 1),
with {:ok, <<_vl::bits-1, second::bits-7>>} <-
I2C.write_read(i2c, 0x51, <<0x02>>, 1),
{:ok, <<_::bits-1, minute::bits-7>>} <-
I2C.write_read(i2c, 0x51, <<0x03>>, 1),
{:ok, <<_::bits-2, hour::bits-6>>} <-
I2C.write_read(i2c, 0x51, <<0x04>>, 1),
{:ok, <<_::bits-2, day::bits-6>>} <-
I2C.write_read(i2c, 0x51, <<0x05>>, 1),
{:ok, <<_c::bits-1, _::bits-2, month::bits-5>>} <-
I2C.write_read(i2c, 0x51, <<0x07>>, 1),
# implied 20XX
@ -178,7 +183,9 @@ defmodule FarmbotOS.Platform.Target.RTCWorker do
Logger.error("Not setting system time from RTC. VL bit is unset")
error ->
Logger.error("failed to get time from rtc or set system time: #{inspect(error)}")
Logger.error(
"failed to get time from rtc or set system time: #{inspect(error)}"
)
end
Process.send_after(self(), :set_rtc_from_ntp, @eleven_minutes)

View File

@ -17,7 +17,11 @@ defmodule FarmbotOS.Platform.Target.ShoehornHandler do
@impl true
def application_exited(:nerves_runtime, _, state) do
# https://github.com/nerves-project/nerves_runtime/issues/152
_ = System.cmd("killall", ["-9", "kmsg_tailer"], into: IO.stream(:stdio, :line))
_ =
System.cmd("killall", ["-9", "kmsg_tailer"],
into: IO.stream(:stdio, :line)
)
{:continue, state}
end
@ -28,7 +32,10 @@ defmodule FarmbotOS.Platform.Target.ShoehornHandler do
:farmbot_ext,
:farmbot
] do
error_log("Farmbot app: #{app} exited #{count}: #{inspect(reason, limit: :infinity)}")
error_log(
"Farmbot app: #{app} exited #{count}: #{inspect(reason, limit: :infinity)}"
)
# Force a factory reset.
FarmbotOS.System.factory_reset(
"Farmbot app: #{app} exited #{count}: #{inspect(reason, limit: :infinity)}"
@ -45,7 +52,10 @@ defmodule FarmbotOS.Platform.Target.ShoehornHandler do
:farmbot_ext,
:farmbot
] do
error_log("Farmbot app: #{app} exited #{count}: #{inspect(reason, limit: :infinity)}")
error_log(
"Farmbot app: #{app} exited #{count}: #{inspect(reason, limit: :infinity)}"
)
FarmbotTelemetry.event(:shoehorn, :application_exit, nil, application: app)
with {:ok, _} <- Application.ensure_all_started(:farmbot_core),
@ -58,7 +68,10 @@ defmodule FarmbotOS.Platform.Target.ShoehornHandler do
end
def application_exited(app, reason, state) do
error_log("Application stopped: #{inspect(app)} #{inspect(reason, limit: :infinity)}")
error_log(
"Application stopped: #{inspect(app)} #{inspect(reason, limit: :infinity)}"
)
FarmbotTelemetry.event(:shoehorn, :application_exit, nil, application: app)
# Application.ensure_all_started(app)
{:continue, state}

View File

@ -55,7 +55,10 @@ defmodule FarmbotOS.Platform.Target.SSHConsole do
end
end
def handle_cast({:add_key, %PublicKey{public_key: authorized_key}}, %{ssh: ssh} = state) do
def handle_cast(
{:add_key, %PublicKey{public_key: authorized_key}},
%{ssh: ssh} = state
) do
_ = stop_ssh(ssh)
decoded_authorized_key = do_decode(authorized_key)
@ -65,7 +68,9 @@ defmodule FarmbotOS.Platform.Target.SSHConsole do
%{
state
| ssh: ssh,
public_keys: [List.first(decoded_authorized_key) | state.public_keys]
public_keys: [
List.first(decoded_authorized_key) | state.public_keys
]
}}
error ->
@ -78,11 +83,13 @@ defmodule FarmbotOS.Platform.Target.SSHConsole do
ssh && :ssh.stop_daemon(ssh)
end
defp start_ssh(port, decoded_authorized_keys) when is_list(decoded_authorized_keys) do
defp start_ssh(port, decoded_authorized_keys)
when is_list(decoded_authorized_keys) do
# Reuse keys from `nerves_firmware_ssh` so that the user only needs one
# config.exs entry.
nerves_keys =
Application.get_env(:nerves_firmware_ssh, :authorized_keys, []) |> Enum.join("\n")
Application.get_env(:nerves_firmware_ssh, :authorized_keys, [])
|> Enum.join("\n")
decoded_nerves_keys = do_decode(nerves_keys)

View File

@ -74,8 +74,20 @@ defmodule FarmbotOS.Platform.Target.Uevent do
FarmbotTelemetry.event(:firmware, :tty_detected, nil, tty: tty)
FarmbotCore.Logger.busy(1, "new firmware interfaces detected: #{tty}")
FirmwareTTYDetector.set_tty(tty)
Config.update_config_value(:bool, "settings", "firmware_needs_flash", true)
Config.update_config_value(:bool, "settings", "firmware_needs_open", false)
Config.update_config_value(
:bool,
"settings",
"firmware_needs_flash",
true
)
Config.update_config_value(
:bool,
"settings",
"firmware_needs_open",
false
)
tty ->
Logger.debug("firmware interface already detected: #{tty}")

View File

@ -141,7 +141,10 @@ defmodule FarmbotOS.Configurator.RouterTest do
assert redirected_to(conn) == "/config_wireless"
conn =
conn(:post, "/config_wireless_step_1", %{"ssid" => "Test Network", "security" => "NONE"})
conn(:post, "/config_wireless_step_1", %{
"ssid" => "Test Network",
"security" => "NONE"
})
|> init_test_session(%{"ifname" => "wlan0"})
|> Router.call(@opts)
@ -149,7 +152,10 @@ defmodule FarmbotOS.Configurator.RouterTest do
assert conn.resp_body =~ "Advanced settings"
conn =
conn(:post, "/config_wireless_step_1", %{"ssid" => "Test Network", "security" => "WPA-PSK"})
conn(:post, "/config_wireless_step_1", %{
"ssid" => "Test Network",
"security" => "WPA-PSK"
})
|> init_test_session(%{"ifname" => "wlan0"})
|> Router.call(@opts)
@ -157,7 +163,10 @@ defmodule FarmbotOS.Configurator.RouterTest do
assert conn.resp_body =~ "Advanced settings"
conn =
conn(:post, "/config_wireless_step_1", %{"ssid" => "Test Network", "security" => "WPA2-PSK"})
conn(:post, "/config_wireless_step_1", %{
"ssid" => "Test Network",
"security" => "WPA2-PSK"
})
|> init_test_session(%{"ifname" => "wlan0"})
|> Router.call(@opts)
@ -165,7 +174,10 @@ defmodule FarmbotOS.Configurator.RouterTest do
assert conn.resp_body =~ "Advanced settings"
conn =
conn(:post, "/config_wireless_step_1", %{"ssid" => "Test Network", "security" => "WPA-EAP"})
conn(:post, "/config_wireless_step_1", %{
"ssid" => "Test Network",
"security" => "WPA-EAP"
})
|> init_test_session(%{"ifname" => "wlan0"})
|> Router.call(@opts)
@ -227,10 +239,18 @@ defmodule FarmbotOS.Configurator.RouterTest do
assert get_session(conn, "net_config_ssid") == "Test Network"
assert get_session(conn, "net_config_security") == "WPA-PSK"
assert get_session(conn, "net_config_psk") == "ABCDEF"
assert get_session(conn, "net_config_identity") == "NOT TECHNICALLY POSSIBLE"
assert get_session(conn, "net_config_password") == "NOT TECHNICALLY POSSIBLE"
assert get_session(conn, "net_config_identity") ==
"NOT TECHNICALLY POSSIBLE"
assert get_session(conn, "net_config_password") ==
"NOT TECHNICALLY POSSIBLE"
assert get_session(conn, "net_config_domain") == "farmbot.org"
assert get_session(conn, "net_config_name_servers") == "192.168.1.1, 192.168.1.2"
assert get_session(conn, "net_config_name_servers") ==
"192.168.1.1, 192.168.1.2"
assert get_session(conn, "net_config_ipv4_method") == "static"
assert get_session(conn, "net_config_ipv4_address") == "192.168.1.100"
assert get_session(conn, "net_config_ipv4_gateway") == "192.168.1.1"
@ -268,7 +288,10 @@ defmodule FarmbotOS.Configurator.RouterTest do
assert get_session(conn, "auth_config_server") == "https://my.farm.bot"
conn =
conn(:post, "/configure_credentials", %{params | "server" => "whoops/i/made/a/type"})
conn(:post, "/configure_credentials", %{
params
| "server" => "whoops/i/made/a/type"
})
|> Router.call(@opts)
assert redirected_to(conn) == "/credentials"

View File

@ -1,2 +1,7 @@
Mox.defmock(FarmbotTest.Configurator.MockDataLayer, for: FarmbotOS.Configurator.DataLayer)
Mox.defmock(FarmbotTest.Configurator.MockNetworkLayer, for: FarmbotOS.Configurator.NetworkLayer)
Mox.defmock(FarmbotTest.Configurator.MockDataLayer,
for: FarmbotOS.Configurator.DataLayer
)
Mox.defmock(FarmbotTest.Configurator.MockNetworkLayer,
for: FarmbotOS.Configurator.NetworkLayer
)

View File

@ -58,7 +58,9 @@ defmodule FarmbotTelemetry do
defmacro event(subsystem, measurement, value, meta) do
Mix.raise("""
Unknown args for telemetry event:
#{inspect(subsystem)}, #{inspect(measurement)}, #{inspect(value)}, #{inspect(meta)}
#{inspect(subsystem)}, #{inspect(measurement)}, #{inspect(value)}, #{
inspect(meta)
}
""")
end
@ -92,15 +94,37 @@ defmodule FarmbotTelemetry do
Dispatching is done by calling the `consume_telemetry/1` function.
"""
@spec bare_telemetry(uuid, kind(), subsystem(), measurement(), value(), DateTime.t(), meta()) ::
@spec bare_telemetry(
uuid,
kind(),
subsystem(),
measurement(),
value(),
DateTime.t(),
meta()
) ::
:ok
def bare_telemetry(uuid, kind, subsystem, measurement, value, captured_at, meta)
when is_binary(uuid) and is_atom(kind) and is_atom(subsystem) and is_atom(measurement) and
def bare_telemetry(
uuid,
kind,
subsystem,
measurement,
value,
captured_at,
meta
)
when is_binary(uuid) and is_atom(kind) and is_atom(subsystem) and
is_atom(measurement) and
is_list(meta) do
_ =
:telemetry.execute(
[:farmbot_telemetry, kind, subsystem],
%{measurement: measurement, value: value, captured_at: captured_at, uuid: uuid},
%{
measurement: measurement,
value: value,
captured_at: captured_at,
uuid: uuid
},
Map.new(meta)
)
@ -142,7 +166,8 @@ defmodule FarmbotTelemetry do
@typedoc "Function passed to `consume_telemetry/1`"
@type consumer_fun() ::
({uuid(), DateTime.t(), kind(), subsystem(), measurement(), value(), meta()} ->
({uuid(), DateTime.t(), kind(), subsystem(), measurement(), value(),
meta()} ->
:ok | any())
@doc """

Some files were not shown because too many files have changed in this diff Show More