Refactor farmbot application into separate folders/apps.

pull/974/head
connor rigby 2018-12-18 07:45:30 -08:00 committed by Connor Rigby
parent 0d569301f1
commit 8c34f2eacd
No known key found for this signature in database
GPG Key ID: 29A88B24B70456E0
26 changed files with 571 additions and 373 deletions

View File

@ -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

View File

@ -1,2 +0,0 @@
erlang 21.0.4
elixir 1.6.6-otp-21

View File

@ -0,0 +1 @@
../.tool-versions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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}

View File

@ -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"},

View File

@ -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

View File

@ -104,7 +104,6 @@ defmodule Farmbot.Bootstrap.Supervisor do
children = [
{Farmbot.HTTP.Supervisor, []},
{Farmbot.SettingsSync, []},
{Farmbot.AMQP.Supervisor , []},
{Farmbot.Bootstrap.AuthTask, []},
{Farmbot.AutoSyncTask, []},

View File

@ -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()

View File

@ -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

View File

@ -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]

View File

@ -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"},

View File

@ -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"},

View File

@ -1,2 +0,0 @@
erlang 21.0.4
elixir 1.6.6-otp-21

View File

@ -0,0 +1 @@
../.tool-versions

View File

@ -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

View File

@ -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"},

View File

@ -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"},

View File

@ -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"},