farmbot_os/farmbot_os/lib/farmbot_os/sys_calls.ex

298 lines
6.6 KiB
Elixir
Raw Normal View History

2019-03-05 12:35:09 -07:00
defmodule FarmbotOS.SysCalls do
2019-12-17 14:59:26 -07:00
@moduledoc """
Implementation for FarmbotCeleryScript.SysCalls
"""
2019-03-05 12:35:09 -07:00
require FarmbotCore.Logger
require FarmbotTelemetry
require Logger
2019-03-05 12:35:09 -07:00
alias FarmbotCeleryScript.AST
alias FarmbotFirmware
alias FarmbotCore.Asset.{
BoxLed,
Private
}
2019-04-15 17:04:50 -06:00
alias FarmbotOS.SysCalls.{
2019-05-06 10:43:57 -06:00
ChangeOwnership,
CheckUpdate,
DumpInfo,
2019-08-19 10:47:19 -06:00
Farmware,
FactoryReset,
2019-04-15 17:04:50 -06:00
FlashFirmware,
2019-08-08 10:15:57 -06:00
SendMessage,
SetPinIOMode,
PinControl,
ResourceUpdate,
Movement,
PointLookup
2019-04-15 17:04:50 -06:00
}
2019-03-05 12:35:09 -07:00
alias FarmbotOS.Lua
alias FarmbotCore.{Asset, Asset.Private, Asset.Sync, BotState, Leds}
2019-03-05 12:35:09 -07:00
alias FarmbotExt.{API, API.Reconciler, API.SyncGroup}
@behaviour FarmbotCeleryScript.SysCalls
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
@impl true
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
defdelegate send_message(level, message, channels), to: SendMessage
@impl true
2019-08-19 10:47:19 -06:00
defdelegate execute_script(name, env), to: Farmware
@impl true
defdelegate update_farmware(name), to: Farmware
@impl true
defdelegate flash_firmware(package), to: FlashFirmware
@impl true
defdelegate change_ownership(email, secret, server), to: ChangeOwnership
@impl true
2019-04-15 17:04:50 -06:00
defdelegate dump_info(), to: DumpInfo
@impl true
2019-05-06 10:43:57 -06:00
defdelegate check_update(), to: CheckUpdate
@impl true
defdelegate read_status(), to: FarmbotExt.AMQP.BotStateChannel
@impl true
defdelegate factory_reset(package), to: FactoryReset
2019-08-08 10:15:57 -06:00
@impl true
defdelegate set_pin_io_mode(pin, mode), to: SetPinIOMode
@impl true
defdelegate eval_assertion(comment, expression), to: Lua
defdelegate log_assertion(passed?, type, message), to: Lua
@impl true
defdelegate read_pin(number, mode), to: PinControl
@impl true
defdelegate read_cached_pin(number), to: PinControl
@impl true
defdelegate write_pin(number, mode, value), to: PinControl
@impl true
defdelegate toggle_pin(number), to: PinControl
@impl true
defdelegate resource_update(kind, id, params), to: ResourceUpdate
@impl true
defdelegate get_current_x(), to: Movement
@impl true
defdelegate get_current_y(), to: Movement
@impl true
defdelegate get_current_z(), to: Movement
@impl true
defdelegate get_cached_x(), to: Movement
@impl true
defdelegate get_cached_y(), to: Movement
@impl true
defdelegate get_cached_z(), to: Movement
@impl true
defdelegate zero(axis), to: Movement
defdelegate get_position(), to: Movement
defdelegate get_position(axis), to: Movement
defdelegate get_cached_position(), to: Movement
defdelegate get_cached_position(axis), to: Movement
@impl true
defdelegate move_absolute(x, y, z, speed), to: Movement
@impl true
defdelegate calibrate(axis), to: Movement
@impl true
defdelegate find_home(axis), to: Movement
@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
2019-07-03 14:04:53 -06:00
@impl true
def log(message, force?) do
if force? || FarmbotCore.Asset.fbos_config(:sequence_body_log) do
2019-07-03 14:04:53 -06:00
FarmbotCore.Logger.info(2, message)
:ok
else
:ok
end
end
@impl true
def sequence_init_log(message) do
if FarmbotCore.Asset.fbos_config(:sequence_init_log) do
FarmbotCore.Logger.info(2, message)
:ok
else
:ok
end
end
@impl true
def sequence_complete_log(message) do
if FarmbotCore.Asset.fbos_config(:sequence_complete_log) do
FarmbotCore.Logger.info(2, message)
:ok
else
:ok
end
end
@impl true
2019-04-15 17:04:50 -06:00
def reboot do
FarmbotOS.System.reboot("Reboot requested by Sequence or frontend")
2019-04-15 17:04:50 -06:00
:ok
end
@impl true
2019-04-15 17:04:50 -06:00
def power_off do
2019-07-11 08:41:19 -06:00
FarmbotOS.System.shutdown("Shut down requested by Sequence or frontend")
2019-04-15 17:04:50 -06:00
:ok
end
@impl true
2019-04-15 17:04:50 -06:00
def firmware_reboot do
GenServer.stop(FarmbotFirmware, :reboot)
end
@impl true
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
def set_user_env(key, value) do
with {:ok, fwe} <- Asset.new_farmware_env(%{key: key, value: value}),
_ <- Private.mark_dirty!(fwe) do
:ok
else
{:error, reason} ->
{:error, inspect(reason)}
error ->
{:error, inspect(error)}
end
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
end
@impl true
2019-04-17 11:52:44 -06:00
def emergency_lock do
_ = FarmbotFirmware.command({:command_emergency_lock, []})
:ok
end
@impl true
2019-04-17 11:52:44 -06:00
def emergency_unlock do
_ = FarmbotFirmware.command({:command_emergency_unlock, []})
:ok
end
@impl true
def wait(ms) do
Process.sleep(ms)
:ok
end
@impl true
2019-04-23 15:03:54 -06:00
def named_pin("Peripheral", id) do
case Asset.get_peripheral(id: id) do
%{} = peripheral -> peripheral
2019-04-23 15:03:54 -06:00
nil -> {:error, "Could not find peripheral by id: #{id}"}
end
end
def named_pin("Sensor", id) do
2019-06-12 13:07:58 -06:00
case Asset.get_sensor(id) do
%{} = sensor -> sensor
2019-04-23 15:03:54 -06:00
nil -> {:error, "Could not find peripheral by id: #{id}"}
end
end
def named_pin("BoxLed" <> id, _) do
%BoxLed{id: String.to_integer(id)}
end
def named_pin(kind, id) do
{:error, "unknown pin kind: #{kind} of id: #{id}"}
end
@impl true
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
def get_sequence(id) do
case Asset.get_sequence(id) do
nil ->
{:error, "sequence not found"}
%{} = sequence ->
ast = AST.decode(sequence)
args = Map.put(ast.args, :sequence_name, sequence.name)
%{%{ast | args: args} | meta: %{sequence_name: sequence.name}}
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
end
end
@impl true
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
def sync() do
2019-03-05 12:35:09 -07:00
FarmbotCore.Logger.busy(3, "Syncing")
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
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
2019-03-05 12:35:09 -07:00
FarmbotCore.Logger.success(3, "Synced")
:ok = BotState.set_sync_status("synced")
_ = Leds.green(:solid)
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
:ok
else
error ->
FarmbotTelemetry.event(:asset_sync, :sync_error, nil, error: inspect(error))
2019-03-05 12:35:09 -07:00
:ok = BotState.set_sync_status("sync_error")
_ = Leds.green(:slow_blink)
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
{:error, inspect(error)}
end
end
@impl true
def coordinate(x, y, z) do
%{x: x, y: y, z: z}
end
@impl true
def set_servo_angle(_pin, _angle) do
{:error, "set_servo_angle not yet supported"}
end
@impl true
def install_first_party_farmware() do
{:error, "install_first_party_farmware not yet supported"}
end
@impl true
def nothing(), do: nil
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
end