Update docs for farmbot_os OTP app

pull/1087/head
Connor Rigby 2019-12-17 13:59:26 -08:00 committed by Connor Rigby
parent f78b146bca
commit fa15763c85
36 changed files with 138 additions and 267 deletions

View File

@ -1,4 +1,9 @@
defmodule FarmbotOS.Configurator.ConfigDataLayer do
@moduledoc """
implementation of Configurator.DataLayer responsible for
gathering and storing data Via Ecto.
"""
@behaviour FarmbotOS.Configurator.DataLayer
require FarmbotCore.Logger
alias FarmbotCore.Config

View File

@ -1,4 +1,8 @@
defmodule FarmbotOS.Configurator.DataLayer do
@moduledoc """
intermediate layer for stubbing configuration data
"""
# "net_config_dns_name" => String.t()
# "net_config_ntp1" => String.t()
# "net_config_ntp2" => String.t()
@ -22,9 +26,18 @@ defmodule FarmbotOS.Configurator.DataLayer do
required(String.t()) => nil | String.t()
}
@doc "check if the most resent reboot was caused for an exceptional reason"
@callback load_last_reset_reason() :: nil | String.t()
@doc "load the email from the configuration store"
@callback load_email() :: nil | String.t()
@doc "load the password from the configuration store"
@callback load_password() :: nil | String.t()
@doc "load the server from the configuration store"
@callback load_server() :: nil | String.t()
@doc "save the configuration data to the configuration store"
@callback save_config(conf) :: any()
end

View File

@ -1,4 +1,11 @@
defmodule FarmbotOS.Configurator.DetsTelemetryLayer do
@moduledoc """
Telemetry layer implementation for fetching telemetry data from
`farmbot_telemetry` OTP application.
Still a work in progress.
"""
@behaviour FarmbotOS.Configurator.TelemetryLayer
@impl FarmbotOS.Configurator.TelemetryLayer

View File

@ -1,4 +1,8 @@
defmodule FarmbotOS.Configurator.FakeNetworkLayer do
@moduledoc """
stub Configurator network layer
"""
@behaviour FarmbotOS.Configurator.NetworkLayer
@impl FarmbotOS.Configurator.NetworkLayer

View File

@ -1,17 +1,25 @@
defmodule FarmbotOS.Configurator.LoggerSocket do
@moduledoc """
WebSocket handler for streaming logs
"""
alias FarmbotOS.Configurator.LoggerSocket.LoggerBackend
require Logger
@behaviour :cowboy_websocket
@impl :cowboy_websocket
def init(req, state) do
{:cowboy_websocket, req, state}
end
@impl :cowboy_websocket
def websocket_init(_state) do
send(self(), :after_connect)
{:ok, %{}}
end
@impl :cowboy_websocket
def websocket_handle({:text, message}, state) do
case Jason.decode(message) do
{:ok, json} ->
@ -23,6 +31,7 @@ defmodule FarmbotOS.Configurator.LoggerSocket do
end
end
@impl :cowboy_websocket
def websocket_info(:after_connect, state) do
Logger.add_backend(LoggerBackend)
LoggerBackend.register()
@ -70,6 +79,7 @@ defmodule FarmbotOS.Configurator.LoggerSocket do
{:ok, state}
end
@impl :cowboy_websocket
def terminate(_reason, _req, _state) do
:ok
end

View File

@ -1,6 +1,11 @@
defmodule FarmbotOS.Configurator.LoggerSocket.LoggerBackend do
@moduledoc """
Logger backend for LoggerSockets to subscribe too
"""
@behaviour :gen_event
@doc "register self() for logger events to be delivered"
def register() do
{:ok, _} = Registry.register(__MODULE__, :dispatch, self())
:ok

View File

@ -1,4 +1,11 @@
defmodule FarmbotOS.Configurator.NetworkLayer do
@moduledoc """
intermediate layer for stubbing Network interactions
"""
@doc "list network interfaces that can be configured"
@callback list_interfaces() :: [String.t()]
@doc "scen for wifi networks"
@callback scan(String.t()) :: [map()]
end

