Refactor farmbot application into separate folders/apps.
parent
0d569301f1
commit
8c34f2eacd
63
Makefile
63
Makefile
|
@ -3,7 +3,6 @@
|
|||
|
||||
MIX_ENV := $(MIX_ENV)
|
||||
MIX_TARGET := $(MIX_TARGET)
|
||||
SLACK_CHANNEL := $(SLACK_CHANNEL)
|
||||
|
||||
ifeq ($(MIX_ENV),)
|
||||
MIX_ENV := dev
|
||||
|
@ -13,53 +12,23 @@ ifeq ($(MIX_TARGET),)
|
|||
MIX_TARGET := host
|
||||
endif
|
||||
|
||||
PROJECTS := farmbot_celery_script \
|
||||
farmbot_core \
|
||||
farmbot_ext \
|
||||
farmbot_os
|
||||
|
||||
all: help
|
||||
|
||||
help:
|
||||
@echo "no"
|
||||
@echo "Usage: "
|
||||
@echo " make [target]"
|
||||
@echo "TARGETS: "
|
||||
@echo " clean - clean all."
|
||||
|
||||
farmbot_celery_script_clean:
|
||||
cd farmbot_celery_script && \
|
||||
rm -rf _build deps
|
||||
|
||||
farmbot_core_clean:
|
||||
cd farmbot_core && \
|
||||
make clean && \
|
||||
rm -rf priv/*.hex &&\
|
||||
rm -rf priv/*.so &&\
|
||||
rm -rf ./.*.sqlite3 &&\
|
||||
rm -rf _build deps
|
||||
|
||||
farmbot_ext_clean:
|
||||
cd farmbot_ext && \
|
||||
rm -rf ./.*.sqlite3 &&\
|
||||
rm -rf _build deps
|
||||
|
||||
farmbot_os_clean:
|
||||
cd farmbot_os && \
|
||||
rm -rf _build deps
|
||||
|
||||
clean: farmbot_celery_script_clean farmbot_core_clean farmbot_ext_clean farmbot_os_clean
|
||||
|
||||
farmbot_core_test:
|
||||
cd farmbot_core && \
|
||||
MIX_ENV=test mix deps.get && \
|
||||
MIX_ENV=test mix ecto.migrate && \
|
||||
MIX_ENV=test mix compile
|
||||
|
||||
farmbot_ext_test:
|
||||
cd farmbot_ext && \
|
||||
MIX_ENV=test SKIP_ARDUINO_BUILD=1 mix deps.get && \
|
||||
MIX_ENV=test SKIP_ARDUINO_BUILD=1 mix ecto.migrate && \
|
||||
MIX_ENV=test SKIP_ARDUINO_BUILD=1 mix compile
|
||||
|
||||
farmbot_os_test:
|
||||
cd farmbot_os && \
|
||||
MIX_ENV=test SKIP_ARDUINO_BUILD=1 mix deps.get && \
|
||||
MIX_ENV=test SKIP_ARDUINO_BUILD=1 mix compile
|
||||
|
||||
test: farmbot_core_test farmbot_ext_test farmbot_os_test
|
||||
|
||||
farmbot_os_firmware:
|
||||
cd farmbot_os && \
|
||||
MIX_ENV=$(MIX_ENV) MIX_TARGET=$(MIX_TARGET) mix do deps.get, firmware
|
||||
clean:
|
||||
@for project in $(PROJECTS) ; do \
|
||||
echo cleaning $$project ; \
|
||||
rm -rf $$project/_build ; \
|
||||
rm -rf $$project/deps ; \
|
||||
rm -rf $$project/priv/*.so ; \
|
||||
done
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
erlang 21.0.4
|
||||
elixir 1.6.6-otp-21
|
|
@ -0,0 +1 @@
|
|||
../.tool-versions
|
|
@ -30,214 +30,23 @@ defmodule Farmbot.Asset do
|
|||
import Farmbot.Config, only: [update_config_value: 4]
|
||||
require Logger
|
||||
|
||||
@device_fields ~W(id name timezone)
|
||||
@farm_events_fields ~W(calendar end_time executable_id executable_type id repeat start_time time_unit)
|
||||
@farmware_envs_fields ~W(id key value)
|
||||
@farmware_installations_fields ~W(id url first_party)
|
||||
@peripherals_fields ~W(id label mode pin)
|
||||
@pin_bindings_fields ~W(id pin_num sequence_id special_action)
|
||||
@points_fields ~W(id meta name pointer_type tool_id x y z)
|
||||
@regimens_fields ~W(farm_event_id id name regimen_items)
|
||||
@sensors_fields ~W(id label mode pin)
|
||||
@sequences_fields ~W(args body id kind name)
|
||||
@tools_fields ~W(id name)
|
||||
defdelegate to_asset(data, kind), to: Farmbot.Asset.Converter
|
||||
|
||||
def to_asset(body, kind) when is_binary(kind) do
|
||||
camel_kind = Module.concat(["Farmbot", "Asset", Macro.camelize(kind)])
|
||||
to_asset(body, camel_kind)
|
||||
end
|
||||
def fragment_sync(_), do: :ok
|
||||
def full_sync(_, _fun), do: :ok
|
||||
|
||||
def to_asset(body, Device), do: resource_decode(body, @device_fields, Device)
|
||||
def to_asset(body, FarmEvent), do: resource_decode(body, @farm_events_fields, FarmEvent)
|
||||
def to_asset(body, FarmwareEnv), do: resource_decode(body, @farmware_envs_fields, FarmwareEnv)
|
||||
def to_asset(body, FarmwareInstallation), do: resource_decode(body, @farmware_installations_fields, FarmwareInstallation)
|
||||
def to_asset(body, Peripheral), do: resource_decode(body, @peripherals_fields, Peripheral)
|
||||
def to_asset(body, PinBinding), do: resource_decode(body, @pin_bindings_fields, PinBinding)
|
||||
def to_asset(body, Point), do: resource_decode(body, @points_fields, Point)
|
||||
def to_asset(body, Regimen), do: resource_decode(body, @regimens_fields, Regimen)
|
||||
def to_asset(body, Sensor), do: resource_decode(body, @sensors_fields, Sensor)
|
||||
def to_asset(body, Sequence), do: resource_decode(body, @sequences_fields, Sequence)
|
||||
def to_asset(body, Tool), do: resource_decode(body, @tools_fields, Tool)
|
||||
|
||||
def resource_decode(data, fields, kind) when is_list(data),
|
||||
do: Enum.map(data, &resource_decode(&1, fields, kind))
|
||||
|
||||
def resource_decode(data, fields, kind) do
|
||||
data
|
||||
|> Map.take(fields)
|
||||
|> Enum.map(&string_to_atom/1)
|
||||
|> into_struct(kind)
|
||||
end
|
||||
|
||||
def string_to_atom({k, v}), do: {String.to_atom(k), v}
|
||||
def into_struct(data, kind), do: struct(kind, data)
|
||||
|
||||
# TODO(Connor) - 2018-08-05 *_sync should upload dirty artifacts
|
||||
# before applying sync commands?
|
||||
def fragment_sync(verbosity \\ 1) do
|
||||
Farmbot.Logger.busy verbosity, "Syncing"
|
||||
Farmbot.Registry.dispatch(__MODULE__, {:sync_status, :syncing})
|
||||
all_sync_cmds = all_sync_cmds()
|
||||
|
||||
Repo.transaction fn() ->
|
||||
for cmd <- all_sync_cmds do
|
||||
apply_sync_cmd(cmd)
|
||||
end
|
||||
end
|
||||
|
||||
destroy_all_sync_cmds()
|
||||
Farmbot.Registry.dispatch(__MODULE__, {:sync_status, :synced})
|
||||
Farmbot.Logger.success verbosity, "Synced"
|
||||
:ok
|
||||
end
|
||||
|
||||
def full_sync(verbosity \\ 1, fetch_fun) do
|
||||
Farmbot.Logger.busy verbosity, "Syncing"
|
||||
Farmbot.Registry.dispatch(__MODULE__, {:sync_status, :syncing})
|
||||
results = try do
|
||||
fetch_fun.()
|
||||
rescue
|
||||
ex ->
|
||||
Farmbot.Registry.dispatch(__MODULE__, {:sync_status, :sync_error})
|
||||
message = Exception.message(ex)
|
||||
Logger.error "Fetching resources failed: #{message}"
|
||||
update_config_value(:bool, "settings", "needs_http_sync", true)
|
||||
{:error, message}
|
||||
end
|
||||
|
||||
case results do
|
||||
{:ok, all_sync_cmds} when is_list(all_sync_cmds) ->
|
||||
Repo.transaction fn() ->
|
||||
:ok = Farmbot.Asset.clear_all_data()
|
||||
for cmd <- all_sync_cmds do
|
||||
apply_sync_cmd(cmd)
|
||||
end
|
||||
end
|
||||
destroy_all_sync_cmds()
|
||||
Farmbot.Registry.dispatch(__MODULE__, {:sync_status, :synced})
|
||||
Farmbot.Logger.success verbosity, "Synced"
|
||||
update_config_value(:bool, "settings", "needs_http_sync", false)
|
||||
:ok
|
||||
{:error, reason} when is_binary(reason) ->
|
||||
destroy_all_sync_cmds()
|
||||
Farmbot.Registry.dispatch(__MODULE__, {:sync_status, :sync_error})
|
||||
Farmbot.Logger.error verbosity, "Sync error: #{reason}"
|
||||
update_config_value(:bool, "settings", "needs_http_sync", true)
|
||||
:ok
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def apply_sync_cmd(cmd) do
|
||||
mod = Module.concat(["Farmbot", "Asset", cmd.kind])
|
||||
if Code.ensure_loaded?(mod) do
|
||||
Farmbot.Registry.dispatch(__MODULE__, {:sync_status, :syncing})
|
||||
old = Repo.snapshot()
|
||||
Farmbot.Logger.debug(3, "Syncing #{cmd.kind}")
|
||||
try do
|
||||
do_apply_sync_cmd(cmd)
|
||||
rescue
|
||||
e ->
|
||||
Farmbot.Logger.error(1, "Error syncing: #{mod}: #{Exception.message(e)}")
|
||||
end
|
||||
new = Repo.snapshot()
|
||||
diff = Snapshot.diff(old, new)
|
||||
dispatch_sync(diff)
|
||||
else
|
||||
Farmbot.Logger.warn(3, "Unknown module: #{mod} #{inspect(cmd)}")
|
||||
end
|
||||
destroy_sync_cmd(cmd)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def dispatch_sync(diff) do
|
||||
for deletion <- diff.deletions do
|
||||
Farmbot.Registry.dispatch(__MODULE__, {:deletion, deletion})
|
||||
end
|
||||
|
||||
for update <- diff.updates do
|
||||
Farmbot.Registry.dispatch(__MODULE__, {:update, update})
|
||||
end
|
||||
|
||||
for addition <- diff.additions do
|
||||
Farmbot.Registry.dispatch(__MODULE__, {:addition, addition})
|
||||
end
|
||||
|
||||
Farmbot.Registry.dispatch(__MODULE__, {:sync_status, :synced})
|
||||
end
|
||||
|
||||
# When `body` is nil, it means an object was deleted.
|
||||
def do_apply_sync_cmd(%{body: nil, remote_id: id, kind: kind}) do
|
||||
mod = Module.concat(["Farmbot", "Asset", kind])
|
||||
case Repo.one(from m in mod, where: m.id == ^id) do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
existing ->
|
||||
Repo.delete!(existing)
|
||||
:ok
|
||||
@doc "Information about _this_ device."
|
||||
def device do
|
||||
case Repo.all(Device) do
|
||||
[device] -> device
|
||||
[] -> nil
|
||||
devices when is_list(devices) ->
|
||||
Repo.delete_all(Device)
|
||||
raise "There should only ever be 1 device!"
|
||||
end
|
||||
end
|
||||
|
||||
def do_apply_sync_cmd(%{body: obj, remote_id: id, kind: kind}) do
|
||||
not_struct = strip_struct(obj)
|
||||
mod = Module.concat(["Farmbot", "Asset", kind])
|
||||
# We need to check if this object exists in the database.
|
||||
case Repo.one(from m in mod, where: m.id == ^id) do
|
||||
# If it does not, just return the newly created object.
|
||||
nil ->
|
||||
change = mod.changeset(struct(mod, not_struct), not_struct)
|
||||
Repo.insert!(change)
|
||||
:ok
|
||||
# if there is an existing record, copy the ecto meta from the old
|
||||
# record. This allows `insert_or_update` to work properly.
|
||||
existing ->
|
||||
existing
|
||||
|> Ecto.Changeset.change(not_struct)
|
||||
|> Repo.update!()
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp strip_struct(%{__struct__: _, __meta__: _} = struct) do
|
||||
Map.from_struct(struct) |> Map.delete(:__meta__)
|
||||
end
|
||||
|
||||
defp strip_struct(already_map), do: already_map
|
||||
|
||||
@doc """
|
||||
Register a sync message from an external source.
|
||||
This is like a snippit of the changes that have happened.
|
||||
`sync_cmd`s should only be applied on `sync`ing.
|
||||
`sync_cmd`s are _not_ a source of truth for transactions that have been applied.
|
||||
Use the `Farmbot.Asset.Registry` for these types of events.
|
||||
"""
|
||||
def register_sync_cmd(remote_id, kind, body) when is_binary(kind) do
|
||||
new_sync_cmd(remote_id, kind, body)
|
||||
|> SyncCmd.changeset()
|
||||
|> Repo.insert!()
|
||||
end
|
||||
|
||||
def new_sync_cmd(remote_id, kind, body)
|
||||
when is_integer(remote_id) when is_binary(kind)
|
||||
do
|
||||
struct(SyncCmd, %{remote_id: remote_id, kind: kind, body: body})
|
||||
end
|
||||
|
||||
@doc "Destroy all sync cmds locally."
|
||||
def destroy_all_sync_cmds do
|
||||
Repo.delete_all(SyncCmd)
|
||||
end
|
||||
|
||||
def all_sync_cmds do
|
||||
Repo.all(SyncCmd)
|
||||
end
|
||||
|
||||
def destroy_sync_cmd(%SyncCmd{id: nil} = cmd), do: {:ok, cmd}
|
||||
def destroy_sync_cmd(%SyncCmd{} = cmd) do
|
||||
Repo.delete(cmd)
|
||||
end
|
||||
|
||||
@doc "Get all pin bindings."
|
||||
def all_pin_bindings do
|
||||
Repo.all(PinBinding)
|
||||
end
|
||||
|
@ -280,37 +89,6 @@ defmodule Farmbot.Asset do
|
|||
end
|
||||
end
|
||||
|
||||
def clear_all_data do
|
||||
# remote assets.
|
||||
Repo.delete_all(Device)
|
||||
Repo.delete_all(FarmEvent)
|
||||
Repo.delete_all(FarmwareEnv)
|
||||
Repo.delete_all(FarmwareInstallation)
|
||||
Repo.delete_all(Peripheral)
|
||||
Repo.delete_all(PinBinding)
|
||||
Repo.delete_all(Point)
|
||||
Repo.delete_all(Regimen)
|
||||
Repo.delete_all(Sensor)
|
||||
Repo.delete_all(Sequence)
|
||||
Repo.delete_all(Tool)
|
||||
|
||||
# Interanal assets.
|
||||
Repo.delete_all(PersistentRegimen)
|
||||
Repo.delete_all(SyncCmd)
|
||||
:ok
|
||||
end
|
||||
|
||||
@doc "Information about _this_ device."
|
||||
def device do
|
||||
case Repo.all(Device) do
|
||||
[device] -> device
|
||||
[] -> nil
|
||||
devices when is_list(devices) ->
|
||||
Repo.delete_all(Device)
|
||||
raise "There should only ever be 1 device!"
|
||||
end
|
||||
end
|
||||
|
||||
@doc "Get a Peripheral by it's id."
|
||||
def get_peripheral_by_id(peripheral_id) do
|
||||
Repo.one(from(p in Peripheral, where: p.id == ^peripheral_id))
|
||||
|
@ -389,4 +167,25 @@ defmodule Farmbot.Asset do
|
|||
def get_farm_event_by_id(feid) do
|
||||
Repo.one(from(fe in FarmEvent, where: fe.id == ^feid))
|
||||
end
|
||||
|
||||
@doc "Clear all data stored in the Asset Repo."
|
||||
def clear_all_data do
|
||||
# remote assets.
|
||||
Repo.delete_all(Device)
|
||||
Repo.delete_all(FarmEvent)
|
||||
Repo.delete_all(FarmwareEnv)
|
||||
Repo.delete_all(FarmwareInstallation)
|
||||
Repo.delete_all(Peripheral)
|
||||
Repo.delete_all(PinBinding)
|
||||
Repo.delete_all(Point)
|
||||
Repo.delete_all(Regimen)
|
||||
Repo.delete_all(Sensor)
|
||||
Repo.delete_all(Sequence)
|
||||
Repo.delete_all(Tool)
|
||||
|
||||
# Interanal assets.
|
||||
Repo.delete_all(PersistentRegimen)
|
||||
Repo.delete_all(SyncCmd)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
defmodule Farmbot.Asset.Converter do
|
||||
alias Asset.{
|
||||
Device,
|
||||
FarmEvent,
|
||||
FarmwareEnv,
|
||||
FarmwareInstallation,
|
||||
Peripheral,
|
||||
PinBinding,
|
||||
Point,
|
||||
Regimen,
|
||||
Sensor,
|
||||
Sequence,
|
||||
Tool,
|
||||
}
|
||||
|
||||
@device_fields ~W(id name timezone)
|
||||
@farm_events_fields ~W(calendar end_time executable_id executable_type id repeat start_time time_unit)
|
||||
@farmware_envs_fields ~W(id key value)
|
||||
@farmware_installations_fields ~W(id url first_party)
|
||||
@peripherals_fields ~W(id label mode pin)
|
||||
@pin_bindings_fields ~W(id pin_num sequence_id special_action)
|
||||
@points_fields ~W(id meta name pointer_type tool_id x y z)
|
||||
@regimens_fields ~W(farm_event_id id name regimen_items)
|
||||
@sensors_fields ~W(id label mode pin)
|
||||
@sequences_fields ~W(args body id kind name)
|
||||
@tools_fields ~W(id name)
|
||||
|
||||
@doc "Converts data to Farmbot Asset types."
|
||||
def to_asset(body, kind) when is_binary(kind) do
|
||||
camel_kind = Module.concat(["Farmbot", "Asset", Macro.camelize(kind)])
|
||||
to_asset(body, camel_kind)
|
||||
end
|
||||
|
||||
def to_asset(body, Device), do: resource_decode(body, @device_fields, Device)
|
||||
def to_asset(body, FarmEvent), do: resource_decode(body, @farm_events_fields, FarmEvent)
|
||||
def to_asset(body, FarmwareEnv), do: resource_decode(body, @farmware_envs_fields, FarmwareEnv)
|
||||
def to_asset(body, FarmwareInstallation), do: resource_decode(body, @farmware_installations_fields, FarmwareInstallation)
|
||||
def to_asset(body, Peripheral), do: resource_decode(body, @peripherals_fields, Peripheral)
|
||||
def to_asset(body, PinBinding), do: resource_decode(body, @pin_bindings_fields, PinBinding)
|
||||
def to_asset(body, Point), do: resource_decode(body, @points_fields, Point)
|
||||
def to_asset(body, Regimen), do: resource_decode(body, @regimens_fields, Regimen)
|
||||
def to_asset(body, Sensor), do: resource_decode(body, @sensors_fields, Sensor)
|
||||
def to_asset(body, Sequence), do: resource_decode(body, @sequences_fields, Sequence)
|
||||
def to_asset(body, Tool), do: resource_decode(body, @tools_fields, Tool)
|
||||
|
||||
defp resource_decode(data, fields, kind) when is_list(data),
|
||||
do: Enum.map(data, &resource_decode(&1, fields, kind))
|
||||
|
||||
defp resource_decode(data, fields, kind) do
|
||||
data
|
||||
|> Map.take(fields)
|
||||
|> Enum.map(&string_to_atom/1)
|
||||
|> into_struct(kind)
|
||||
end
|
||||
|
||||
defp string_to_atom({k, v}), do: {String.to_atom(k), v}
|
||||
defp into_struct(data, kind), do: struct(kind, data)
|
||||
end
|
|
@ -16,10 +16,10 @@ require Logger
|
|||
end
|
||||
|
||||
def dispatch do
|
||||
old = %Snapshot{}
|
||||
new = Repo.snapshot()
|
||||
diff = Snapshot.diff(old, new)
|
||||
Farmbot.Asset.dispatch_sync(diff)
|
||||
# old = %Snapshot{}
|
||||
# new = Repo.snapshot()
|
||||
# diff = Snapshot.diff(old, new)
|
||||
# Farmbot.Asset.dispatch_sync(diff)
|
||||
:ignore
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,27 +9,31 @@ defmodule Farmbot.Asset.Repo do
|
|||
alias Farmbot.Asset.{
|
||||
Device,
|
||||
FarmEvent,
|
||||
FarmwareEnv,
|
||||
FarmwareInstallation,
|
||||
Peripheral,
|
||||
PinBinding,
|
||||
Point,
|
||||
Regimen,
|
||||
Sensor,
|
||||
Sequence,
|
||||
Tool
|
||||
Tool,
|
||||
}
|
||||
|
||||
def snapshot do
|
||||
results = Farmbot.Asset.Repo.all(Device) ++
|
||||
Farmbot.Asset.Repo.all(FarmEvent) ++
|
||||
Farmbot.Asset.Repo.all(Peripheral) ++
|
||||
Farmbot.Asset.Repo.all(PinBinding) ++
|
||||
Farmbot.Asset.Repo.all(Point) ++
|
||||
Farmbot.Asset.Repo.all(Regimen) ++
|
||||
Farmbot.Asset.Repo.all(Sensor) ++
|
||||
Farmbot.Asset.Repo.all(Sequence) ++
|
||||
Farmbot.Asset.Repo.all(Tool)
|
||||
Farmbot.Asset.Repo.all(FarmEvent) ++
|
||||
Farmbot.Asset.Repo.all(FarmwareEnv) ++
|
||||
Farmbot.Asset.Repo.all(FarmwareInstallation) ++
|
||||
Farmbot.Asset.Repo.all(Peripheral) ++
|
||||
Farmbot.Asset.Repo.all(PinBinding) ++
|
||||
Farmbot.Asset.Repo.all(Point) ++
|
||||
Farmbot.Asset.Repo.all(Regimen) ++
|
||||
Farmbot.Asset.Repo.all(Sensor) ++
|
||||
Farmbot.Asset.Repo.all(Sequence) ++
|
||||
Farmbot.Asset.Repo.all(Tool)
|
||||
|
||||
struct(Snapshot, [data: results])
|
||||
%Snapshot{data: results}
|
||||
|> Snapshot.md5()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
defmodule Farmbot.Asset.Settings do
|
||||
@moduledoc """
|
||||
Responsible for turning FbosConfig and FirmwareConfig into
|
||||
local Farmbot.Config settings.
|
||||
"""
|
||||
alias Farmbot.Asset.Settings.{
|
||||
FbosConfig,
|
||||
FirmwareConfig
|
||||
}
|
||||
|
||||
import Farmbot.Config, only: [get_config_as_map: 0]
|
||||
|
||||
def download_firmware(%{} = remote_fw_config) do
|
||||
local_fw_config = get_config_as_map()["hardware_params"]
|
||||
:ok = FirmwareConfig.download(remote_fw_config, local_fw_config)
|
||||
end
|
||||
|
||||
def download_os(%{} = remote_os_config) do
|
||||
local_os_config = get_config_as_map()["settings"]
|
||||
:ok = FbosConfig.download(remote_os_config, local_os_config)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,49 @@
|
|||
defmodule Farmbot.Asset.Settings.FbosConfig do
|
||||
@moduledoc false
|
||||
import Farmbot.Asset.Settings.Helpers
|
||||
require Farmbot.Logger
|
||||
@keys ~W(arduino_debug_messages
|
||||
auto_sync
|
||||
beta_opt_in
|
||||
disable_factory_reset
|
||||
firmware_hardware
|
||||
firmware_input_log
|
||||
firmware_output_log
|
||||
network_not_found_timer
|
||||
os_auto_update
|
||||
sequence_body_log
|
||||
sequence_complete_log
|
||||
sequence_init_log)
|
||||
|
||||
def download(new, old) do
|
||||
new = Map.take(new, @keys)
|
||||
for k <- @keys do
|
||||
if old[k] != new[k] do
|
||||
try do
|
||||
apply_kv(k, new[k], old[k])
|
||||
rescue
|
||||
_ -> Farmbot.Logger.error 1, "Failed to apply Fbos Config: #{k}"
|
||||
end
|
||||
end
|
||||
end
|
||||
:ok
|
||||
end
|
||||
|
||||
def log(key, new, old) do
|
||||
Farmbot.Logger.info 3, "Fbos Config #{key} updated: #{new || "NULL"} => #{old || "NULL"}"
|
||||
end
|
||||
|
||||
bool("arduino_debug_messages")
|
||||
bool("auto_sync")
|
||||
bool("beta_opt_in")
|
||||
bool("disable_factory_reset")
|
||||
bool("firmware_input_log")
|
||||
bool("firmware_output_log")
|
||||
bool("os_auto_update")
|
||||
bool("sequence_body_log")
|
||||
bool("sequence_complete_log")
|
||||
bool("sequence_init_log")
|
||||
|
||||
string("firmware_hardware")
|
||||
float("network_not_found_timer")
|
||||
end
|
|
@ -0,0 +1,115 @@
|
|||
defmodule Farmbot.Asset.Settings.FirmwareConfig do
|
||||
@moduledoc false
|
||||
import Farmbot.Asset.Settings.Helpers
|
||||
require Farmbot.Logger
|
||||
@keys ~W(pin_guard_4_time_out
|
||||
pin_guard_1_active_state
|
||||
encoder_scaling_y
|
||||
movement_invert_2_endpoints_x
|
||||
movement_min_spd_y
|
||||
pin_guard_2_time_out
|
||||
movement_timeout_y
|
||||
movement_home_at_boot_y
|
||||
movement_home_spd_z
|
||||
movement_invert_endpoints_z
|
||||
pin_guard_1_pin_nr
|
||||
movement_invert_endpoints_y
|
||||
movement_max_spd_y
|
||||
movement_home_up_y
|
||||
encoder_missed_steps_decay_z
|
||||
movement_home_spd_y
|
||||
encoder_use_for_pos_x
|
||||
movement_step_per_mm_x
|
||||
movement_home_at_boot_z
|
||||
movement_steps_acc_dec_z
|
||||
pin_guard_5_pin_nr
|
||||
movement_invert_motor_z
|
||||
movement_max_spd_x
|
||||
movement_enable_endpoints_y
|
||||
movement_enable_endpoints_z
|
||||
movement_stop_at_home_x
|
||||
movement_axis_nr_steps_y
|
||||
pin_guard_1_time_out
|
||||
movement_home_at_boot_x
|
||||
pin_guard_2_pin_nr
|
||||
encoder_scaling_z
|
||||
param_e_stop_on_mov_err
|
||||
encoder_enabled_x
|
||||
pin_guard_2_active_state
|
||||
encoder_missed_steps_decay_y
|
||||
movement_home_up_z
|
||||
movement_enable_endpoints_x
|
||||
movement_step_per_mm_y
|
||||
pin_guard_3_pin_nr
|
||||
param_mov_nr_retry
|
||||
movement_stop_at_home_z
|
||||
pin_guard_4_active_state
|
||||
movement_steps_acc_dec_y
|
||||
movement_home_spd_x
|
||||
movement_keep_active_x
|
||||
pin_guard_3_time_out
|
||||
movement_keep_active_y
|
||||
encoder_scaling_x
|
||||
movement_invert_2_endpoints_z
|
||||
encoder_missed_steps_decay_x
|
||||
movement_timeout_z
|
||||
encoder_missed_steps_max_z
|
||||
movement_min_spd_z
|
||||
encoder_enabled_y
|
||||
encoder_type_y
|
||||
movement_home_up_x
|
||||
pin_guard_3_active_state
|
||||
movement_invert_motor_x
|
||||
movement_keep_active_z
|
||||
movement_max_spd_z
|
||||
movement_secondary_motor_invert_x
|
||||
movement_stop_at_max_x
|
||||
movement_steps_acc_dec_x
|
||||
pin_guard_4_pin_nr
|
||||
encoder_type_x
|
||||
movement_invert_2_endpoints_y
|
||||
encoder_invert_y
|
||||
movement_axis_nr_steps_x
|
||||
movement_stop_at_max_z
|
||||
movement_invert_endpoints_x
|
||||
encoder_invert_z
|
||||
encoder_use_for_pos_z
|
||||
pin_guard_5_active_state
|
||||
movement_step_per_mm_z
|
||||
encoder_enabled_z
|
||||
movement_secondary_motor_x
|
||||
pin_guard_5_time_out
|
||||
movement_min_spd_x
|
||||
encoder_type_z
|
||||
movement_stop_at_max_y
|
||||
encoder_use_for_pos_y
|
||||
encoder_missed_steps_max_y
|
||||
movement_timeout_x
|
||||
movement_stop_at_home_y
|
||||
movement_axis_nr_steps_z
|
||||
encoder_invert_x
|
||||
encoder_missed_steps_max_x
|
||||
movement_invert_motor_y)
|
||||
|
||||
def download(new, old) do
|
||||
new = Map.take(new, @keys)
|
||||
for k <- @keys do
|
||||
if old[k] != new[k] do
|
||||
try do
|
||||
apply_kv(k, new[k], old[k])
|
||||
rescue
|
||||
_ -> Farmbot.Logger.error 1, "Failed to apply Firmware Config: #{k}"
|
||||
end
|
||||
end
|
||||
end
|
||||
:ok
|
||||
end
|
||||
|
||||
def log(key, new, old) do
|
||||
Farmbot.Logger.info 3, "Firmware Config #{key} updated: #{new || "NULL"} => #{old || "NULL"}"
|
||||
end
|
||||
|
||||
for key <- @keys do
|
||||
fw_float(unquote(key))
|
||||
end
|
||||
end
|
|
@ -0,0 +1,58 @@
|
|||
defmodule Farmbot.Asset.Settings.Helpers do
|
||||
import Farmbot.Config, only: [update_config_value: 4]
|
||||
defmacro bool(kind) do
|
||||
quote do
|
||||
def apply_kv(unquote(kind), nil = new, old) do
|
||||
log(unquote(kind), old, new)
|
||||
update_config_value(:bool, "settings", unquote(kind), nil)
|
||||
end
|
||||
|
||||
def apply_kv(unquote(kind), new, old) when is_boolean(new) do
|
||||
log(unquote(kind), old, new)
|
||||
update_config_value(:bool, "settings", unquote(kind), new)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmacro string(kind) do
|
||||
quote do
|
||||
def apply_kv(unquote(kind), nil = new, old) do
|
||||
log(unquote(kind), old, new)
|
||||
update_config_value(:string, "settings", unquote(kind), nil)
|
||||
end
|
||||
|
||||
def apply_kv(unquote(kind), new, old) when is_binary(new) do
|
||||
log(unquote(kind), old, new)
|
||||
update_config_value(:string, "settings", unquote(kind), new)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmacro float(kind) do
|
||||
quote do
|
||||
def apply_kv(unquote(kind), nil = new, old) do
|
||||
log(unquote(kind), old, new)
|
||||
update_config_value(:float, "settings", unquote(kind), nil)
|
||||
end
|
||||
|
||||
def apply_kv(unquote(kind), new, old) when is_number(new) do
|
||||
log(unquote(kind), old, new)
|
||||
update_config_value(:float, "settings", unquote(kind), new / 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmacro fw_float(kind) do
|
||||
quote do
|
||||
def apply_kv(unquote(kind), nil = new, old) do
|
||||
log(unquote(kind), old, new)
|
||||
update_config_value(:float, "hardware_params", unquote(kind), nil)
|
||||
end
|
||||
|
||||
def apply_kv(unquote(kind), new, old) when is_number(new) do
|
||||
log(unquote(kind), old, new)
|
||||
update_config_value(:float, "hardware_params", unquote(kind), new / 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,121 @@
|
|||
defmodule Farmbot.Asset.Sync do
|
||||
alias Farmbot.Asset.{
|
||||
Repo,
|
||||
SyncCmd
|
||||
}
|
||||
require Farmbot.Logger
|
||||
require Logger
|
||||
import Ecto.Query, warn: false
|
||||
|
||||
def sync(verbosity \\ 1) do
|
||||
Farmbot.Logger.info(verbosity, "Syncing")
|
||||
before_sync = Repo.snapshot()
|
||||
|
||||
after_sync = Repo.snapshot()
|
||||
diff = Repo.Snapshot.diff(before_sync, after_sync)
|
||||
dispatch_sync(diff)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Register a sync message from an external source.
|
||||
This is like a snippit of the changes that have happened.
|
||||
`sync_cmd`s should only be applied on `sync`ing.
|
||||
`sync_cmd`s are _not_ a source of truth for transactions that have been applied.
|
||||
Use the `Farmbot.Asset.Registry` for these types of events.
|
||||
"""
|
||||
def register_sync_cmd(remote_id, kind, body) when is_binary(kind) do
|
||||
# Make sure to raise if this isn't valid.
|
||||
_ = kind_to_module(kind)
|
||||
new_sync_cmd(remote_id, kind, body)
|
||||
|> SyncCmd.changeset()
|
||||
|> Repo.insert!()
|
||||
end
|
||||
|
||||
@doc "Destroy all sync cmds locally."
|
||||
def destroy_all_sync_cmds do
|
||||
Repo.delete_all(SyncCmd)
|
||||
end
|
||||
|
||||
@doc "Returns all sync cmds stored locally."
|
||||
def all_sync_cmds, do: Repo.all(SyncCmd)
|
||||
|
||||
@doc "Delete a single cmd."
|
||||
def destroy_sync_cmd(%SyncCmd{id: nil} = cmd), do: {:ok, cmd}
|
||||
def destroy_sync_cmd(%SyncCmd{} = cmd), do: Repo.delete(cmd)
|
||||
|
||||
defp apply_sync_cmd(%SyncCmd{} = cmd) do
|
||||
mod = kind_to_module(kind)
|
||||
Farmbot.Logger.debug(3, "Syncing #{cmd.kind}")
|
||||
try do
|
||||
do_apply_sync_cmd(cmd)
|
||||
rescue
|
||||
e ->
|
||||
Farmbot.Logger.error(1, "Error syncing: #{mod}: #{Exception.message(e)}")
|
||||
end
|
||||
destroy_sync_cmd(cmd)
|
||||
end
|
||||
|
||||
# When `body` is nil, it means an object was deleted.
|
||||
defp do_apply_sync_cmd(%{body: nil, remote_id: id, kind: kind}) do
|
||||
mod = kind_to_module(kind)
|
||||
case Repo.one(from m in mod, where: m.id == ^id) do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
existing ->
|
||||
Repo.delete!(existing)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp do_apply_sync_cmd(%{body: obj, remote_id: id, kind: kind}) do
|
||||
not_struct = strip_struct(obj)
|
||||
mod = kind_to_module(kind)
|
||||
# We need to check if this object exists in the database.
|
||||
case Repo.one(from m in mod, where: m.id == ^id) do
|
||||
# If it does not, just return the newly created object.
|
||||
nil ->
|
||||
change = mod.changeset(struct(mod, not_struct), not_struct)
|
||||
Repo.insert!(change)
|
||||
:ok
|
||||
# if there is an existing record, copy the ecto meta from the old
|
||||
# record. This allows `insert_or_update` to work properly.
|
||||
existing ->
|
||||
existing
|
||||
|> Ecto.Changeset.change(not_struct)
|
||||
|> Repo.update!()
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp strip_struct(%{__struct__: _, __meta__: _} = struct),
|
||||
do: Map.from_struct(struct) |> Map.drop([:__struct__, :__meta__])
|
||||
defp strip_struct(%{} = already_map), do: already_map
|
||||
|
||||
defp new_sync_cmd(remote_id, kind, body)
|
||||
when is_integer(remote_id) when is_binary(kind)
|
||||
do
|
||||
_mod = Module.concat(["Farmbot", "Asset", kind])
|
||||
struct(SyncCmd, %{remote_id: remote_id, kind: kind, body: body})
|
||||
end
|
||||
|
||||
defp kind_to_module(kind) do
|
||||
mod = Module.concat(["Farmbot", "Asset", kind])
|
||||
if !Code.ensure_loaded?(mod), do: raise("Unknown kind: #{kind}")
|
||||
mod
|
||||
end
|
||||
|
||||
defp dispatch_sync(diff) do
|
||||
for deletion <- diff.deletions do
|
||||
Farmbot.Registry.dispatch(__MODULE__, {:deletion, deletion})
|
||||
end
|
||||
|
||||
for update <- diff.updates do
|
||||
Farmbot.Registry.dispatch(__MODULE__, {:update, update})
|
||||
end
|
||||
|
||||
for addition <- diff.additions do
|
||||
Farmbot.Registry.dispatch(__MODULE__, {:addition, addition})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,11 @@
|
|||
defmodule Farmbot.Asset.SyncCmd do
|
||||
@moduledoc "describes an update to a API resource."
|
||||
@moduledoc """
|
||||
Describes an update to an API resource.
|
||||
|
||||
* `remote_id` - ID of remote object change.
|
||||
* `kind` - String camel case representation of the asset kind.
|
||||
* `body` - Data for the change.
|
||||
"""
|
||||
|
||||
alias Farmbot.Asset.SyncCmd
|
||||
use Ecto.Schema
|
||||
|
|
|
@ -57,18 +57,12 @@ defmodule FarmbotCore.MixProject do
|
|||
defp deps do
|
||||
[
|
||||
{:farmbot_celery_script, path: "../farmbot_celery_script", env: Mix.env()},
|
||||
# Arduino Firmware stuff.
|
||||
{:elixir_make, "~> 0.4", runtime: false},
|
||||
{:nerves_uart, "~> 1.2"},
|
||||
|
||||
{:gen_stage, "~> 0.14"},
|
||||
|
||||
# Storage of data.
|
||||
{:ecto, "~> 2.2"},
|
||||
{:sqlite_ecto2, "~> 2.2"},
|
||||
|
||||
{:timex, "~> 3.4"},
|
||||
|
||||
{:dialyxir, "~> 1.0.0-rc.3", only: [:dev], runtime: false},
|
||||
{:ex_doc, "~> 0.19", only: :dev, runtime: false},
|
||||
{:excoveralls, "~> 0.10", only: :test}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"decimal": {:hex, :decimal, "1.5.0", "b0433a36d0e2430e3d50291b1c65f53c37d56f83665b43d79963684865beab68", [:mix], [], "hexpm"},
|
||||
"dialyxir": {:hex, :dialyxir, "1.0.0-rc.3", "774306f84973fc3f1e2e8743eeaa5f5d29b117f3916e5de74c075c02f1b8ef55", [:mix], [], "hexpm"},
|
||||
"earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "2.2.11", "4bb8f11718b72ba97a2696f65d247a379e739a0ecabf6a13ad1face79844791c", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "2.2.9", "031d55df9bb430cb118e6f3026a87408d9ce9638737bda3871e5d727a3594aae", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.4.2", "332c649d08c18bc1ecc73b1befc68c647136de4f340b548844efc796405743bf", [:mix], [], "hexpm"},
|
||||
"esqlite": {:hex, :esqlite, "0.2.4", "3a8a352c190afe2d6b828b252a6fbff65b5cc1124647f38b15bdab3bf6fd4b3e", [:rebar3], [], "hexpm"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
|
@ -27,8 +27,8 @@
|
|||
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], [], "hexpm"},
|
||||
"ring_logger": {:hex, :ring_logger, "0.4.1", "db972365bfda705288d7629e80af5704a1aafdbe9da842712c3cdd587639c72e", [:mix], [], "hexpm"},
|
||||
"sbroker": {:hex, :sbroker, "1.0.0", "28ff1b5e58887c5098539f236307b36fe1d3edaa2acff9d6a3d17c2dcafebbd0", [:rebar3], [], "hexpm"},
|
||||
"sqlite_ecto2": {:hex, :sqlite_ecto2, "2.2.4", "e192c243750511efc1369d74c15d65a014e176e4aba9f67ca7852e9826b2cba4", [:mix], [{:connection, "~> 1.0.3", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 2.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: false]}, {:sqlitex, "~> 1.3.2 or ~> 1.4", [hex: :sqlitex, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"sqlitex": {:hex, :sqlitex, "1.4.2", "b18f2b53cefbc9cca0bd17d51386f9caa7cf341144cb314e5cd9fd2a1f9b0845", [:mix], [{:decimal, "~> 1.1", [hex: :decimal, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.2.4", [hex: :esqlite, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"sqlite_ecto2": {:hex, :sqlite_ecto2, "2.2.5", "f111a48188b0640effb7f2952071c4cf285501d3ce090820a7c2fc20af3867e9", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "2.2.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: false]}, {:sqlitex, "~> 1.4", [hex: :sqlitex, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"sqlitex": {:hex, :sqlitex, "1.4.3", "a50f12d6aeb25f4ebb128453386c09bbba8f5abd3c7713dc5eaa92f359926ac5", [:mix], [{:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.2.4", [hex: :esqlite, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
|
||||
"timex": {:hex, :timex, "3.4.1", "e63fc1a37453035e534c3febfe9b6b9e18583ec7b37fd9c390efdef97397d70b", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"tzdata": {:hex, :tzdata, "0.5.19", "7962a3997bf06303b7d1772988ede22260f3dae1bf897408ebdac2b4435f4e6a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
|
|
|
@ -2,6 +2,7 @@ defmodule Farmbot.AMQP.AutoSyncTransport do
|
|||
use GenServer
|
||||
use AMQP
|
||||
require Farmbot.Logger
|
||||
require Logger
|
||||
import Farmbot.Config, only: [get_config_value: 3, update_config_value: 4]
|
||||
|
||||
@exchange "amq.topic"
|
||||
|
@ -21,7 +22,6 @@ defmodule Farmbot.AMQP.AutoSyncTransport do
|
|||
{:ok, _} = AMQP.Queue.declare(chan, jwt.bot <> "_auto_sync", [auto_delete: false])
|
||||
:ok = AMQP.Queue.bind(chan, jwt.bot <> "_auto_sync", @exchange, [routing_key: "bot.#{jwt.bot}.sync.#"])
|
||||
{:ok, _tag} = Basic.consume(chan, jwt.bot <> "_auto_sync", self(), [no_ack: true])
|
||||
Farmbot.Registry.subscribe()
|
||||
{:ok, struct(State, [conn: conn, chan: chan, bot: jwt.bot])}
|
||||
end
|
||||
|
||||
|
@ -52,24 +52,21 @@ defmodule Farmbot.AMQP.AutoSyncTransport do
|
|||
body = data["body"]
|
||||
case asset_kind do
|
||||
"FbosConfig" when is_nil(body) ->
|
||||
pl = %{"api_migrated" => true} |> Farmbot.JSON.encode!()
|
||||
Farmbot.HTTP.put!("/api/fbos_config", pl)
|
||||
Farmbot.SettingsSync.run()
|
||||
Farmbot.Logger.error 1, "FbosConfig deleted via API?"
|
||||
"FbosConfig" ->
|
||||
Farmbot.SettingsSync.apply_fbos_map(Farmbot.Config.get_config_as_map()["settings"], body)
|
||||
"FirmwareConfig" when is_nil(body) -> :ok
|
||||
Farmbot.HTTP.SettingsWorker.download_os(data)
|
||||
"FirmwareConfig" when is_nil(body) ->
|
||||
Farmbot.Logger.error 1, "FirmwareConfig deleted via API?"
|
||||
"FirmwareConfig" ->
|
||||
Farmbot.SettingsSync.apply_fw_map(Farmbot.Config.get_config_as_map()["hardware_params"], body)
|
||||
Farmbot.HTTP.SettingsWorker.download_firmware(data)
|
||||
_ ->
|
||||
if !get_config_value(:bool, "settings", "needs_http_sync") do
|
||||
id = String.to_integer(id_str)
|
||||
body = if body, do: Farmbot.Asset.to_asset(body, asset_kind), else: nil
|
||||
id = data["id"] || String.to_integer(id_str)
|
||||
# Body might be nil if a resource was deleted.
|
||||
body = if body, do: Farmbot.Asset.to_asset(body, asset_kind)
|
||||
_cmd = Farmbot.Asset.register_sync_cmd(id, asset_kind, body)
|
||||
if get_config_value(:bool, "settings", "auto_sync") do
|
||||
Farmbot.Asset.fragment_sync()
|
||||
end
|
||||
else
|
||||
IO.puts "not accepting sync_cmd from amqp because bot needs http sync first."
|
||||
Logger.warn "not accepting sync_cmd from amqp because bot needs http sync first."
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -77,11 +74,4 @@ defmodule Farmbot.AMQP.AutoSyncTransport do
|
|||
:ok = AMQP.Basic.publish state.chan, @exchange, "bot.#{device}.from_device", json
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info({Farmbot.Registry, {Farmbot.Config, {"settings", "auto_sync", true}}}, state) do
|
||||
Farmbot.AutoSyncTask.maybe_auto_sync()
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info({Farmbot.Registry, _}, state), do: {:noreply, state}
|
||||
end
|
||||
|
|
|
@ -104,7 +104,6 @@ defmodule Farmbot.Bootstrap.Supervisor do
|
|||
|
||||
children = [
|
||||
{Farmbot.HTTP.Supervisor, []},
|
||||
{Farmbot.SettingsSync, []},
|
||||
{Farmbot.AMQP.Supervisor , []},
|
||||
{Farmbot.Bootstrap.AuthTask, []},
|
||||
{Farmbot.AutoSyncTask, []},
|
||||
|
|
|
@ -38,6 +38,7 @@ defmodule Farmbot.HTTP do
|
|||
def firmware_config do
|
||||
client()
|
||||
|> get!("/api/firmware_config")
|
||||
|> Map.fetch!(:body)
|
||||
end
|
||||
|
||||
def update_firmware_config(%{} = data) do
|
||||
|
@ -51,6 +52,12 @@ defmodule Farmbot.HTTP do
|
|||
|> Map.fetch!(:body)
|
||||
end
|
||||
|
||||
def update_fbos_config(%{} = data) do
|
||||
client()
|
||||
|> patch!("/api/fbos_config", data)
|
||||
|> Map.fetch!(:body)
|
||||
end
|
||||
|
||||
@doc "Upload a file to Farmbot GCS."
|
||||
def upload_file(filename, meta) do
|
||||
client()
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
defmodule Farmbot.HTTP.SettingsWorker do
|
||||
@moduledoc """
|
||||
Watches the local database for changes to the following resources:
|
||||
* FbosConfig
|
||||
* FirmwareConfig
|
||||
* FarmwareEnv
|
||||
|
||||
When a change is detected, the asset is uploaded to the API.
|
||||
"""
|
||||
|
||||
alias Farmbot.Asset.Settings
|
||||
import Farmbot.Config, only: [get_config_as_map: 0]
|
||||
require Farmbot.Logger
|
||||
use GenServer
|
||||
|
||||
def download_all_settings do
|
||||
Farmbot.Logger.debug 3, "Syncing all settings."
|
||||
|
||||
remote_fw_config = HTTP.firmware_config()
|
||||
Settings.download_firmware(remote_fw_config)
|
||||
|
||||
# Make sure that the API has the correct firmware hardware.
|
||||
patch = %{"firmware_hardware" => get_config_as_map()["settings"]["firmware_hardware"]}
|
||||
remote_os_config = HTTP.update_fbos_config(patch)
|
||||
Settings.download_os(remote_os_config)
|
||||
|
||||
Farmbot.Logger.debug 3, "Done syncing all settings."
|
||||
:ok
|
||||
end
|
||||
|
||||
def start_link(args) do
|
||||
GenServer.start_link(__MODULE__, args, [name: __MODULE__])
|
||||
end
|
||||
|
||||
def init([]) do
|
||||
:ok = download_all_settings()
|
||||
{:ok, %{}}
|
||||
end
|
||||
end
|
|
@ -10,7 +10,8 @@ defmodule Farmbot.HTTP.Supervisor do
|
|||
|
||||
def init([]) do
|
||||
children = [
|
||||
{Farmbot.HTTP.ImageUploader, []}
|
||||
{Farmbot.HTTP.ImageUploader, []},
|
||||
{Farmbot.HTTP.SettingsWorker, []}
|
||||
]
|
||||
|
||||
opts = [strategy: :one_for_all]
|
||||
|
|
|
@ -27,7 +27,7 @@ defmodule Farmbot.Ext.MixProject do
|
|||
[
|
||||
{:farmbot_core, path: "../farmbot_core", env: Mix.env()},
|
||||
{:ranch_proxy_protocol, "~> 2.0", override: true},
|
||||
{:tesla, "~> 1.0.0"},
|
||||
{:tesla, "~> 1.1"},
|
||||
{:jason, "~> 1.1"},
|
||||
{:uuid, "~> 1.1"},
|
||||
{:amqp, "~> 1.0"},
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
"sqlite_ecto2": {:hex, :sqlite_ecto2, "2.2.4", "e192c243750511efc1369d74c15d65a014e176e4aba9f67ca7852e9826b2cba4", [:mix], [{:connection, "~> 1.0.3", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 2.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: false]}, {:sqlitex, "~> 1.3.2 or ~> 1.4", [hex: :sqlitex, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"sqlitex": {:hex, :sqlitex, "1.4.2", "b18f2b53cefbc9cca0bd17d51386f9caa7cf341144cb314e5cd9fd2a1f9b0845", [:mix], [{:decimal, "~> 1.1", [hex: :decimal, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.2.4", [hex: :esqlite, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
|
||||
"tesla": {:hex, :tesla, "1.0.0", "94a9059456d51266f3d40b75ff6be3d18496072ce5ffaabad03d6c00f065cfd1", [:mix], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"tesla": {:hex, :tesla, "1.2.1", "864783cc27f71dd8c8969163704752476cec0f3a51eb3b06393b3971dc9733ff", [:mix], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"timex": {:hex, :timex, "3.4.2", "d74649c93ad0e12ce5b17cf5e11fbd1fb1b24a3d114643e86dba194b64439547", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"tzdata": {:hex, :tzdata, "0.5.19", "7962a3997bf06303b7d1772988ede22260f3dae1bf897408ebdac2b4435f4e6a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
erlang 21.0.4
|
||||
elixir 1.6.6-otp-21
|
|
@ -0,0 +1 @@
|
|||
../.tool-versions
|
|
@ -1,32 +0,0 @@
|
|||
defmodule Test do
|
||||
import Ecto.Query
|
||||
alias Farmbot.CeleryScript
|
||||
alias CeleryScript.RunTime.FarmProc
|
||||
import CeleryScript.Utils
|
||||
|
||||
def test(amnt) do
|
||||
state = :sys.get_state(CeleryScript.RunTime)
|
||||
fun = state.process_io_layer
|
||||
seq = Farmbot.Asset.Repo.one(from s in Farmbot.Asset.Sequence, where: s.name == "new sequence 12")
|
||||
ast = CeleryScript.AST.decode(seq)
|
||||
heap = CeleryScript.AST.slice(ast)
|
||||
page = addr(seq.id)
|
||||
proc0 = FarmProc.new(fun, page, heap)
|
||||
reduce(proc0, amnt)
|
||||
end
|
||||
|
||||
def reduce(proc, count, acc \\ [])
|
||||
|
||||
def reduce(proc, 0, acc) do
|
||||
[proc | acc]
|
||||
end
|
||||
|
||||
def reduce(proc, count, acc) do
|
||||
next = FarmProc.step(proc)
|
||||
if next.status == :waiting do
|
||||
reduce(next, count, acc)
|
||||
else
|
||||
reduce(next, count - 1, [proc | acc])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -21,6 +21,7 @@ defmodule Farmbot.OS.MixProject do
|
|||
build_path: "_build/#{@target}",
|
||||
lockfile: "mix.lock.#{@target}",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
start_embedded: @target == "host",
|
||||
aliases: [loadconfig: [&bootstrap/1]],
|
||||
elixirc_paths: elixirc_paths(Mix.env(), @target),
|
||||
deps: deps()
|
||||
|
@ -46,20 +47,20 @@ defmodule Farmbot.OS.MixProject do
|
|||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# Nerves stuff.
|
||||
{:nerves, "~> 1.3", runtime: false},
|
||||
{:nerves_hub_cli, "~> 0.4", runtime: false},
|
||||
{:shoehorn, "~> 0.4"},
|
||||
|
||||
{:logger_backend_sqlite, "~> 2.2"},
|
||||
{:farmbot_core, path: "../farmbot_core", env: Mix.env()},
|
||||
{:farmbot_ext, path: "../farmbot_ext", env: Mix.env()},
|
||||
{:logger_backend_sqlite, "~> 2.1"},
|
||||
{:dialyxir, "~> 1.0.0-rc.3", runtime: false, override: true},
|
||||
{:nerves_hub_cli, "~> 0.5", runtime: false}
|
||||
] ++ deps(@target)
|
||||
end
|
||||
|
||||
# Specify target specific dependencies
|
||||
defp deps("host"), do: [
|
||||
{:excoveralls, "~> 0.9", only: [:test]},
|
||||
{:excoveralls, "~> 0.10", only: [:test]},
|
||||
{:dialyxir, "~> 1.0.0-rc.3", only: [:dev], runtime: false},
|
||||
{:ex_doc, "~> 0.19", only: [:dev], runtime: false},
|
||||
]
|
||||
|
||||
|
@ -79,6 +80,7 @@ defmodule Farmbot.OS.MixProject do
|
|||
|
||||
{:nerves_runtime, "~> 0.8"},
|
||||
{:nerves_network, "~> 0.3"},
|
||||
{:nerves_wpa_supplicant, "~> 0.3"},
|
||||
{:nerves_firmware, "~> 0.4"},
|
||||
{:nerves_time, "~> 0.2"},
|
||||
{:nerves_hub, "~> 0.2"},
|
||||
|
|
|
@ -28,10 +28,9 @@
|
|||
"jsx": {:hex, :jsx, "2.8.2", "7acc7d785b5abe8a6e9adbde926a24e481f29956dd8b4df49e3e4e7bcc92a018", [:mix, :rebar3], [], "hexpm"},
|
||||
"lager": {:hex, :lager, "3.6.3", "fe78951d174616273f87f0dbc3374d1430b1952e5efc4e1c995592d30a207294", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"logger_backend_ecto": {:hex, :logger_backend_ecto, "1.3.0", "6bb1a9d2b0ac1ee04049df94e49ea469f1f0db774d2fa05d673dc796a5ad9ed7", [:mix], [{:sqlite_ecto2, "~> 2.2", [hex: :sqlite_ecto2, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"logger_backend_sqlite": {:hex, :logger_backend_sqlite, "2.1.0", "c67dfe52e41d02c96cbeafcc0e0d613f7d6a2d029cd297ee004aa2f37a908d6e", [:mix], [{:esqlite, "~> 0.2.4", [hex: :esqlite, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"logger_backend_sqlite": {:hex, :logger_backend_sqlite, "2.2.0", "3d3529e7425aede462478896be303acc53c8b195eeef5383fd601b42b809a73e", [:mix], [{:esqlite, "~> 0.2.4", [hex: :esqlite, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"makeup": {:hex, :makeup, "0.5.5", "9e08dfc45280c5684d771ad58159f718a7b5788596099bdfb0284597d368a882", [:mix], [{:nimble_parsec, "~> 0.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"meck": {:hex, :meck, "0.8.12", "1f7b1a9f5d12c511848fec26bbefd09a21e1432eadb8982d9a8aceb9891a3cf2", [:rebar3], [], "hexpm"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
|
||||
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
|
||||
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"},
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
"lager": {:hex, :lager, "3.6.3", "fe78951d174616273f87f0dbc3374d1430b1952e5efc4e1c995592d30a207294", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"lager_logger": {:hex, :lager_logger, "1.0.5", "2b58be52fe1e0fb82656180fc54e45618aa2dc619090b00e6d3fb4707c6a1fe5", [:mix], [{:lager, ">= 2.1.0", [hex: :lager, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"logger_backend_ecto": {:hex, :logger_backend_ecto, "1.3.0", "6bb1a9d2b0ac1ee04049df94e49ea469f1f0db774d2fa05d673dc796a5ad9ed7", [:mix], [{:sqlite_ecto2, "~> 2.2", [hex: :sqlite_ecto2, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"logger_backend_sqlite": {:hex, :logger_backend_sqlite, "2.1.0", "c67dfe52e41d02c96cbeafcc0e0d613f7d6a2d029cd297ee004aa2f37a908d6e", [:mix], [{:esqlite, "~> 0.2.4", [hex: :esqlite, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"logger_backend_sqlite": {:hex, :logger_backend_sqlite, "2.2.0", "3d3529e7425aede462478896be303acc53c8b195eeef5383fd601b42b809a73e", [:mix], [{:esqlite, "~> 0.2.4", [hex: :esqlite, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"makeup": {:hex, :makeup, "0.5.5", "9e08dfc45280c5684d771ad58159f718a7b5788596099bdfb0284597d368a882", [:mix], [{:nimble_parsec, "~> 0.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"mdns": {:hex, :mdns, "1.0.2", "c8228dd44d3fdd55e9842cb7111c9145f2eeaa8b7adac75012ee0e250962215e", [:mix], [{:dns, "~> 2.0", [hex: :dns, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
|
|
Loading…
Reference in New Issue