Skip to content

Commit

Permalink
Merge pull request #1443 from FarmBot/staging
Browse files Browse the repository at this point in the history
Staging => Main (Stable Release v14.6.2)
  • Loading branch information
RickCarlino authored Nov 22, 2021
2 parents ab64755 + a7327b8 commit dfcf10e
Show file tree
Hide file tree
Showing 54 changed files with 20,131 additions and 19,225 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

# 14.6.2

* Fix bug where Lua scripts would not stop sending GCode during estop
* Genesis and Express firmware updates.
* Improved Lua error legibility.
* Technical preview of load sense API
* Routine dependency upgrades.

# 14.6.1

* Remove need to download farmwares over the network (improves offline experience)
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
14.6.1
14.6.2
20 changes: 19 additions & 1 deletion lib/core/asset/firmware_config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ defmodule FarmbotOS.Asset.FirmwareConfig do
field(:movement_calibration_deadzone_x, :float)
field(:movement_calibration_deadzone_y, :float)
field(:movement_calibration_deadzone_z, :float)
field(:movement_calibration_retry_total_x, :float)
field(:movement_calibration_retry_total_y, :float)
field(:movement_calibration_retry_total_z, :float)
field(:movement_calibration_retry_x, :float)
field(:movement_calibration_retry_y, :float)
field(:movement_calibration_retry_z, :float)
Expand Down Expand Up @@ -124,6 +127,8 @@ defmodule FarmbotOS.Asset.FirmwareConfig do
field(:pin_guard_5_active_state, :float)
field(:pin_guard_5_pin_nr, :float)
field(:pin_guard_5_time_out, :float)
field(:pin_report_1_pin_nr, :float)
field(:pin_report_2_pin_nr, :float)
timestamps()
end

Expand Down Expand Up @@ -212,6 +217,12 @@ defmodule FarmbotOS.Asset.FirmwareConfig do
movement_min_spd_y: firmware_config.movement_min_spd_y,
movement_min_spd_z: firmware_config.movement_min_spd_z,
movement_min_spd_z2: firmware_config.movement_min_spd_z2,
movement_calibration_retry_total_x:
firmware_config.movement_calibration_retry_total_x,
movement_calibration_retry_total_y:
firmware_config.movement_calibration_retry_total_y,
movement_calibration_retry_total_z:
firmware_config.movement_calibration_retry_total_z,
movement_motor_current_x: firmware_config.movement_motor_current_x,
movement_motor_current_y: firmware_config.movement_motor_current_y,
movement_motor_current_z: firmware_config.movement_motor_current_z,
Expand Down Expand Up @@ -256,7 +267,9 @@ defmodule FarmbotOS.Asset.FirmwareConfig do
pin_guard_4_time_out: firmware_config.pin_guard_4_time_out,
pin_guard_5_active_state: firmware_config.pin_guard_5_active_state,
pin_guard_5_pin_nr: firmware_config.pin_guard_5_pin_nr,
pin_guard_5_time_out: firmware_config.pin_guard_5_time_out
pin_guard_5_time_out: firmware_config.pin_guard_5_time_out,
pin_report_1_pin_nr: firmware_config.pin_report_1_pin_nr,
pin_report_2_pin_nr: firmware_config.pin_report_2_pin_nr
}
end

Expand Down Expand Up @@ -285,6 +298,11 @@ defmodule FarmbotOS.Asset.FirmwareConfig do
:encoder_type_z,
:encoder_use_for_pos_x,
:encoder_use_for_pos_y,
:movement_calibration_retry_total_x,
:movement_calibration_retry_total_y,
:movement_calibration_retry_total_z,
:pin_report_1_pin_nr,
:pin_report_2_pin_nr,
:encoder_use_for_pos_z,
:movement_axis_nr_steps_x,
:movement_axis_nr_steps_y,
Expand Down
5 changes: 2 additions & 3 deletions lib/firmware/command.ex
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,15 @@ defmodule FarmbotOS.Firmware.Command do
# F16 Find length of Z axis (measure length + find 0) *
def find_length(:z), do: schedule(:F16, [])

# # F21(P) Read parameter
def read_param(param), do: schedule(:F21, P: param)

# F82
def report_current_position() do
schedule(:F82, [])
pos = %{x: _, y: _, z: _} = cached_position()
{:ok, pos}
end

def watch_pin(pin_number), do: schedule(:F22, P: 199, V: pin_number)

# F43(P, M) Set the I/O mode M (input=0/output=1) of a pin P in arduino
def set_pin_io_mode(pin, mode) do
write_pm(43, pin, mode)
Expand Down
20 changes: 4 additions & 16 deletions lib/firmware/config_uploader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -58,28 +58,16 @@ defmodule FarmbotOS.Firmware.ConfigUploader do
state
end