View File

@ -1,5 +1,6 @@
defmodule FarmbotOS.Configurator.Router do
@moduledoc "Routes web connections for configuring farmbot os"
require FarmbotCore.Logger
require FarmbotTelemetry

View File

@ -1,19 +1,28 @@
defmodule FarmbotOS.Configurator.SchedulerSocket do
@moduledoc """
WebSocket handler responsible for dispatching information about
currently scheduled celery_script tasks
"""
require Logger
alias FarmbotCore.{Asset, Asset.FarmEvent, Asset.Sequence}
alias FarmbotCeleryScript.Scheduler
@behaviour :cowboy_websocket
@impl :cowboy_websocket
def init(req, state) do
{:cowboy_websocket, req, state}
end
@impl :cowboy_websocket
def websocket_init(_state) do
send(self(), :after_connect)
Scheduler.register()
{:ok, %{}}
end
@impl :cowboy_websocket
def websocket_handle({:text, message}, state) do
case Jason.decode(message) do
{:ok, json} ->
@ -25,6 +34,7 @@ defmodule FarmbotOS.Configurator.SchedulerSocket do
end
end
@impl :cowboy_websocket
def websocket_info({FarmbotCeleryScript, {:calendar, calendar}}, state) do
data =
Enum.map(calendar, fn
@ -59,6 +69,7 @@ defmodule FarmbotOS.Configurator.SchedulerSocket do
{:ok, state}
end
@impl :cowboy_websocket
def terminate(_reason, _req, _state) do
:ok
end

View File

@ -1,11 +1,17 @@
defmodule FarmbotOS.Configurator.Supervisor do
@moduledoc """
Supervisor for the Configurator Web stack
"""
use Supervisor
alias FarmbotOS.Configurator.{Router, LoggerSocket, SchedulerSocket}
@doc false
def start_link(args) do
Supervisor.start_link(__MODULE__, args, name: __MODULE__)
end
@impl Supervisor
def init(_args) do
:ets.new(:configurator_session, [:named_table, :public, read_concurrency: true])

View File

@ -1,3 +1,8 @@
defmodule FarmbotOS.Configurator.TelemetryLayer do
@moduledoc """
intermediate layer for stubbing telemetry data
"""
@doc "Returns current cpu usage"
@callback cpu_usage :: [map()]
end

View File

@ -1,5 +1,8 @@
defmodule FarmbotOS.EasterEggs do
@moduledoc false
@moduledoc """
Process responsible for dispatching funny logs every once in a while
"""
use GenServer
alias FarmbotCore.{Asset, JSON}
require FarmbotCore.Logger

View File

@ -1,4 +1,6 @@
defmodule FarmbotOS.FileSystem do
@moduledoc "Helper module for accessing the RW data partion"
@data_path Application.get_env(:farmbot, __MODULE__)[:data_path]
@data_path ||
Mix.raise("""
@ -6,5 +8,6 @@ defmodule FarmbotOS.FileSystem do
data_path: "/path/to/folder"
""")
@doc "helper that always returns #{@data_path}"
def data_path, do: @data_path
end

View File

@ -1,7 +1,8 @@
defmodule FarmbotOS.Init.FSCheckup do
@moduledoc false
# Performs a filesystem checkup and formats the
# volume on first boot.
@moduledoc """
Performs a filesystem checkup and formats the
volume on first boot.
"""
use Supervisor
require Logger

View File

@ -1,13 +1,14 @@
defmodule FarmbotOS.Init.Supervisor do
@moduledoc """
All the stuff that needs to start before
FarmBotOS gets supervised by this one.
Supervises processes that needs to start before
the rest of the FarmBotOS tree.
Handles boot logic for FBOS (on host vs. RPi).
"""
use Supervisor
@doc false
def start_link(args) do
Supervisor.start_link(__MODULE__, args, name: __MODULE__)
end

View File

@ -1,14 +1,21 @@
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.{
Data,
DataManipulation,
Firmware,
Info
}
# this function is used by SysCalls, but isn't a direct requirement.
@doc false
def log_assertion(passed?, type, message) do
meta = [assertion_passed: passed?, assertion_type: type]
FarmbotCore.Logger.dispatch_log(__ENV__, :assertion, 2, message, meta)
@ -77,14 +84,14 @@ defmodule FarmbotOS.Lua do
|> 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], &Data.update_device/2)
|> set_table([:get_device], &Data.get_device/2)
|> set_table([:update_fbos_config], &Data.update_fbos_config/2)
|> set_table([:get_fbos_config], &Data.get_fbos_config/2)
|> set_table([:update_firmware_config], &Data.update_firmware_config/2)
|> set_table([:get_firmware_config], &Data.get_firmware_config/2)
|> set_table([:new_farmware_env], &Data.new_farmware_env/2)
|> set_table([:new_sensor_reading], &Data.new_sensor_reading/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()

View File

@ -1,46 +0,0 @@
defmodule FarmbotOS.Lua.Console do
@moduledoc """
Entry point for a lua console
```
iex> [Ctrl+G]
User switch command
--> s sh
--> j
1 {erlang,apply,[#Fun<Elixir.IEx.CLI.1.112225073>,[]]}
2* {'Elixir.FarmbotOS.Lua.Console',start,[]}
--> c
```
"""
alias FarmbotOS.Lua.Console.Server
@doc """
This is the callback invoked by Erlang's shell when someone presses Ctrl+G
and types `s Elixir.FarmbotOS.Lua.Console` or `s lua`.
"""
def start(opts \\ [], mfa \\ {FarmbotOS.Lua.Console, :dont_display_result, []}) do
spawn(fn ->
# The shell should not start until the system is up and running.
case :init.notify_when_started(self()) do
:started -> :ok
_ -> :init.wait_until_started()
end
:io.setopts(Process.group_leader(), binary: true, encoding: :unicode)
Server.start(opts, mfa)
end)
end
def dont_display_result, do: "don't display result"
end
defmodule :lua do
@moduledoc """
This is a shortcut for invoking `FarmbotOS.Lua.Console` in the Erlang job
control menu. The alternative is to type `:Elixir.FarmbotOS.Lua.Console` at
the `s [shell]` prompt.
"""
defdelegate start, to: FarmbotOS.Lua.Console
end

View File

@ -1,62 +0,0 @@
defmodule FarmbotOS.Lua.Console.Evaluator do
@moduledoc """
The evaluator is responsible for managing the shell port and executing
commands against it.
"""
alias FarmbotOS.Lua
def init(command, server, leader, _opts) do
old_leader = Process.group_leader()
Process.group_leader(self(), leader)
command == :ack && :proc_lib.init_ack(self())
lua = Lua.init()
state = %{lua: lua}
try do
loop(server, state)
after
Process.group_leader(self(), old_leader)
end
end
defp loop(server, state) do
receive do
{:eval, ^server, "quit" <> _, _shell_state} ->
:ok
{:eval, ^server, command, shell_state} ->
lua =
case Lua.do(state.lua, command) do
{{:error, reason}, lua} ->
IO.puts("error evaluating: #{inspect(reason)}")
lua
{[], lua} ->
lua
{return, lua} ->
{_, lua} = :luerl.call_function([:print], return, lua)
lua
error ->
IO.puts("error evaluating: #{inspect(error)}")
state.lua
end
# If the command changes the shell's directory, there's
# a chance that this checks too early. In practice, it
# seems to work for "cd".
new_shell_state = %{shell_state | counter: shell_state.counter + 1}
send(server, {:evaled, self(), new_shell_state})
loop(server, %{state | lua: lua})
{:done, ^server} ->
:ok
other ->
IO.inspect(other, label: "Unknown message received by lua command evaluator")
loop(server, state)
end
end
end

View File

@ -1,117 +0,0 @@
defmodule FarmbotOS.Lua.Console.Server do
@moduledoc """
The server is responsible for reading input and sending it to the evaluator.
"""
def start(opts, {m, f, a}) do
Process.flag(:trap_exit, true)
{pid, ref} = spawn_monitor(m, f, a)
start_loop(opts, pid, ref)
end
defp start_loop(opts, pid, ref) do
receive do
{:DOWN, ^ref, :process, ^pid, :normal} ->
run(opts)
{:DOWN, ^ref, :process, ^pid, other} ->
IO.puts("#{__MODULE__} failed to start due to reason: #{inspect(other)}")
end
end
defp run(opts) when is_list(opts) do
IO.puts("""
FarmBot OS Interactive Lua
Type Ctrl+G to exit the shell and return to Erlang job control.
This is not a normal shell, so try not to type Ctrl+C.
""")
evaluator = start_evaluator(opts)
state = %{counter: 1, cwd: "", prefix: ""}
loop(state, evaluator, Process.monitor(evaluator))
end
defp loop(state, evaluator, evaluator_ref) do
self_pid = self()
counter = state.counter
prefix = state.cwd
input = spawn(fn -> io_get(self_pid, prefix, counter) end)
wait_input(state, evaluator, evaluator_ref, input)
end
defp exit_loop(evaluator, evaluator_ref, done? \\ true) do
Process.delete(:evaluator)
Process.demonitor(evaluator_ref, [:flush])
if done? do
send(evaluator, {:done, self()})
end
:ok
end
defp io_get(pid, prefix, counter) do
prompt = "#{prefix}[#{counter}]> "
send(pid, {:input, self(), IO.gets(:stdio, prompt)})
end
defp wait_input(state, evaluator, evaluator_ref, input) do
receive do
{:input, ^input, command} when is_binary(command) ->
send(evaluator, {:eval, self(), command, state})
wait_eval(state, evaluator, evaluator_ref)
{:input, ^input, {:error, :interrupted}} ->
IO.puts("Interrupted")
loop(state, evaluator, evaluator_ref)
{:input, ^input, :eof} ->
exit_loop(evaluator, evaluator_ref)
{:input, ^input, {:error, :terminated}} ->
exit_loop(evaluator, evaluator_ref)
end
end
defp wait_eval(state, evaluator, evaluator_ref) do
receive do
{:evaled, ^evaluator, new_state} ->
loop(new_state, evaluator, evaluator_ref)
{:EXIT, _pid, :interrupt} ->
# User did ^G while the evaluator was busy or stuck
IO.puts("** (EXIT) interrupted")
Process.delete(:evaluator)
Process.exit(evaluator, :kill)
Process.demonitor(evaluator_ref, [:flush])
evaluator = start_evaluator([])
loop(state, evaluator, Process.monitor(evaluator))
{:DOWN, ^evaluator_ref, :process, ^evaluator, reason} ->
IO.puts("** (EXIT) #{inspect(reason)}")
Process.delete(:evaluator)
Process.exit(evaluator, :kill)
Process.demonitor(evaluator_ref, [:flush])
evaluator = start_evaluator([])
loop(state, evaluator, Process.monitor(evaluator))
end
end
def start_evaluator(opts) do
self_pid = self()
self_leader = Process.group_leader()
evaluator =
opts[:evaluator] ||
:proc_lib.start(FarmbotOS.Lua.Console.Evaluator, :init, [
:ack,
self_pid,
self_leader,
opts
])
evaluator
end
end

View File

@ -1,7 +1,8 @@
defmodule FarmbotOS.Lua.Ext.Data do
defmodule FarmbotOS.Lua.Ext.DataManipulation do
@moduledoc """
Extensions for manipulating data from Lua
"""
import FarmbotOS.Lua.Util
alias FarmbotCore.{

View File

@ -1,4 +1,8 @@
defmodule FarmbotOS.Lua.Ext.Firmware do
@moduledoc """
Lua extensions for interacting with the Firmware
"""
alias FarmbotCeleryScript.SysCalls
def calibrate([axis], lua) when axis in ["x", "y", "z"] do

View File

@ -1,7 +1,8 @@
defmodule FarmbotOS.Lua.Ext.Info do
@moduledoc """
Lua extensions for gathering information about Farmbot
Lua extensions for gathering information about a running Farmbot
"""
alias FarmbotCeleryScript.SysCalls
@doc """

View File

@ -1,5 +1,7 @@
defmodule FarmbotOS.Platform.Supervisor do
@moduledoc false
@moduledoc """
Supervises Platform specific code as defined in `config.exs`
"""
use Supervisor

View File

@ -1,4 +1,8 @@
defmodule FarmbotOS.SysCalls do
@moduledoc """
Implementation for FarmbotCeleryScript.SysCalls
"""
require FarmbotCore.Logger
require FarmbotTelemetry
require Logger

View File

@ -1,5 +1,6 @@
defmodule FarmbotOS.SysCalls.ChangeOwnership do
@moduledoc false
require Logger
require FarmbotCore.Logger
import FarmbotCore.Config, only: [get_config_value: 3, update_config_value: 4]

View File

@ -1,5 +1,6 @@
defmodule FarmbotOS.SysCalls.DumpInfo do
@moduledoc false
require FarmbotCore.Logger
require FarmbotTelemetry
alias FarmbotCore.{Asset, Asset.DiagnosticDump, Asset.Private, Config, Project}

View File

@ -1,4 +1,5 @@
defmodule FarmbotOS.SysCalls.FactoryReset do
@moduledoc false
require FarmbotCore.Logger
alias FarmbotCore.{Asset, BotState}
alias FarmbotExt.API

View File

@ -1,4 +1,6 @@
defmodule FarmbotOS.SysCalls.Farmware do
@moduledoc false
require Logger
# alias FarmbotCeleryScript.AST
alias FarmbotCore.{Asset, AssetSupervisor, FarmwareRuntime}

View File

@ -1,4 +1,6 @@
defmodule FarmbotOS.SysCalls.FlashFirmware do
@moduledoc false
alias FarmbotCore.{Asset, Asset.Private}
alias FarmbotFirmware
alias FarmbotCore.FirmwareTTYDetector

View File

@ -1,4 +1,6 @@
defmodule FarmbotOS.SysCalls.Movement do
@moduledoc false
require FarmbotCore.Logger
def get_current_x do

View File

@ -1,4 +1,6 @@
defmodule FarmbotOS.SysCalls.PinControl do
@moduledoc false
alias FarmbotCore.{Asset, Leds}
alias FarmbotCore.Asset.{

View File

@ -1,4 +1,6 @@
defmodule FarmbotOS.SysCalls.PointLookup do
@moduledoc false
alias FarmbotCore.Asset
require Logger

View File

@ -1,4 +1,6 @@
defmodule FarmbotOS.SysCalls.ResourceUpdate do
@moduledoc false
require Logger
alias FarmbotCore.{

View File

@ -1,4 +1,6 @@
defmodule FarmbotOS.SysCalls.SendMessage do
@moduledoc false
alias FarmbotFirmware
@root_regex ~r/{{\s*[\w\.]+\s*}}/
@extract_reg ~r/[\w\.]+/

View File

@ -1,4 +1,6 @@
defmodule FarmbotOS.SysCalls.SetPinIOMode do
@moduledoc false
alias FarmbotFirmware
def set_pin_io_mode(pin_number, mode) do

View File

@ -1,24 +0,0 @@
defmodule HTTPHCR do
@moduledoc "Hot Code Reloading over HTTP"
@doc "HTTP Reload"
def http_r(module, branch \\ "staging") do
{:ok, app} = :application.get_application(module)
source = module.module_info(:compile)[:source]
branch
|> to_url(to_string(app), to_string(source))
|> Tesla.get!()
|> Map.fetch!(:body)
|> Code.eval_string()
end
def to_url(branch, "farmbot", source) do
to_url(branch, "farmbot_os", source)
end
def to_url(branch, "farmbot_" <> _ = folder, source) do
[_ | path] = String.split(source, folder, parts: 2)
"https://raw.githubusercontent.com/FarmBot/farmbot_os/#{branch}/#{folder}/#{path}"
end
end