begin migration to sqlite3

pull/363/head
Connor Rigby 2017-09-18 17:23:10 -07:00
parent 94e3a21970
commit 223af26580
32 changed files with 174 additions and 882 deletions

3
.gitignore vendored
View File

@ -49,3 +49,6 @@ auth_secret_test.exs
flash_fw.sh
tmp
*.sqlite3
*.img
run-qemu.sh

View File

@ -33,13 +33,18 @@ config :farmbot, :init, []
# See Farmbot.BotState.Transport for details.
config :farmbot, :transport, []
# Configure Farmbot Behaviours.
config :farmbot, :behaviour, [
authorization: Farmbot.Bootstrap.Authorization,
firmware_handler: Farmbot.Firmware.StubHandler,
]
config :farmbot, Farmbot.Repo,
adapter: Sqlite.Ecto2,
database: "#{env}.sqlite3"
config :farmbot, ecto_repos: [Farmbot.Repo]
case target do
"host" -> import_config("host/#{env}.exs")
_ ->

View File

@ -1,16 +1,23 @@
use Mix.Config
config :farmbot, data_path: "/state"
config :farmbot, Farmbot.Repo,
adapter: Sqlite.Ecto2,
database: "/root/#{Mix.env()}.sqlite3"
config :farmbot, data_path: "/root"
# Configure your our system.
# Default implementation needs no special stuff.
config :farmbot, :init, [
# Run migrations and whatnot.
Farmbot.Target.Ecto,
# initialize the configuration.
# This bring up a captive portal if needed.
Farmbot.Bootstrap.Configurator,
# Start up Network
Farmbot.System.Network
Farmbot.Target.Network
]
# Transports.

View File

