Implement `every_point` and `point_group` Celery nodes
parent
d3e51996b0
commit
1ff2a7d153
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"args": {
|
||||
"locals": {
|
||||
"args": {},
|
||||
"body": [],
|
||||
"comment": null,
|
||||
"kind": "scope_declaration",
|
||||
"meta": null
|
||||
}
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"args": {
|
||||
"sequence_id": 19012
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"args": {
|
||||
"data_value": {
|
||||
"args": {
|
||||
"every_point_type": "Plant"
|
||||
},
|
||||
"body": null,
|
||||
"comment": null,
|
||||
"kind": "every_point",
|
||||
"meta": null
|
||||
},
|
||||
"label": "parent"
|
||||
},
|
||||
"body": null,
|
||||
"comment": null,
|
||||
"kind": "parameter_application",
|
||||
"meta": null
|
||||
}
|
||||
],
|
||||
"comment": null,
|
||||
"kind": "execute",
|
||||
"meta": null
|
||||
}
|
||||
],
|
||||
"comment": null,
|
||||
"kind": "sequence",
|
||||
"meta": null
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"args": {
|
||||
"locals": {
|
||||
"args": {},
|
||||
"body": [],
|
||||
"comment": null,
|
||||
"kind": "scope_declaration",
|
||||
"meta": null
|
||||
}
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"args": {
|
||||
"sequence_id": 19012
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"args": {
|
||||
"data_value": {
|
||||
"args": {
|
||||
"resource_id": 31
|
||||
},
|
||||
"body": null,
|
||||
"comment": null,
|
||||
"kind": "point_group",
|
||||
"meta": null
|
||||
},
|
||||
"label": "parent"
|
||||
},
|
||||
"body": null,
|
||||
"comment": null,
|
||||
"kind": "parameter_application",
|
||||
"meta": null
|
||||
}
|
||||
],
|
||||
"comment": null,
|
||||
"kind": "execute",
|
||||
"meta": null
|
||||
}
|
||||
],
|
||||
"comment": null,
|
||||
"kind": "sequence",
|
||||
"meta": null
|
||||
}
|
|
@ -387,18 +387,92 @@ defmodule FarmbotCeleryScript.Compiler do
|
|||
end
|
||||
end
|
||||
|
||||
# Compiles an `execute` block.
|
||||
compile :execute, %{sequence_id: id}, parameter_applications 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))
|
||||
FarmbotCeleryScript.Compiler.compile(ast, env)
|
||||
@iterables [:point_group, :every_point]
|
||||
|
||||
error ->
|
||||
error
|
||||
# Compiles an `execute` block.
|
||||
# This one is actually pretty complex and is split into two parts.
|
||||
# TODO(Connor) refactor this into it's own module, or at least two
|
||||
# different functions.
|
||||
compile :execute, %{sequence_id: id}, parameter_applications do
|
||||
# if there is an iterable AST here,
|
||||
# we need to compile _many_ sequences, not just one.
|
||||
|
||||
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
|
||||
when kind in @iterables ->
|
||||
iterable
|
||||
|
||||
_other ->
|
||||
false
|
||||
end)
|
||||
|
||||
# if there was an iterable, inject it's value for every instance of that item
|
||||
if loop_parameter_appl_ast do
|
||||
# remove the iterable from the parameter applications,
|
||||
# since it will be injected after this.
|
||||
parameter_applications =
|
||||
Enum.reduce(parameter_applications, [], fn
|
||||
# Remove point_group from parameter appls
|
||||
%{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
|
||||
# Everything else gets added back
|
||||
ast, acc -> acc ++ [ast]
|
||||
end)
|
||||
|
||||
# will be a point_group or every_point node
|
||||
group_ast = loop_parameter_appl_ast.args.data_value
|
||||
# check if it's a point_group first, then fall back to every_point
|
||||
point_group_arg = group_ast.args[:resource_id] || group_ast.args[:every_point_type]
|
||||
|
||||
# lookup all point_groups related to this value
|
||||
case FarmbotCeleryScript.SysCalls.get_point_group(point_group_arg) do
|
||||
{:error, reason} ->
|
||||
quote location: :keep, do: Macro.escape({:error, unquote(reason)})
|
||||
|
||||
%{} = point_group ->
|
||||
# Map over all the points returned by `get_point_group/1`
|
||||
Enum.map(point_group.point_ids, fn point_id ->
|
||||
# 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"
|
||||
# compile a `execute` ast, injecting the appropriate `point` ast with
|
||||
# the matching `label`
|
||||
compile_ast(%FarmbotCeleryScript.AST{
|
||||
kind: :execute,
|
||||
args: %{sequence_id: id},
|
||||
body: [
|
||||
# this is the injection. This parameter_application was removed
|
||||
%FarmbotCeleryScript.AST{
|
||||
kind: :parameter_application,
|
||||
args: %{
|
||||
# inject the replacement with the same label
|
||||
label: loop_parameter_appl_ast.args.label,
|
||||
data_value: %FarmbotCeleryScript.AST{
|
||||
kind: :point,
|
||||
args: %{pointer_type: pointer_type, pointer_id: point_id}
|
||||
}
|
||||
}
|
||||
}
|
||||
# add all other parmeter_applications back in the case of variables, etc
|
||||
| parameter_applications
|
||||
]
|
||||
})
|
||||
end)
|
||||
end
|
||||
else
|
||||
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))
|
||||
FarmbotCeleryScript.Compiler.compile(ast, env)
|
||||
|
||||
error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1051,6 +1125,6 @@ defmodule FarmbotCeleryScript.Compiler do
|
|||
# compiled
|
||||
# |> Macro.to_string()
|
||||
# |> Code.format_string!()
|
||||
# |> Logger.debug()
|
||||
# |> IO.puts()
|
||||
# end
|
||||
end
|
||||
|
|
|
@ -77,6 +77,12 @@ 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]}
|
||||
|
||||
def get_point_group(sys_calls \\ @sys_calls, point_group_id) do
|
||||
point_group_or_error(sys_calls, :get_point_group, [point_group_id])
|
||||
end
|
||||
|
||||
def format_lhs(sys_calls \\ @sys_calls, lhs)
|
||||
|
||||
def format_lhs(_sys_calls, "x"), do: "current X position"
|
||||
|
@ -335,6 +341,13 @@ defmodule FarmbotCeleryScript.SysCalls do
|
|||
end
|
||||
end
|
||||
|
||||
defp point_group_or_error(sys_calls, fun, args) do
|
||||
case apply(sys_calls, fun, args) do
|
||||
%{point_ids: ids} = point_group when is_list(ids) -> point_group
|
||||
error -> or_error(sys_calls, fun, args, error)
|
||||
end
|
||||
end
|
||||
|
||||
defp or_error(_sys_calls, _fun, _args, {:error, reason}) when is_binary(reason) do
|
||||
{:error, reason}
|
||||
end
|
||||
|
|
|
@ -97,6 +97,9 @@ defmodule FarmbotCeleryScript.SysCalls.Stubs do
|
|||
@impl true
|
||||
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])
|
||||
|
||||
@impl true
|
||||
def power_off(), do: error(:power_off, [])
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ defmodule FarmbotCore.Asset do
|
|||
Peripheral,
|
||||
PinBinding,
|
||||
Point,
|
||||
PointGroup,
|
||||
PublicKey,
|
||||
Regimen,
|
||||
RegimenInstance,
|
||||
|
@ -228,8 +229,21 @@ defmodule FarmbotCore.Asset do
|
|||
|> Repo.update()
|
||||
end
|
||||
|
||||
@doc "Returns all points matching Point.pointer_type"
|
||||
def get_all_points_by_type(type) do
|
||||
Repo.all(from p in Point, where: p.pointer_type == ^type)
|
||||
end
|
||||
|
||||
## End Point
|
||||
|
||||
## Begin PointGroup
|
||||
|
||||
def get_point_group(params) do
|
||||
Repo.get_by(PointGroup, params)
|
||||
end
|
||||
|
||||
## End PointGroup
|
||||
|
||||
## Begin PublicKey
|
||||
|
||||
def get_public_key(id) do
|
||||
|
|
|
@ -21,7 +21,8 @@ defmodule FarmbotOS.SysCalls do
|
|||
SetPinIOMode,
|
||||
PinControl,
|
||||
ResourceUpdate,
|
||||
Movement
|
||||
Movement,
|
||||
PointLookup
|
||||
}
|
||||
|
||||
alias FarmbotOS.Lua
|
||||
|
@ -121,6 +122,15 @@ defmodule FarmbotOS.SysCalls do
|
|||
@impl true
|
||||
defdelegate home(axis, speed), to: Movement
|
||||
|
||||
@impl true
|
||||
defdelegate point(kind, id), to: PointLookup
|
||||
|
||||
@impl true
|
||||
defdelegate get_point_group(type_or_id), to: PointLookup
|
||||
|
||||
@impl true
|
||||
defdelegate get_toolslot_for_tool(id), to: PointLookup
|
||||
|
||||
@impl true
|
||||
def log(message, force?) do
|
||||
if force? || FarmbotCore.Asset.fbos_config(:sequence_body_log) do
|
||||
|
@ -182,14 +192,6 @@ defmodule FarmbotOS.SysCalls do
|
|||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def point(kind, id) do
|
||||
case Asset.get_point(id: id) do
|
||||
nil -> {:error, "#{kind} not found"}
|
||||
%{x: x, y: y, z: z} -> %{x: x, y: y, z: z}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def emergency_lock do
|
||||
_ = FarmbotFirmware.command({:command_emergency_lock, []})
|
||||
|
@ -244,16 +246,6 @@ defmodule FarmbotOS.SysCalls do
|
|||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def get_toolslot_for_tool(id) do
|
||||
with %{id: ^id} <- Asset.get_tool(id: id),
|
||||
%{x: x, y: y, z: z} <- Asset.get_point(tool_id: id) do
|
||||
%{x: x, y: y, z: z}
|
||||
else
|
||||
nil -> {:error, "Could not find point for tool by id: #{id}"}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def sync() do
|
||||
FarmbotCore.Logger.busy(3, "Syncing")
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
defmodule FarmbotOS.SysCalls.PointLookup do
|
||||
alias FarmbotCore.Asset
|
||||
|
||||
def point(kind, id) do
|
||||
case Asset.get_point(id: id) do
|
||||
nil -> {:error, "#{kind} not found"}
|
||||
%{x: x, y: y, z: z} -> %{x: x, y: y, z: z}
|
||||
end
|
||||
end
|
||||
|
||||
def get_toolslot_for_tool(id) do
|
||||
with %{id: ^id} <- Asset.get_tool(id: id),
|
||||
%{x: x, y: y, z: z} <- Asset.get_point(tool_id: id) do
|
||||
%{x: x, y: y, z: z}
|
||||
else
|
||||
nil -> {:error, "Could not find point for tool by id: #{id}"}
|
||||
end
|
||||
end
|
||||
|
||||
def get_point_group(id) when is_number(id) do
|
||||
case Asset.get_point_group(id: id) do
|
||||
nil -> {:error, "Could not find PointGroup.#{id}"}
|
||||
%{point_ids: _} = group -> group
|
||||
end
|
||||
end
|
||||
|
||||
def get_point_group(type) when is_binary(type) do
|
||||
points = Asset.get_all_points_by_type(type)
|
||||
|
||||
Enum.reduce(points, %{point_ids: []}, fn
|
||||
%{id: id}, acc -> %{acc | point_ids: [id | acc.point_ids]}
|
||||
end)
|
||||
end
|
||||
end
|
|
@ -86,6 +86,11 @@ defmodule Farmbot.TestSupport.CeleryScript.TestSysCalls do
|
|||
call({:point, [type, id]})
|
||||
end
|
||||
|
||||
@impl true
|
||||
def get_point_group(type_or_id) do
|
||||
call({:get_point_group, [type_or_id]})
|
||||
end
|
||||
|
||||
@impl true
|
||||
def move_absolute(x, y, z, speed) do
|
||||
call({:move_absolute, [x, y, z, speed]})
|
||||
|
|
Loading…
Reference in New Issue