126 lines
3.8 KiB
Elixir
126 lines
3.8 KiB
Elixir
defmodule FarmbotOS.Lua do
|
|
@moduledoc """
|
|
Embedded scripting language for testing,
|
|
assertion, and other debugging things.
|
|
"""
|
|
|
|
@type t() :: tuple()
|
|
@type table() :: [{any, any}]
|
|
require FarmbotCore.Logger
|
|
|
|
alias FarmbotOS.Lua.Ext.{
|
|
DataManipulation,
|
|
Firmware,
|
|
Info
|
|
}
|
|
|
|
# this function is used by SysCalls, but isn't a direct requirement.
|
|
@doc "Logs an assertion based on it's result"
|
|
def log_assertion(passed?, type, message) do
|
|
meta = [assertion_passed: passed?, assertion_type: type]
|
|
FarmbotCore.Logger.dispatch_log(__ENV__, :assertion, 2, message, meta)
|
|
end
|
|
|
|
@doc """
|
|
Evaluates some Lua code. The code should
|
|
return a boolean value.
|
|
"""
|
|
def eval_assertion(comment, str) when is_binary(str) do
|
|
init()
|
|
|> eval(str)
|
|
|> case do
|
|
{:ok, [true | _]} ->
|
|
true
|
|
|
|
{:ok, [false | _]} ->
|
|
false
|
|
|
|
{:ok, [_, reason]} when is_binary(reason) ->
|
|
{:error, reason}
|
|
|
|
{:ok, _data} ->
|
|
{:error, "bad return value from expression evaluation"}
|
|
|
|
{:error, {:lua_error, _error, _lua}} ->
|
|
{:error, "lua runtime error evaluating expression"}
|
|
|
|
{:error, {:badmatch, {:error, [{line, :luerl_parse, parse_error}], _}}} ->
|
|
FarmbotCore.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
|
|
end
|
|
end
|
|
|
|
@spec init() :: t()
|
|
def init do
|
|
:luerl.init()
|
|
|> set_table([:calibrate], &Firmware.calibrate/2)
|
|
|> set_table([:emergency_lock], &Firmware.emergency_lock/2)
|
|
|> set_table([:emergency_unlock], &Firmware.emergency_unlock/2)
|
|
|> set_table([:find_home], &Firmware.find_home/2)
|
|
|> set_table([:home], &Firmware.home/2)
|
|
|> set_table([:move_absolute], &Firmware.move_absolute/2)
|
|
|> set_table([:get_position], &Firmware.get_position/2)
|
|
|> set_table([:check_position], &Firmware.check_position/2)
|
|
|> set_table([:get_pin], &Firmware.get_pin/2)
|
|
|> set_table([:get_pins], &Firmware.get_pins/2)
|
|
|> set_table([:coordinate], &Firmware.coordinate/2)
|
|
|> set_table([:read_status], &Info.read_status/2)
|
|
|> set_table([:send_message], &Info.send_message/2)
|
|
|> set_table([:version], &Info.version/2)
|
|
|> set_table([:current_month], &Info.current_month/2)
|
|
|> set_table([:current_hour], &Info.current_hour/2)
|
|
|> set_table([:current_minute], &Info.current_minute/2)
|
|
|> set_table([:current_second], &Info.current_second/2)
|
|
|> set_table([:update_device], &DataManipulation.update_device/2)
|
|
|> 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([:new_farmware_env], &DataManipulation.new_farmware_env/2)
|
|
|> set_table([:new_sensor_reading], &DataManipulation.new_sensor_reading/2)
|
|
end
|
|
|
|
@spec set_table(t(), Path.t(), any()) :: t()
|
|
def set_table(lua, path, value) do
|
|
:luerl.set_table(path, value, lua)
|
|
end
|
|
|
|
@spec eval(t(), String.t()) :: {:ok, any()} | {:error, any()}
|
|
def eval(lua, hook) when is_binary(hook) 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}
|
|
|
|
error, reason ->
|
|
{{:error, {error, reason}}, lua}
|
|
end
|
|
end
|