# Ignore :param_version
defp do_verify_param(_, {0, _}), do: nil
# Ignore :param_test
defp do_verify_param(_, {1, _}), do: nil
# Ignore :param_config_ok
defp do_verify_param(_, {2, _}), do: nil
# Ignore :param_use_eeprom
defp do_verify_param(_, {3, _}), do: nil

defp do_verify_param(nil, _conf) do
end
@ignored_params [0, 1, 2, 3]

defp do_verify_param(_, {p, _}) when p in @ignored_params, do: nil
defp do_verify_param(nil, _conf), do: nil

defp do_verify_param(conf, {p, actual}) do
key = Parameter.translate(p)
expected = Map.fetch!(conf, key)

unless actual == expected do
a = inspect(actual)
e = inspect(expected)
k = inspect(key)
Logger.warn("Expected #{k} to eq #{e}. Got: #{a}")
else
:ok = BotState.set_firmware_config(key, actual)
end
end
Expand Down
1 change: 1 addition & 0 deletions lib/firmware/gcode_decoder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ defmodule FarmbotOS.Firmware.GCodeDecoder do
"21" => :param_value_report,
"23" => :report_updated_param_during_calibration,
"41" => :pin_value_report,
"61" => :report_pin_monitor_analog_value,
"71" => :x_axis_timeout,
"72" => :y_axis_timeout,
"73" => :z_axis_timeout,
Expand Down
17 changes: 17 additions & 0 deletions lib/firmware/inbound_side_effects.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule FarmbotOS.Firmware.InboundSideEffects do
alias FarmbotOS.{
Asset,
BotState,
Firmware.GCode,
Firmware.TxBuffer,
Firmware.UARTCoreSupport,
FirmwareEstopTimer,
Expand Down Expand Up @@ -219,6 +220,22 @@ defmodule FarmbotOS.Firmware.InboundSideEffects do
defp reduce({:y_axis_timeout, _}, s), do: s
defp reduce({:z_axis_timeout, _}, s), do: s

defp reduce(
{:report_pin_monitor_analog_value, %{pin_or_param: p, value1: v}},
s
) do
watcher = s.pin_watcher

if watcher && Process.alive?(watcher) do
send(watcher, {:pin_data, p, v})
s
else
# Turn off pin watching if the watcher dies.
off = GCode.new(:F22, P: 199, V: 0)
TxBuffer.push(%{s | pin_watcher: nil}, nil, off)
end
end

defp reduce(unknown, state) do
msg = "=== Unhandled inbound side effects: #{inspect(unknown)}"
FarmbotOS.Logger.info(3, msg)
Expand Down
5 changes: 5 additions & 0 deletions lib/firmware/parameter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ defmodule FarmbotOS.Firmware.Parameter do
{171, :movement_calibration_deadzone_x},
{172, :movement_calibration_deadzone_y},
{173, :movement_calibration_deadzone_z},
{175, :movement_calibration_retry_total_x},
{176, :movement_calibration_retry_total_y},
{177, :movement_calibration_retry_total_z},
{198, :pin_report_1_pin_nr},
{199, :pin_report_2_pin_nr},
{201, :pin_guard_1_pin_nr},
{202, :pin_guard_1_time_out},
{203, :pin_guard_1_active_state},
Expand Down
22 changes: 21 additions & 1 deletion lib/firmware/uart_core.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ defmodule FarmbotOS.Firmware.UARTCore do
fw_type: nil,
rx_count: 0,
rx_buffer: RxBuffer.new(),
tx_buffer: TxBuffer.new()
tx_buffer: TxBuffer.new(),
pin_watcher: nil

# The Firmware has a 120 second default timeout.
# Queuing up 10 messages that take one minute each == 10 minutes.
Expand All @@ -51,6 +52,16 @@ defmodule FarmbotOS.Firmware.UARTCore do
@bugfix_timeout 60_000
# ===== END HISTORICAL CODE ==============================

def watch_pin(server \\ __MODULE__, pin_number) do
send(server, {:watch_pin, self()})
FarmbotOS.Firmware.Command.watch_pin(pin_number)
end

def unwatch_pin(server \\ __MODULE__) do
send(server, :unwatch_pin)
FarmbotOS.Firmware.Command.watch_pin(0)
end

# This is a helper method that I use for inspecting GCode
# over SSH. It is not used by production systems except for
# debugging.
Expand Down Expand Up @@ -97,6 +108,15 @@ defmodule FarmbotOS.Firmware.UARTCore do
{:ok, state}
end

def handle_info({:watch_pin, watcher}, state) do
if state.pin_watcher, do: raise("Pin observer did not cleanly exit!")
{:noreply, Map.put(state, :pin_watcher, watcher)}
end

def handle_info(:unwatch_pin, state) do
{:noreply, Map.put(state, :pin_watcher, nil)}
end

def handle_info(:reset_state, %State{uart_path: old_path} = state1) do
# Teardown existing connection.
Support.disconnect(state1, "Rebooting firmware")
Expand Down
76 changes: 28 additions & 48 deletions lib/os/lua.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@ defmodule FarmbotOS.Lua do
Embedded scripting language for "formulas" in the MOVE block,
assertion, and general scripting via LUA block.
"""

@type t() :: tuple()
@type table() :: [{any, any}]
require FarmbotOS.Logger
require Logger

alias FarmbotOS.Lua.{
DataManipulation,
Firmware,
Info,
Wait
Wait,
PinWatcher
}

# this function is used by SysCalls, but isn't a direct requirement.
Expand Down Expand Up @@ -48,44 +46,11 @@ defmodule FarmbotOS.Lua do
`extra_vm_args` is a set of extra args to place inside the
Lua sandbox. The extra args are passed to set_table/3
"""
def perform_lua(lua_code, extra_vm_args, comment) do
comment = comment || "sequence"
def perform_lua(lua_code, extra_vm_args, _comment) do
lua_code = add_implicit_return(lua_code)
reducer = fn args, vm -> apply(__MODULE__, :set_table, [vm | args]) end
vm = Enum.reduce(extra_vm_args, init(), reducer)

case raw_eval(vm, lua_code) do
{:ok, value} ->
{:ok, value}

{:error, {:lua_error, error, _lua}} ->
{:error, "lua runtime error evaluating expression: #{inspect(error)}"}

{:error, {:badmatch, {:error, [{line, :luerl_parse, parse_error}], _}}} ->
FarmbotOS.Logger.error(
1,
"""
Failed to parse expression:
`#{comment}.lua:#{line}`
#{IO.iodata_to_binary(parse_error)}
""",
channels: [:toast]
)

{:error,
"failed to parse expression (line:#{line}): #{IO.iodata_to_binary(parse_error)}"}

{:error, error, backtrace} ->
IO.inspect(backtrace, label: "=== LUA ERROR TRACE")
{:error, error}

{:error, error} ->
{:error, error}

error ->
{:error, inspect(error)}
end
FarmbotOS.Lua.Result.new(raw_eval(vm, lua_code))
end

def init do
Expand All @@ -102,22 +67,22 @@ defmodule FarmbotOS.Lua do
:luerl.eval(hook, lua)
end

def unquote(:do)(lua, hook) when is_binary(hook) do
:luerl.do(hook, lua)
catch
:error, {:error, reason} ->
{{:error, reason}, lua}
# def unquote(:do)(lua, hook) when is_binary(hook) do
# :luerl.do(hook, lua)
# catch
# :error, {:error, reason} ->
# {{:error, reason}, lua}

error, reason ->
{{:error, {error, reason}}, lua}
end
# error, reason ->
# {{:error, {error, reason}}, lua}
# end

# Wrap a function so that it cannot be called when device
# is locked.
def safe(action, fun) when is_function(fun, 2) do
fn args, lua ->
if FarmbotOS.Firmware.UARTCoreSupport.locked?() do
{[nil, "Can't #{action} when locked."], :luerl.stop(lua)}
{[nil, "Can't #{action} when locked."], disabled_sandbox()}
else
fun.(args, lua)
end
Expand Down Expand Up @@ -186,7 +151,22 @@ defmodule FarmbotOS.Lua do
update_fbos_config: &DataManipulation.update_fbos_config/2,
update_firmware_config: &DataManipulation.update_firmware_config/2,
wait: &Wait.wait/2,
watch_pin: &PinWatcher.new/2,
write_pin: safe("write pin", &Firmware.write_pin/2)
}
end