@ -50,6 +50,7 @@ defmodule Farmbot do
def init(args) do
children = [
supervisor(Farmbot.Repo, []),
supervisor(Farmbot.System.Supervisor, [args, [name: Farmbot.System.Supervisor ]]),
supervisor(Farmbot.Bootstrap.Supervisor, [args, [name: Farmbot.Bootstrap.Supervisor ]]),
# supervisor(Farmbot.FarmEvent.Supervisor, [args, [name: Farmbot.FarmEvent.Supervisor ]]),

View File

@ -21,7 +21,7 @@ defmodule Farmbot.Bootstrap.Configurator do
"""
def start_link(_, opts) do
Logger.info "Configuring Farmbot."
sup = Supervisor.start_link(__MODULE__, [self()], opts)
supervisor = Supervisor.start_link(__MODULE__, [self()], opts)
# case supervisor do
# {:ok, pid} ->
# receive do
@ -51,7 +51,7 @@ defmodule Farmbot.Bootstrap.Configurator do
end
end
defp stop(supervisor, status) do
def stop(supervisor, status) do
Supervisor.stop(supervisor, :normal)
status
end

View File

@ -1,226 +0,0 @@
defmodule Farmbot.Database.RecordStorage do
@moduledoc """
This module is only responsible for storage of information.
"""
alias Farmbot.Database.Syncable
use Farmbot.DebugLog, color: :LIGHT_PURPLE
require Logger
use GenServer
@typedoc """
The module name of the object you want to access.
"""
@type syncable :: atom
@typedoc """
The incremental id givin to resources.
"""
@type local_id :: integer
@typedoc """
The (API) Database id given to resources.
"""
@type db_id :: integer
@typedoc """
Identifies a resource by its `syncable` (kind), `local_id`, and `db_id`
"""
@type ref_id :: {syncable, local_id, db_id}
@typedoc """
Held in `refs`.
"""
@type resource_map :: Syncable.t
@typedoc false
@type record_storage :: GenServer.server
@typedoc false
@type syncable_object :: map
@doc """
Commits a list of records to the db.
"""
@spec commit_records([map] | map, record_storage) :: :ok | {:error, term}
def commit_records(list_or_single_record, record_storage)
def commit_records([record | rest], record_storage) do
:ok = commit_records(record, record_storage)
commit_records(rest, record_storage)
end
def commit_records([], _record_storage) do
:ok
end
def commit_records(record, record_storage) when is_map(record) do
:ok = GenServer.call(record_storage, {:update_or_create, record})
end
@doc "Clear all records."
def flush(record_storage), do: GenServer.call(record_storage, :flush)
@doc "Flush a syncable."
def flush(record_storage, syncable), do: GenServer.call(record_storage, {:flush, syncable})
@doc """
Get a resource by its kind and id.
"""
@spec get_by_id(record_storage, syncable, db_id) :: resource_map | nil
def get_by_id(record_storage, kind, id),
do: GenServer.call(record_storage, {:get_by, kind, id})
@doc """
Get all resources of this kind.
"""
@spec get_all(record_storage, syncable) :: [resource_map]
def get_all(record_storage, kind),
do: GenServer.call(record_storage, {:get_all, kind})
## GenServer
defmodule State do
@moduledoc false
defimpl Inspect, for: __MODULE__ do
def inspect(thing, _) do
"#DatabaseState<#{inspect thing.all}>"
end
end
defstruct [
:by_kind_and_id,
:awaiting,
:by_kind,
:refs,
:all
]
end
@typep state :: State.t
@doc """
Start the Database
"""
def start_link(opts),
do: GenServer.start_link(__MODULE__, [], opts)
def init([]) do
initial_by_kind_and_id = %{}
initial_by_kind = %{}
initial_refs = %{}
initial_all = []
state = %State{
by_kind_and_id: initial_by_kind_and_id,
by_kind: initial_by_kind,
refs: initial_refs,
all: initial_all,
}
{:ok, state}
end
def handle_call(:flush, _, _old_state) do
{:ok, new_state} = init([])
{:reply, :ok, new_state}
end
def handle_call({:flush, syncable}, _, old_state) do
{:reply, :ok, remove_all_syncable(old_state, syncable)}
end
def handle_call({:get_by, kind, id}, _, state) do
r = get_by_kind_and_id(state, kind, id)
{:reply, r, state}
end
def handle_call({:get_all, syncable}, _, state) do
{:reply, get_all_by_kind(state, syncable), state}
end
def handle_call({:update_or_create, record}, _, state) do
{:reply, :ok, reindex(state, record)}
end
@spec remove_all_syncable(state, syncable) :: state
defp remove_all_syncable(state, syncable) do
new_all = Enum.reject(state.all, fn({s, _, _}) -> s == syncable end)
new_by_kind_and_id = state.by_kind_and_id
|> Enum.reject(fn({{s, _}, _}) -> s == syncable end)
|> Map.new
new_refs = state.refs
|> Enum.reject(fn({{s, _, _}, _}) -> s == syncable end)
|> Map.new()
%{
state |
by_kind_and_id: new_by_kind_and_id,
by_kind: Map.put(state.by_kind, syncable, []),
refs: new_refs,
all: new_all
}
end
# returns all the references of syncable
@spec get_all_by_kind(state, syncable) :: [resource_map]
defp get_all_by_kind(state, syncable) do
all = state.by_kind[syncable] || []
Enum.map(all, fn(ref) -> state.refs[ref] end)
end
@spec get_by_kind_and_id(state, syncable, integer) :: resource_map | nil
defp get_by_kind_and_id(state, kind, id) do
case state.by_kind_and_id[{kind, id}] do
{_kind, _local_id, db_id} = ref when id == db_id ->
state.refs[ref]
_ -> nil
end
end
@spec new_ref_id(map) :: ref_id
defp new_ref_id(%{__struct__: syncable, id: id}) do
# TODO(Connor) One day, we will need a local id.
{syncable, -1, id}
end
defp reindex(state, record) do
# get some info
kind = Map.fetch!(record, :__struct__)
id = Map.fetch!(record, :id) || raise "No id for record: #{inspect record}"
# Do we have it already?
maybe_old = get_by_kind_and_id(state, kind, id)
if maybe_old do
debug_log("updating old record")
already_exists = maybe_old
# if it existed, update it.
# set the ref from the old one.
new = %{already_exists | body: record}
new_refs = %{state.refs | new.ref_id => new}
%{state | refs: new_refs}
else
debug_log("inputting new record")
# if not, just add it.
rid = new_ref_id(record)
new_syncable = %Syncable{
body: record,
ref_id: rid
}
all = [rid | state.all]
by_kind = Map.put(state.by_kind, kind, [rid | state.by_kind[kind] || []])
new_refs = Map.put(state.refs, rid, new_syncable)
by_kind_and_id = Map.put(state.by_kind_and_id, {kind, id}, rid)
%{ state |
refs: new_refs,
all: all,
by_kind: by_kind,
by_kind_and_id: by_kind_and_id }
end
end
end

View File

@ -1,60 +0,0 @@
defmodule Farmbot.Database.Selectors do
@moduledoc """
Instead of litering the codebase with map/reduce/filter functions,
consider putting database query functions into this module.
"""
alias Farmbot.{Database, DebugLog}
alias Database.RecordStorage
alias Database.Syncable
alias __MODULE__.Error, as: SelectorError
alias Syncable.{Point, Device}
use DebugLog
@doc """
Find a Point with a particular type.
* "Plant"
* "ToolSlot"
* "GenericPointer"
"""
@spec find_point(GenServer.server(), binary, integer) :: Syncable.t | no_return
def find_point(record_storage, "Plant" = pt, id),
do: do_find_point(record_storage, pt, id)
def find_point(record_storage, "ToolSlot" = pt, id),
do: do_find_point(record_storage, pt, id)
def find_point(record_storage, "GenericPointer" = pt, id),
do: do_find_point(record_storage, pt, id)
@spec do_find_point(GenServer.server(), binary, integer) :: Point.t
defp do_find_point(record_storage, point_t, point_id) do
result = RecordStorage.get_by_id(record_storage, Point, point_id) || raise SelectorError, [
syncable: Point, syncable_id: point_id, message: "does not exist."
]
case result.body.pointer_type do
type when type == point_t -> result
_ -> raise SelectorError, [
syncable: Point, syncable_id: point_id, message: "does not match type: #{point_t}"
]
end
end
@doc """
Get this device. Raises.
"""
@spec get_device(GenServer.server()) :: Syncable.body | no_return
def get_device(record_storage) do
case RecordStorage.get_all(record_storage, Device) do
[device] -> device.body
[_device | _] ->
raise SelectorError, [
syncable: Device, syncable_id: nil, message: "Too many devices."
]
[] ->
raise SelectorError, [
syncable: Device, syncable_id: nil, message: "No device."
]
end
end
end

View File

@ -1,8 +0,0 @@
defmodule Farmbot.Database.Selectors.Error do
@moduledoc "Error message for selectors."
defexception [
:syncable_id,
:syncable,
:message,
]
end

View File

@ -1,159 +0,0 @@
defmodule Farmbot.Database.Syncable do
@moduledoc """
Glue between HTTP and Database.
"""
@enforce_keys [:ref_id, :body]
defstruct [:ref_id, :body]
@typedoc """
Module structs.
"""
@type body :: map
@type ref_id :: Farmbot.Database.ref_id
@type t :: %__MODULE__{ref_id: ref_id, body: body}
import Farmbot.HTTP.Helpers
alias __MODULE__.Error
@doc """
Pipe a HTTP request thru this. Trust me :tm:
"""
def parse_resp({:error, message}, _module), do: {:error, message}
def parse_resp({:ok, %{status_code: code, body: resp_body}}, module)
when is_2xx(code) do
parsed = resp_body |> String.trim() |> Poison.decode |> handle_json_output(resp_body)
cond do
is_list(parsed) -> Enum.map(parsed, fn(i) -> module.to_struct(i) end)
is_map(parsed) -> module.to_struct(parsed)
true -> raise Error,
message: "Don't know how to handle: #{inspect parsed}"
end
end
def parse_resp({:ok, bad_response}, _module), do: {:error, bad_response}
defp handle_json_output(res, resp_body) do
case res do
{:ok, body} -> body
{:error, :invalid, _pos} ->
spawn(fn() ->
IO.puts "INVALID JSON \r\n\r\n #{inspect resp_body} \r\n\r\n"
end)
raise Error, message: "Bad json"
end
end
@doc ~s"""
Builds common functionality for all Syncable Resources. `args` takes two keywords.
* `model` - a definition of the struct.
* `endpoint` - a tuple shaped like: {"/single_url", "/plural_url"}
Heres what it _WILL_ provide:
* For HTTP access we have:
* `sindular_url/0` - The single url endpoint
* `plural_url/0` - The plural url endpoint
* `fetch/1` and `fetch/2`
* `fetch/1` - takes a callback of either an anon function, or a tuple
shaped: {module, function, args} where the first arg is the result
described below.
* `fetch/2` - takes an id and a callback described above.
* For Data manipulation
* `to_struct/1` - takes a stringed map and _safely_ turns it into a stuct.
Heres what it _WILL NOT_ provide:
* local database access
* extensibility
"""
defmacro __using__(args) do
model = Keyword.get(args, :model) || raise "You need a model!"
{singular, plural} = Keyword.get(args, :endpoint) || raise Error,
message: "Syncable requires a endpoint: {singular_url, plural_url}"
quote do
alias Farmbot.HTTP
import Farmbot.Database.Syncable, only: [parse_resp: 2]
defstruct unquote(model) ++ [:id]
defimpl Inspect, for: __MODULE__ do
def inspect(syncable, _) do
"#Syncable<#{List.last(Module.split(__MODULE__))} #{syncable.id}>"
end
end
@doc "Find an item by id from the database."
def get_by_id(record_storage, id) do
Farmbot.Database.RecordStorage.get_by_id(record_storage, __MODULE__, id)
end
@doc "Get all items."
def get_all(record_storage) do
Farmbot.Database.RecordStorage.get_all(record_storage, __MODULE__)
end
@doc "Flush all items."
def flush(record_storage) do
Farmbot.Database.RecordStorage.flush(record_storage, __MODULE__)
end
@doc """
The Singular api endpoing url.
"""
def singular_url, do: unquote(singular)
@doc """
The plural api endpoint.
"""
def plural_url, do: unquote(plural)
@doc """
Fetches all `#{__MODULE__}` objects from the API.
"""
def fetch(http, then) do
url = "/api" <> plural_url()
result = http |> HTTP.get(url) |> parse_resp(__MODULE__)
if function_exported?(__MODULE__, :on_fetch, 2) do
apply __MODULE__, :on_fetch, [result]
end
case then do
{module, fun, args} -> apply(module, fun, [result | args])
anon when is_function(anon) -> anon.(result)
end
end
@doc """
Fetches a specific `#{__MODULE__}` from the API, by it's id.
"""
def fetch(http, id, then) do
url = "/api" <> unquote(singular) <> "/#{id}"
result = http |> HTTP.get(url) |> parse_resp(__MODULE__)
if function_exported?(__MODULE__, :on_fetch, 2) do
apply __MODULE__, :on_fetch, [result]
end
case then do
{module, fun, args} -> apply(module, fun, [result | args])
anon when is_function(anon) -> anon.(result)
end
end
@doc """
Changes a string map, to a struct
"""
def to_struct(item) do
module = __MODULE__
sym_keys = Map.keys(%__MODULE__{})
str_keys = Enum.map(sym_keys, fn(key) -> Atom.to_string(key) end)
next = Map.take(item, str_keys)
new = Map.new(next, fn({key, val}) -> {String.to_atom(key), val} end)
struct(module, new)
end
end
end
end

View File

@ -1,16 +0,0 @@
defmodule Farmbot.Database.Syncable.Device do
@moduledoc """
A Device from the Farmbot API.
"""
alias Farmbot.Database
alias Database.Syncable
use Syncable, model: [
:name,
:timezone
], endpoint: {"/device", "/device"}
# def on_fetch(%Context{} = context, %__MODULE__{timezone: tz}) do
# true = Farmbot.BotState.update_config(context, "timezone", tz)
# end
end

View File

@ -1,16 +0,0 @@
defmodule Farmbot.Database.Syncable.FarmEvent do
@moduledoc """
A FarmEvent from the Farmbot API.
"""
alias Farmbot.Database.Syncable
use Syncable, model: [
:start_time,
:end_time,
:repeat,
:time_unit,
:executable_id,
:executable_type,
:calendar
], endpoint: {"/farm_events", "/farm_events"}
end

View File

@ -1,32 +0,0 @@
defmodule Farmbot.Database.Syncable.Peripheral do
@moduledoc """
A Peripheral from the Farmbot API.
"""
alias Farmbot.Database
alias Database.Syncable
use Syncable, model: [
:pin,
:mode,
:label
], endpoint: {"/peripherals", "/peripherals"}
# def on_fetch(context, object_or_list)
#
# def on_fetch(%Context{} = _, []), do: :ok
#
# def on_fetch(%Context{} = context, [%__MODULE__{} = first | rest]) do
# on_fetch(context, first)
# on_fetch(context, rest)
# end
#
# def on_fetch(%Context{} = context, %__MODULE__{pin: pin, mode: mode, label: label}) do
# :ok = Farmbot.BotState.set_pin_mode(context, pin, mode)
# ast = %Ast{
# kind: "read_pin",
# args: %{pin_number: pin, pin_mode: mode, label: label},
# body: []
# }
# Command.do_command(ast, context)
# end
end

View File

@ -1,31 +0,0 @@
defmodule Farmbot.Database.Syncable.Point do
@moduledoc """
A Point from the Farmbot API.
"""
alias Farmbot.Database
alias Database.{Syncable, Selectors}
alias Selectors.Error, as: SelectorError
use Syncable, model: [
:pointer_type,
:created_at,
:tool_id,
:radius,
:name,
:meta,
:x,
:y,
:z,
], endpoint: {"/points", "/points"}
@doc """
Turn a tool into a Point.
"""
def get_tool(record_storage, tool_id) do
record_storage
|> Database.RecordStorage.get_all(__MODULE__)
|> Enum.find(fn(%{body: point}) -> point.tool_id == tool_id end) ||
raise SelectorError, syncable: __MODULE__, syncable_id: tool_id,
message: "Could not find tool_slot with tool_id: #{tool_id}"
end
end

View File

@ -1,12 +0,0 @@
defmodule Farmbot.Database.Syncable.Regimen do
@moduledoc """
A Regimen from the Farmbot API.
"""
alias Farmbot.Database
alias Database.Syncable
use Syncable, model: [
:name,
:regimen_items
], endpoint: {"/regimens", "/regimens"}
end

View File

@ -1,15 +0,0 @@
defmodule Farmbot.Database.Syncable.Sequence do
@moduledoc """
A Sequence from the Farmbot API.
"""
alias Farmbot.Database
alias Database.Syncable
use Syncable, model: [
:version,
:body,
:args,
:kind,
:name
], endpoint: {"/sequences", "/sequences"}
end

View File

@ -1,12 +0,0 @@
defmodule Farmbot.Database.Syncable.Tool do
@moduledoc """
A Tool from the Farmbot API.
"""
alias Farmbot.Database
alias Database.Syncable
use Syncable, model: [
:name,
:status
], endpoint: {"/tools", "/tools"}
end

View File

@ -1,4 +0,0 @@
defmodule Farmbot.Database.Syncable.Error do
@moduledoc "Syncable error"
defexception [:message]
end

View File

@ -0,0 +1,5 @@
defmodule Farmbot.Repo do
use Ecto.Repo,
otp_app: :farmbot,
adapter: Sqlite.Ecto2
end

View File

@ -19,7 +19,6 @@ defmodule Farmbot.System do
Should remove all persistant data. this includes:
* network config
* credentials
* database
"""
@callback factory_reset(reason) :: no_return
@ -49,5 +48,5 @@ defmodule Farmbot.System do
@spec shutdown(unparsed_reason) :: no_return
def shutdown(reason) do
@system_tasks.shutdown(reason)
end
end
end

13
mix.exs
View File

@ -86,6 +86,8 @@ defmodule Farmbot.Mixfile do
{:cowboy, "~> 1.0.0"},
{:plug, "~> 1.0"},
{:ecto, "~> 2.2.2"},
{:sqlite_ecto2, "~> 2.2.1"}
]
end
@ -107,6 +109,7 @@ defmodule Farmbot.Mixfile do
end
defp system("rpi0"), do: {:nerves_system_rpi0, ">= 0.0.0", runtime: false}
defp system("qemu_arm"), do: {:nerves_system_qemu_arm, ">= 0.0.0", runtime: false}
defp package do
[
@ -117,20 +120,18 @@ defmodule Farmbot.Mixfile do
end
defp elixirc_paths(:test, "host") do
["./lib", "./nerves/host", "./test/support"]
["./lib", "./nerves/farmbot/host", "./test/support"]
end
defp elixirc_paths(_env, target) do
["./lib", "./nerves/#{target}"]
["./lib", "./nerves/farmbot/target"]
end
defp aliases("host"), do: []
defp aliases(_system) do
["deps.precompile": ["nerves.precompile", "deps.precompile"],
"deps.loadpaths": ["deps.loadpaths", "nerves.loadpaths"],
"firmware.upload": ["farmbot.upload"],
"firmware.sign": ["farmbot.sign"]
]
"deps.loadpaths": ["deps.loadpaths", "nerves.loadpaths"]
]
end
end

View File

@ -1,12 +1,17 @@
%{"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [], [], "hexpm"},
"certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [], [], "hexpm"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [], [], "hexpm"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [], [], "hexpm"},
"cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [], [{:cowlib, "~> 1.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [], [], "hexpm"},
"credo": {:hex, :credo, "0.8.6", "335f723772d35da499b5ebfdaf6b426bfb73590b6fcbc8908d476b75f8cbca3f", [], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}], "hexpm"},
"db_connection": {:hex, :db_connection, "1.1.2", "2865c2a4bae0714e2213a0ce60a1b12d76a6efba0c51fbda59c9ab8d1accc7a8", [], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"decimal": {:hex, :decimal, "1.4.0", "fac965ce71a46aab53d3a6ce45662806bdd708a4a95a65cde8a12eb0124a1333", [], [], "hexpm"},
"distillery": {:hex, :distillery, "1.5.1", "7ad7354214959c0f65f57ddd49478c81c3b2733ca2e5ccfb9eb55351108466aa", [], [], "hexpm"},
"earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [], [], "hexpm"},
"ecto": {:hex, :ecto, "2.2.3", "b1896b129db30d54073bedd5f3ba8a99dd6d64ebae3cf59057ae287060b46905", [], [{: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.0", "992f38fabe705bb45821a728f20914c554b276838433349d4f2341f7a687cddf", [], [], "hexpm"},
"esqlite": {:hex, :esqlite, "0.2.3", "1a8b60877fdd3d50a8a84b342db04032c0231cc27ecff4ddd0d934485d4c0cd5", [], [], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.16.3", "cd2a4cfe5d26e37502d3ec776702c72efa1adfa24ed9ce723bb565f4c30bd31a", [], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
"ex_json_schema": {:hex, :ex_json_schema, "0.5.5", "d8d4c3f47b86c9e634e124d518b290dda82a8b94dcc314e45af10042fc369361", [], [], "hexpm"},
"excoveralls": {:hex, :excoveralls, "0.7.2", "f69ede8c122ccd3b60afc775348a53fc8c39fe4278aee2f538f0d81cc5e7ff3a", [], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:hackney, ">= 0.12.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
@ -27,8 +32,12 @@
"nerves_uart": {:hex, :nerves_uart, "0.1.2", "4310dbb1721a5a007b8e5c416cf81754415bde6b7e2c9aa65a059886b85e637c", [], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [], [], "hexpm"},
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [], [], "hexpm"},
"ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [], [], "hexpm"},
"rsa": {:hex, :rsa, "0.0.1", "a63069f88ce342ffdf8448b7cdef4b39ba7dee3c1510644a39385c7e63ba246f", [], [], "hexpm"},
"sbroker": {:hex, :sbroker, "1.0.0", "28ff1b5e58887c5098539f236307b36fe1d3edaa2acff9d6a3d17c2dcafebbd0", [], [], "hexpm"},
"sqlite_ecto2": {:hex, :sqlite_ecto2, "2.2.1", "6447456ef4264177d16e489b88f7abc63463e9eddc1fef4358b3f73562b7a2d8", [], [{:connection, "~> 1.0.3", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 2.2.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.2.3", [hex: :esqlite, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.13.0", [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.3.3", "3aac5fd702be346f71d9de6e01702c9954484cd0971aa443490bb3bde045d919", [], [{:decimal, "~> 1.1", [hex: :decimal, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.2.3", [hex: :esqlite, repo: "hexpm", optional: false]}], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [], [], "hexpm"},
"timex": {:hex, :timex, "3.1.24", "d198ae9783ac807721cca0c5535384ebdf99da4976be8cefb9665a9262a1e9e3", [], [{:combine, "~> 0.7", [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.1.201605", "0c4184819b9d6adedcc02107b68321c45d8e853def7a32629b7961b9f2e95f33", [], [], "hexpm"},

51
mix.lock.qemu_arm 100644
View File

@ -0,0 +1,51 @@
%{"bootloader": {:hex, :bootloader, "0.1.2", "835ddcf50b796714658f342061d5d48ebc34cbd0d81cdbd5a5a8ae00705d72b1", [], [{:distillery, "~> 1.0", [hex: :distillery, repo: "hexpm", optional: false]}], "hexpm"},
"certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [], [], "hexpm"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [], [], "hexpm"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [], [], "hexpm"},
"cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [], [{:cowlib, "~> 1.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [], [], "hexpm"},
"db_connection": {:hex, :db_connection, "1.1.2", "2865c2a4bae0714e2213a0ce60a1b12d76a6efba0c51fbda59c9ab8d1accc7a8", [], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"decimal": {:hex, :decimal, "1.4.0", "fac965ce71a46aab53d3a6ce45662806bdd708a4a95a65cde8a12eb0124a1333", [], [], "hexpm"},
"distillery": {:hex, :distillery, "1.5.1", "7ad7354214959c0f65f57ddd49478c81c3b2733ca2e5ccfb9eb55351108466aa", [], [], "hexpm"},
"dns": {:hex, :dns, "1.0.1", "1d88187fdf564d937cee202949141090707fd0c9d7fcae903a6878ef24ef5d1e", [], [{:socket, "~> 0.3.12", [hex: :socket, repo: "hexpm", optional: false]}], "hexpm"},
"ecto": {:hex, :ecto, "2.2.3", "b1896b129db30d54073bedd5f3ba8a99dd6d64ebae3cf59057ae287060b46905", [], [{: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.0", "992f38fabe705bb45821a728f20914c554b276838433349d4f2341f7a687cddf", [], [], "hexpm"},
"esqlite": {:hex, :esqlite, "0.2.3", "1a8b60877fdd3d50a8a84b342db04032c0231cc27ecff4ddd0d934485d4c0cd5", [], [], "hexpm"},
"ex_json_schema": {:hex, :ex_json_schema, "0.5.5", "d8d4c3f47b86c9e634e124d518b290dda82a8b94dcc314e45af10042fc369361", [], [], "hexpm"},
"gen_mqtt": {:hex, :gen_mqtt, "0.3.1", "6ce6af7c2bcb125d5b4125c67c5ab1f29bcec2638236509bcc6abf510a6661ed", [], [{:vmq_commons, "1.0.0", [hex: :vmq_commons, repo: "hexpm", optional: false]}], "hexpm"},
"gettext": {:hex, :gettext, "0.13.1", "5e0daf4e7636d771c4c71ad5f3f53ba09a9ae5c250e1ab9c42ba9edccc476263", [], [], "hexpm"},
"hackney": {:hex, :hackney, "1.9.0", "51c506afc0a365868469dcfc79a9d0b94d896ec741cfd5bd338f49a5ec515bfe", [], [{:certifi, "2.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"httpoison": {:hex, :httpoison, "0.13.0", "bfaf44d9f133a6599886720f3937a7699466d23bb0cd7a88b6ba011f53c6f562", [], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"mdns": {:hex, :mdns, "0.1.6", "b51b902b15b50e0e1522483c6a5fb073413e3d3d6ef52a44b93a541460b47d29", [], [{:dns, "~> 1.0", [hex: :dns, repo: "hexpm", optional: false]}], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [], [], "hexpm"},
"mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [], [], "hexpm"},
"nerves": {:hex, :nerves, "0.7.5", "3aa6a336b2ad6c1c9589cc2b577511b3c4c375c1ba6c533ab9f88adb8c21f0c3", [], [{:distillery, "~> 1.4", [hex: :distillery, repo: "hexpm", optional: false]}], "hexpm"},
"nerves_firmware_ssh": {:hex, :nerves_firmware_ssh, "0.2.2", "a876f4e44ccc02606b923d7097b64dc7793384d716583cfca756b7f0dff9d441", [], [{:nerves_runtime, "~> 0.4", [hex: :nerves_runtime, repo: "hexpm", optional: false]}], "hexpm"},
"nerves_init_gadget": {:hex, :nerves_init_gadget, "0.2.1", "20f36dd062fb00e2be8817ddff1b9ced9762877cfe23f6ec1d5936a37e3fc2c8", [], [{:mdns, "~> 0.1", [hex: :mdns, repo: "hexpm", optional: false]}, {:nerves_firmware_ssh, "~> 0.2", [hex: :nerves_firmware_ssh, repo: "hexpm", optional: false]}, {:nerves_network, "~> 0.3", [hex: :nerves_network, repo: "hexpm", optional: false]}, {:nerves_runtime, "~> 0.3", [hex: :nerves_runtime, repo: "hexpm", optional: false]}], "hexpm"},
"nerves_network": {:hex, :nerves_network, "0.3.4", "c50a36b8263cda2bee18f408287d0f4474f8367702d170864325abbd5d424e4d", [], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nerves_network_interface, "~> 0.4.0", [hex: :nerves_network_interface, repo: "hexpm", optional: false]}, {:nerves_wpa_supplicant, "~> 0.3.0", [hex: :nerves_wpa_supplicant, repo: "hexpm", optional: false]}, {:system_registry, "~> 0.4", [hex: :system_registry, repo: "hexpm", optional: false]}], "hexpm"},
"nerves_network_interface": {:hex, :nerves_network_interface, "0.4.2", "7a3663a07803f2f9f1e37146714d24ccec1e9349268586e4ed8c41f38641d837", [], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"nerves_runtime": {:hex, :nerves_runtime, "0.4.4", "26034bc7d13dbd46aab2f429f988656621a4d91872ccf5fa748c16630bd65016", [], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:system_registry, "~> 0.5", [hex: :system_registry, repo: "hexpm", optional: false]}], "hexpm"},
"nerves_system_br": {:hex, :nerves_system_br, "0.13.7", "85399547717c0386a4f2039e3467c14a23c3b77f66a4b97c2a67e48734dab2bb", [], [], "hexpm"},
"nerves_system_qemu_arm": {:hex, :nerves_system_qemu_arm, "0.12.2", "9b90ad998ed75e93edd96fc79c8c8e0c872926ddde05ad56031ad81a903dc4d5", [], [{:nerves, "~> 0.7", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_system_br, "~> 0.13.7", [hex: :nerves_system_br, repo: "hexpm", optional: false]}, {:nerves_toolchain_arm_unknown_linux_gnueabihf, "~> 0.11.0", [hex: :nerves_toolchain_arm_unknown_linux_gnueabihf, repo: "hexpm", optional: false]}], "hexpm"},
"nerves_toolchain_arm_unknown_linux_gnueabihf": {:hex, :nerves_toolchain_arm_unknown_linux_gnueabihf, "0.11.0", "8d7606275a2d19de26ae238cd59475f4c06679aa9222b8987518d7c8a7beae51", [], [{:nerves, "~> 0.7", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.1", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm"},
"nerves_toolchain_ctng": {:hex, :nerves_toolchain_ctng, "1.1.0", "0f03e4a3f3beef5fe271de0148b9f106c417e57f303f635c21c74b4bd6eb68ee", [], [], "hexpm"},
"nerves_uart": {:hex, :nerves_uart, "0.1.2", "4310dbb1721a5a007b8e5c416cf81754415bde6b7e2c9aa65a059886b85e637c", [], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"nerves_wpa_supplicant": {:hex, :nerves_wpa_supplicant, "0.3.2", "19dc7e1248336e7f542b11b2b857ceb5b088d3eb41a6ca75b7b76628dcf67aad", [], [{:elixir_make, "~> 0.3", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [], [], "hexpm"},
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [], [], "hexpm"},
"ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [], [], "hexpm"},
"rsa": {:hex, :rsa, "0.0.1", "a63069f88ce342ffdf8448b7cdef4b39ba7dee3c1510644a39385c7e63ba246f", [], [], "hexpm"},
"sbroker": {:hex, :sbroker, "1.0.0", "28ff1b5e58887c5098539f236307b36fe1d3edaa2acff9d6a3d17c2dcafebbd0", [], [], "hexpm"},
"socket": {:hex, :socket, "0.3.12", "4a6543815136503fee67eff0932da1742fad83f84c49130c854114153cc549a6", [], [], "hexpm"},
"sqlite_ecto2": {:hex, :sqlite_ecto2, "2.2.1", "6447456ef4264177d16e489b88f7abc63463e9eddc1fef4358b3f73562b7a2d8", [], [{:connection, "~> 1.0.3", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 2.2.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.2.3", [hex: :esqlite, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.13.0", [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.3.3", "3aac5fd702be346f71d9de6e01702c9954484cd0971aa443490bb3bde045d919", [], [{:decimal, "~> 1.1", [hex: :decimal, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.2.3", [hex: :esqlite, repo: "hexpm", optional: false]}], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [], [], "hexpm"},
"system_registry": {:hex, :system_registry, "0.6.0", "31642177e6002d3cff2ada3553ed4e9c0a6ca015797d62d7d17c0ab8696185fc", [], [], "hexpm"},
"timex": {:hex, :timex, "3.1.24", "d198ae9783ac807721cca0c5535384ebdf99da4976be8cefb9665a9262a1e9e3", [], [{:combine, "~> 0.7", [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.1.201605", "0c4184819b9d6adedcc02107b68321c45d8e853def7a32629b7961b9f2e95f33", [], [], "hexpm"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [], [], "hexpm"},
"uuid": {:hex, :uuid, "1.1.7", "007afd58273bc0bc7f849c3bdc763e2f8124e83b957e515368c498b641f7ab69", [], [], "hexpm"},
"vmq_commons": {:hex, :vmq_commons, "1.0.0", "5f5005c12db33f92f40e818a3617fb148972d59adcf99298c9d3808ef3582e34", [], [], "hexpm"}}

View File

@ -1,9 +1,16 @@
%{"bootloader": {:hex, :bootloader, "0.1.2", "835ddcf50b796714658f342061d5d48ebc34cbd0d81cdbd5a5a8ae00705d72b1", [], [{:distillery, "~> 1.0", [hex: :distillery, repo: "hexpm", optional: false]}], "hexpm"},
"certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [], [], "hexpm"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [], [], "hexpm"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [], [], "hexpm"},
"cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [], [{:cowlib, "~> 1.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [], [], "hexpm"},
"db_connection": {:hex, :db_connection, "1.1.2", "2865c2a4bae0714e2213a0ce60a1b12d76a6efba0c51fbda59c9ab8d1accc7a8", [], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"decimal": {:hex, :decimal, "1.4.0", "fac965ce71a46aab53d3a6ce45662806bdd708a4a95a65cde8a12eb0124a1333", [], [], "hexpm"},
"distillery": {:hex, :distillery, "1.5.1", "7ad7354214959c0f65f57ddd49478c81c3b2733ca2e5ccfb9eb55351108466aa", [], [], "hexpm"},
"dns": {:hex, :dns, "1.0.1", "1d88187fdf564d937cee202949141090707fd0c9d7fcae903a6878ef24ef5d1e", [], [{:socket, "~> 0.3.12", [hex: :socket, repo: "hexpm", optional: false]}], "hexpm"},
"ecto": {:hex, :ecto, "2.2.3", "b1896b129db30d54073bedd5f3ba8a99dd6d64ebae3cf59057ae287060b46905", [], [{: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.0", "992f38fabe705bb45821a728f20914c554b276838433349d4f2341f7a687cddf", [], [], "hexpm"},
"esqlite": {:hex, :esqlite, "0.2.3", "1a8b60877fdd3d50a8a84b342db04032c0231cc27ecff4ddd0d934485d4c0cd5", [], [], "hexpm"},
"ex_json_schema": {:hex, :ex_json_schema, "0.5.5", "d8d4c3f47b86c9e634e124d518b290dda82a8b94dcc314e45af10042fc369361", [], [], "hexpm"},
"ex_syslogger": {:git, "https://github.com/slashmili/ex_syslogger.git", "7e2ddccb90b69d1b593d24b71f46c682c0300c00", []},
"fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [], [], "hexpm"},
@ -14,6 +21,7 @@
"idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"mdns": {:hex, :mdns, "0.1.6", "b51b902b15b50e0e1522483c6a5fb073413e3d3d6ef52a44b93a541460b47d29", [], [{:dns, "~> 1.0", [hex: :dns, repo: "hexpm", optional: false]}], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [], [], "hexpm"},
"mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [], [], "hexpm"},
"nerves": {:hex, :nerves, "0.7.5", "3aa6a336b2ad6c1c9589cc2b577511b3c4c375c1ba6c533ab9f88adb8c21f0c3", [], [{:distillery, "~> 1.4", [hex: :distillery, repo: "hexpm", optional: false]}], "hexpm"},
"nerves_firmware_ssh": {:hex, :nerves_firmware_ssh, "0.2.2", "a876f4e44ccc02606b923d7097b64dc7793384d716583cfca756b7f0dff9d441", [], [{:nerves_runtime, "~> 0.4", [hex: :nerves_runtime, repo: "hexpm", optional: false]}], "hexpm"},
@ -28,9 +36,15 @@
"nerves_uart": {:hex, :nerves_uart, "0.1.2", "4310dbb1721a5a007b8e5c416cf81754415bde6b7e2c9aa65a059886b85e637c", [], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"nerves_wpa_supplicant": {:hex, :nerves_wpa_supplicant, "0.3.2", "19dc7e1248336e7f542b11b2b857ceb5b088d3eb41a6ca75b7b76628dcf67aad", [], [{:elixir_make, "~> 0.3", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"neves_init_gadget": {:git, "https://github.com/fhunleth/nerves_init_gadget.git", "801b0e2ec96b6b50585b8bf86e605608502e41b6", []},
"plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [], [], "hexpm"},
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [], [], "hexpm"},
"ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [], [], "hexpm"},
"rsa": {:hex, :rsa, "0.0.1", "a63069f88ce342ffdf8448b7cdef4b39ba7dee3c1510644a39385c7e63ba246f", [], [], "hexpm"},
"sbroker": {:hex, :sbroker, "1.0.0", "28ff1b5e58887c5098539f236307b36fe1d3edaa2acff9d6a3d17c2dcafebbd0", [], [], "hexpm"},
"socket": {:hex, :socket, "0.3.12", "4a6543815136503fee67eff0932da1742fad83f84c49130c854114153cc549a6", [], [], "hexpm"},
"sqlite_ecto2": {:hex, :sqlite_ecto2, "2.2.1", "6447456ef4264177d16e489b88f7abc63463e9eddc1fef4358b3f73562b7a2d8", [], [{:connection, "~> 1.0.3", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 2.2.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.2.3", [hex: :esqlite, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.13.0", [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.3.3", "3aac5fd702be346f71d9de6e01702c9954484cd0971aa443490bb3bde045d919", [], [{:decimal, "~> 1.1", [hex: :decimal, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.2.3", [hex: :esqlite, repo: "hexpm", optional: false]}], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [], [], "hexpm"},
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]},
"system_registry": {:hex, :system_registry, "0.6.0", "31642177e6002d3cff2ada3553ed4e9c0a6ca015797d62d7d17c0ab8696185fc", [], [], "hexpm"},

View File

@ -0,0 +1,35 @@
defmodule Farmbot.Target.Ecto do
@moduledoc "Setup the Database."
@behaviour Farmbot.System.Init
require Logger
def start_link(_, _opts) do
[repo] = Application.get_env(:farmbot, :ecto_repos)
Logger.info "Dropping DB."
repo.__adapter__.storage_down(repo.config)
Logger.info "Creating DB."
repo.__adapter__.storage_up(repo.config)
Logger.info "Migrating DB."
opts = [all: true]
{:ok, pid, apps} = Mix.Ecto.ensure_started(repo, opts)
migrator = &Ecto.Migrator.run/4
pool = repo.config[:pool]
migrations_path = Path.join((:code.priv_dir(:farmbot) |> to_string), "repo")
migrated =
if function_exported?(pool, :unboxed_run, 2) do
pool.unboxed_run(repo, fn -> migrator.(repo, migrations_path, :up, opts) end)
else
migrator.(repo, migrations_path, :up, opts)
end
pid && repo.stop(pid)
Mix.Ecto.restart_apps_if_migrated(apps, migrated)
:ignore
end
end

View File

@ -0,0 +1,9 @@
defmodule Farmbot.Target.Network do
@moduledoc "Bring up network."
@behaviour Farmbot.System.Init
def start_link(_, _opts) do
:ignore
end
end

View File

@ -0,0 +1,15 @@
defmodule Farmbot.Target.SystemTasks do
@moduledoc "Target implementation for System Tasks."
@behaviour Farmbot.System
def factory_reset(_reason) do
end
def reboot(_reason) do
end
def shutdown(_reason) do
end
end

View File

@ -1,9 +0,0 @@
defmodule Farmbot.Host.FirmwareHandlerStub do
@moduledoc "Stubs out firmware functionality when you don't have an arduino."
use GenServer
@doc "Start the firmware handler stub."
def start_link(firmware, opts) do
GenServer.start_link(__MODULE__, firmware, opts)
end
end

View File

@ -0,0 +1,7 @@
defmodule Farmbot.Repo.Migrations.AddThing do
use Ecto.Migration
def change do
end
end

View File

@ -1,71 +0,0 @@
defmodule Farmbot.Database.RecordStorageTest do
@moduledoc "Test storage implementation."
use ExUnit.Case
alias Farmbot.Database.RecordStorage, as: RS
alias Farmbot.Database.Syncable
defmodule SomeSyncable do
use Farmbot.Database.Syncable, model: [:foo, :bar], endpoint: {"fake", "nope"}
end
defmodule SomeOtherSyncable do
use Farmbot.Database.Syncable, model: [:foo, :baz], endpoint: {"fake", "nope"}
end
setup do
{:ok, rs} = RS.start_link([])
[rs: rs]
end
test "Commits a single new record.", ctx do
record = %SomeSyncable{foo: 1, bar: 1, id: 123}
:ok = RS.commit_records(record, ctx.rs)
assert RS.get_all(ctx.rs, SomeSyncable) == [%Syncable{body: record, ref_id: {SomeSyncable, -1, 123}}]
end
test "Commits a list of new records.", ctx do
records = Enum.map(0..10, fn(id) -> %SomeSyncable{foo: "hello", bar: "world", id: id} end)
:ok = RS.commit_records(records, ctx.rs)
assert Enum.all?(0..10, fn(id) ->
assert RS.get_by_id(ctx.rs, SomeSyncable, id) == %Syncable{body: %SomeSyncable{foo: "hello", bar: "world", id: id}, ref_id: {SomeSyncable, -1, id}}
end)
end
test "updates a record", ctx do
record_a = %SomeSyncable{foo: 1, bar: 1, id: 123}
:ok = RS.commit_records(record_a, ctx.rs)
record_b = %{record_a | foo: 123, bar: 123}
RS.commit_records(record_b, ctx.rs)
r = RS.get_by_id(ctx.rs, SomeSyncable, 123)
assert r == %Syncable{body: %SomeSyncable{bar: 123, foo: 123, id: 123}, ref_id: {SomeSyncable, -1, 123}}
refute Enum.count(RS.get_all(ctx.rs, SomeSyncable)) > 2
end
test "Inspects the state reasonably.", ctx do
record = %SomeSyncable{foo: 1, bar: 1, id: 123}
:ok = RS.commit_records(record, ctx.rs)
assert inspect( :sys.get_state(ctx.rs)) == "#DatabaseState<[{Farmbot.Database.RecordStorageTest.SomeSyncable, -1, 123}]>"
end
test "flushes all records.", ctx do
record = %SomeSyncable{foo: 1, bar: 1, id: 123}
:ok = RS.commit_records(record, ctx.rs)
RS.flush(ctx.rs)
assert RS.get_all(ctx.rs, SomeSyncable) == []
assert inspect( :sys.get_state(ctx.rs)) == "#DatabaseState<[]>"
end
test "flushes records of one syncable.", ctx do
record1 = %SomeSyncable{foo: 1, bar: 2, id: 22}
record2 = %SomeOtherSyncable{foo: 12, baz: 33, id: 123 }
:ok = RS.commit_records([record1, record2], ctx.rs)
RS.flush(ctx.rs, SomeSyncable)
assert RS.get_all(ctx.rs, SomeSyncable) == []
assert match?([_syncable], RS.get_all(ctx.rs, SomeOtherSyncable))
end
end

View File

@ -1,140 +0,0 @@
defmodule Farmbot.Database.SelectorsTest do
@moduledoc "Tests selectors."
use ExUnit.Case
alias Farmbot.Database.RecordStorage, as: RS
alias Farmbot.Database.Syncable
alias Syncable.Point
alias Syncable.Tool
alias Farmbot.Database.Selectors
alias Selectors.Error, as: SelectorError
setup do
{:ok, rs} = RS.start_link([])
[
%Point{
pointer_type: "Plant",
created_at: nil,
tool_id: nil,
radius: 5,
name: "Cabbage",
meta: nil,
x: 0,
y: 1,
z: 2,
id: 1
},
%Tool{
name: "Laser Beam",
status: "idle",
id: 9000
},
%Point{
pointer_type: "ToolSlot",
created_at: nil,
tool_id: 9000,
radius: 5,
name: "Laser Beam holder",
meta: nil,
x: 0,
y: 10,
z: 10,
id: 2
},
%Point{
pointer_type: "GenericPointer",
created_at: nil,
tool_id: nil,
radius: 5,
name: "Hole in the bed",
meta: nil,
x: 123,
y: 222,
z: 0,
id: 3
},
] |> RS.commit_records(rs)
[rs: rs]
end
test "finds a plant", ctx do
r = Selectors.find_point(ctx.rs, "Plant", 1)
assert r == %Syncable{body: %Point{
pointer_type: "Plant",
created_at: nil,
tool_id: nil,
radius: 5,
name: "Cabbage",
meta: nil,
x: 0,
y: 1,
z: 2,
id: 1
}, ref_id: {Point, -1, 1}}
end
test "finds a toolslot", ctx do
r = Selectors.find_point(ctx.rs, "GenericPointer", 3)
assert r == %Syncable{body: %Point{
pointer_type: "GenericPointer",
created_at: nil,
tool_id: nil,
radius: 5,
name: "Hole in the bed",
meta: nil,
x: 123,
y: 222,
z: 0,
id: 3
}, ref_id: {Point, -1, 3}}
end
test "finds an uncatagorized pointer.", ctx do
r = Selectors.find_point(ctx.rs, "ToolSlot", 2)
assert r == %Syncable{body: %Point{
pointer_type: "ToolSlot",
created_at: nil,
tool_id: 9000,
radius: 5,
name: "Laser Beam holder",
meta: nil,
x: 0,
y: 10,
z: 10,
id: 2
}, ref_id: {Point, -1, 2}}
end
test "raises when requesting the wrong type", ctx do
assert_raise SelectorError, "does not match type: ToolSlot", fn() ->
Selectors.find_point(ctx.rs, "ToolSlot", 1)
end
end
alias Syncable.Device
test "Gets the db entry for this device.", ctx do
device_record = %Device{name: "farmbot_negative_one", timezone: nil, id: 1}
RS.commit_records(device_record, ctx.rs)
device = Selectors.get_device(ctx.rs)
assert device.name == "farmbot_negative_one"
end
test "raises if there is more than one device", ctx do
device_record_1 = %Device{name: "farmbot_negative_one", timezone: nil, id: 1}
device_record_2 = %Device{name: "farmbot_negative_two", timezone: nil, id: 2}
RS.commit_records([device_record_1, device_record_2], ctx.rs)
assert_raise SelectorError, "Too many devices.", fn() ->
IO.inspect Selectors.get_device(ctx.rs)
end
end
test "raises if there is no device", ctx do
assert_raise SelectorError, "No device.", fn() ->
Selectors.get_device(ctx.rs)
end
end
end

View File

@ -1,58 +0,0 @@
defmodule Farmbot.Database.Syncable.PointTest do
@moduledoc "Tests point funs."
alias Farmbot.Database.{Syncable, Selectors}
alias Selectors.Error, as: SelectorError
alias Syncable.{Point, Tool}
alias Farmbot.Database.RecordStorage, as: RS
use ExUnit.Case
setup do
{:ok, rs} = RS.start_link([])
[rs: rs]
end
test "gets a tool by id", ctx do
[
%Tool{
name: "Laser Beam",
status: "idle",
id: 9000
},
%Point{
pointer_type: "ToolSlot",
created_at: nil,
tool_id: 9000,
radius: 5,
name: "Laser Beam holder",
meta: nil,
x: 0,
y: 10,
z: 10,
id: 2
},
] |> RS.commit_records(ctx.rs)
assert Point.get_tool(ctx.rs, 9000) == %Syncable{body: %Point{
pointer_type: "ToolSlot",
created_at: nil,
tool_id: 9000,
radius: 5,
name: "Laser Beam holder",
meta: nil,
x: 0,
y: 10,
z: 10,
id: 2
}, ref_id: {Point, -1, 2}}
end
test "Raises when a tool doesn't exist", ctx do
assert_raise SelectorError, "Could not find tool_slot with tool_id: 1234",
fn() ->
Point.get_tool(ctx.rs, 1234)
end
end
end