# WHAT IS GOING ON HERE?:
# * We want to allow some Lua to execute when the bot
# is estopped.
# * If the bot is estopped, we DO NOT want it to perform
# unsafe actions like motor movement.
# * If execution reaches this point, you are estopped
# and you get this far, it means you tried to move
# the bot or perform an unsafe action.
# * For safety, we will revert the Lua VM to an empty
# state.
def disabled_sandbox() do
:luerl.set_table([:estop], true, :luerl.init())
end
end
27 changes: 27 additions & 0 deletions lib/os/lua/pin_watcher.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule FarmbotOS.Lua.PinWatcher do
alias FarmbotOS.Firmware.UARTCore

defstruct [:pin, :callback, :parent]

def new([pin, callback], lua) do
GenServer.start_link(__MODULE__, [pin, callback, self()])
{[], lua}
end

def init([pin, callback, parent]) do
UARTCore.watch_pin(pin)
{:ok, %__MODULE__{pin: pin, callback: callback, parent: parent}}
end

def handle_info({:pin_data, pin, val}, state) do
if Process.alive?(state.parent) do
state.callback.([[pin: pin, value: val]])
{:noreply, state}
else
# Stop process if CSVM is done/crashed
{:stop, :normal, state}
end
end

def terminate(_reason, _state), do: UARTCore.unwatch_pin()
end
Loading

0 comments on commit dfcf10e

Please sign in to comment.