Implement Regimens again, add tests and test support
* This also includes changes to all Farmbot API Assets adding a `monitor: :boolean` field. This allows tests to be run causing minimal side effects on the rest of the systems.pull/974/head
parent
a4c1eac2d2
commit
6e2a018598
|
@ -12,3 +12,5 @@ alias Farmbot.Asset.{
|
|||
PersistentRegimen,
|
||||
Sequence
|
||||
}
|
||||
|
||||
alias Farmbot.TestSupport.AssetFixtures
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use Mix.Config
|
||||
|
||||
config :farmbot_core, Farmbot.AssetWorker.Farmbot.Asset.FarmEvent, checkup_time_ms: 10_000
|
||||
config :farmbot_core, Farmbot.AssetWorker.Farmbot.Asset.PersistentRegimen, checkup_time_ms: 10_000
|
||||
|
||||
config :farmbot_core, Farmbot.AssetWorker.Farmbot.Asset.FarmwareInstallation,
|
||||
error_retry_time_ms: 30_000,
|
||||
|
|
|
@ -4,6 +4,7 @@ config :farmbot_core, :behaviour,
|
|||
celery_script_io_layer: Farmbot.TestSupport.CeleryScript.TestIOLayer
|
||||
|
||||
config :farmbot_core, Farmbot.AssetWorker.Farmbot.Asset.FarmEvent, checkup_time_ms: 1000
|
||||
config :farmbot_core, Farmbot.AssetWorker.Farmbot.Asset.PersistentRegimen, checkup_time_ms: 1000
|
||||
|
||||
# must be lower than other timers
|
||||
# To ensure other timers have time to timeout
|
||||
|
|
|
@ -65,7 +65,7 @@ defmodule Farmbot.Asset do
|
|||
|
||||
## Begin PersistentRegimen
|
||||
|
||||
def upsert_persistent_regimen(%Regimen{} = regimen, %FarmEvent{} = farm_event, params \\ %{}) do
|
||||
def upsert_persistent_regimen!(%Regimen{} = regimen, %FarmEvent{} = farm_event, params \\ %{}) do
|
||||
q =
|
||||
from(pr in PersistentRegimen,
|
||||
where: pr.regimen_id == ^regimen.local_id and pr.farm_event_id == ^farm_event.local_id
|
||||
|
@ -78,7 +78,13 @@ defmodule Farmbot.Asset do
|
|||
|> PersistentRegimen.changeset(params)
|
||||
|> Ecto.Changeset.put_assoc(:regimen, regimen)
|
||||
|> Ecto.Changeset.put_assoc(:farm_event, farm_event)
|
||||
|> Repo.insert_or_update()
|
||||
|> Repo.insert_or_update!()
|
||||
end
|
||||
|
||||
def update_persistent_regimen!(%PersistentRegimen{} = pr, params \\ %{}) do
|
||||
pr
|
||||
|> PersistentRegimen.changeset(params)
|
||||
|> Repo.update!()
|
||||
end
|
||||
|
||||
## End PersistentRegimen
|
||||
|
|
|
@ -17,6 +17,7 @@ defmodule Farmbot.Asset.Device do
|
|||
|
||||
field(:name, :string)
|
||||
field(:timezone, :string)
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -30,7 +31,7 @@ defmodule Farmbot.Asset.Device do
|
|||
|
||||
def changeset(device, params \\ %{}) do
|
||||
device
|
||||
|> cast(params, [:id, :name, :timezone, :created_at, :updated_at])
|
||||
|> cast(params, [:id, :name, :timezone, :monitor, :created_at, :updated_at])
|
||||
|> validate_required([])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,6 +20,7 @@ defmodule Elixir.Farmbot.Asset.DiagnosticDump do
|
|||
field(:firmware_state, :string)
|
||||
field(:network_interface, :string)
|
||||
field(:fbos_dmesg_dump, :string)
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -47,6 +48,7 @@ defmodule Elixir.Farmbot.Asset.DiagnosticDump do
|
|||
:firmware_state,
|
||||
:network_interface,
|
||||
:fbos_dmesg_dump,
|
||||
:monitor,
|
||||
:created_at,
|
||||
:updated_at
|
||||
])
|
||||
|
|
|
@ -22,6 +22,7 @@ defmodule Elixir.Farmbot.Asset.FarmEvent do
|
|||
|
||||
# Private
|
||||
field(:last_executed, :utc_datetime)
|
||||
field(:monitor, :boolean, default: true)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
@ -49,6 +50,7 @@ defmodule Elixir.Farmbot.Asset.FarmEvent do
|
|||
:start_time,
|
||||
:time_unit,
|
||||
:last_executed,
|
||||
:monitor,
|
||||
:created_at,
|
||||
:updated_at
|
||||
])
|
||||
|
|
|
@ -15,6 +15,7 @@ defmodule Elixir.Farmbot.Asset.FarmwareEnv do
|
|||
|
||||
field(:key, :string)
|
||||
field(:value, :string)
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -28,7 +29,7 @@ defmodule Elixir.Farmbot.Asset.FarmwareEnv do
|
|||
|
||||
def changeset(farmware_env, params \\ %{}) do
|
||||
farmware_env
|
||||
|> cast(params, [:id, :key, :value, :created_at, :updated_at])
|
||||
|> cast(params, [:id, :key, :value, :monitor, :created_at, :updated_at])
|
||||
|> validate_required([])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,6 +18,7 @@ defmodule Farmbot.Asset.FarmwareInstallation do
|
|||
field(:url, :string)
|
||||
|
||||
embeds_one(:manifest, Manifest, on_replace: :update)
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -30,7 +31,7 @@ defmodule Farmbot.Asset.FarmwareInstallation do
|
|||
|
||||
def changeset(farmware_installation, params \\ %{}) do
|
||||
farmware_installation
|
||||
|> cast(params, [:id, :url, :created_at, :updated_at])
|
||||
|> cast(params, [:id, :url, :monitor, :created_at, :updated_at])
|
||||
|> cast_embed(:manifest)
|
||||
|> validate_required([])
|
||||
end
|
||||
|
|
|
@ -27,6 +27,7 @@ defmodule Elixir.Farmbot.Asset.FbosConfig do
|
|||
field(:sequence_body_log, :boolean)
|
||||
field(:sequence_complete_log, :boolean)
|
||||
field(:sequence_init_log, :boolean)
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -68,6 +69,7 @@ defmodule Elixir.Farmbot.Asset.FbosConfig do
|
|||
:sequence_body_log,
|
||||
:sequence_complete_log,
|
||||
:sequence_init_log,
|
||||
:monitor,
|
||||
:created_at,
|
||||
:updated_at
|
||||
])
|
||||
|
|
|
@ -101,6 +101,7 @@ defmodule Elixir.Farmbot.Asset.FirmwareConfig do
|
|||
field(:encoder_invert_x, :float)
|
||||
field(:encoder_missed_steps_max_x, :float)
|
||||
field(:movement_invert_motor_y, :float)
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -290,6 +291,7 @@ defmodule Elixir.Farmbot.Asset.FirmwareConfig do
|
|||
:encoder_invert_x,
|
||||
:encoder_missed_steps_max_x,
|
||||
:movement_invert_motor_y,
|
||||
:monitor,
|
||||
:created_at,
|
||||
:updated_at
|
||||
])
|
||||
|
|
|
@ -17,6 +17,7 @@ defmodule Farmbot.Asset.Peripheral do
|
|||
field(:pin, :integer)
|
||||
field(:mode, :integer)
|
||||
field(:label, :string)
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -31,7 +32,7 @@ defmodule Farmbot.Asset.Peripheral do
|
|||
|
||||
def changeset(peripheral, params \\ %{}) do
|
||||
peripheral
|
||||
|> cast(params, [:id, :pin, :mode, :label, :created_at, :updated_at])
|
||||
|> cast(params, [:id, :pin, :mode, :label, :monitor, :created_at, :updated_at])
|
||||
|> validate_required([])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,14 +7,51 @@ defmodule Farmbot.Asset.PersistentRegimen do
|
|||
schema "persistent_regimens" do
|
||||
belongs_to(:regimen, Farmbot.Asset.Regimen, references: :local_id, type: :binary_id)
|
||||
belongs_to(:farm_event, Farmbot.Asset.FarmEvent, references: :local_id, type: :binary_id)
|
||||
field(:epoch, :utc_datetime)
|
||||
field(:started_at, :utc_datetime)
|
||||
field(:next, :utc_datetime)
|
||||
# Can't use references here.
|
||||
field(:next_sequence_id, :id)
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
def changeset(persistent_regimen, params \\ %{}) do
|
||||
persistent_regimen
|
||||
|> cast(params, [:started_at])
|
||||
|> cast(params, [:started_at, :next, :next_sequence_id, :monitor])
|
||||
|> put_epoch()
|
||||
|> cast_assoc(:regimen)
|
||||
|> cast_assoc(:farm_event)
|
||||
end
|
||||
|
||||
defp put_epoch(%{valid?: true} = changeset) do
|
||||
started_at = get_field(changeset, :started_at) || DateTime.utc_now()
|
||||
|
||||
if get_field(changeset, :epoch) do
|
||||
changeset
|
||||
else
|
||||
case build_epoch(started_at) do
|
||||
{:ok, epoch} -> put_change(changeset, :epoch, epoch)
|
||||
:error -> add_error(changeset, :epoch, "Missing timezone")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp put_epoch(changeset), do: changeset
|
||||
|
||||
# returns midnight of today
|
||||
@spec build_epoch(DateTime.t()) :: DateTime.t()
|
||||
def build_epoch(%DateTime{} = datetime) do
|
||||
case Farmbot.Asset.device().timezone do
|
||||
nil ->
|
||||
:error
|
||||
|
||||
tz ->
|
||||
%DateTime{} = n = Timex.Timezone.convert(datetime, tz)
|
||||
opts = [hours: -n.hour, seconds: -n.second, minutes: -n.minute]
|
||||
localized_epoch = Timex.shift(n, opts)
|
||||
epoch = Timex.Timezone.convert(localized_epoch, datetime.time_zone)
|
||||
{:ok, epoch}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,6 +16,7 @@ defmodule Farmbot.Asset.PinBinding do
|
|||
field(:pin_num, :integer)
|
||||
field(:sequence_id, :integer)
|
||||
field(:special_action, :string)
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -30,7 +31,15 @@ defmodule Farmbot.Asset.PinBinding do
|
|||
|
||||
def changeset(pin_binding, params \\ %{}) do
|
||||
pin_binding
|
||||
|> cast(params, [:id, :pin_num, :sequence_id, :special_action, :created_at, :updated_at])
|
||||
|> cast(params, [
|
||||
:id,
|
||||
:pin_num,
|
||||
:sequence_id,
|
||||
:special_action,
|
||||
:monitor,
|
||||
:created_at,
|
||||
:updated_at
|
||||
])
|
||||
|> validate_required([])
|
||||
|> validate_pin_num()
|
||||
|> unique_constraint(:pin_num)
|
||||
|
|
|
@ -22,6 +22,7 @@ defmodule Farmbot.Asset.Point do
|
|||
field(:x, :float)
|
||||
field(:y, :float)
|
||||
field(:z, :float)
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -53,6 +54,7 @@ defmodule Farmbot.Asset.Point do
|
|||
:x,
|
||||
:y,
|
||||
:z,
|
||||
:monitor,
|
||||
:created_at,
|
||||
:updated_at
|
||||
])
|
||||
|
|
|
@ -28,6 +28,7 @@ defmodule Farmbot.Asset.Private.LocalMeta do
|
|||
field(:status, :string)
|
||||
field(:table, :string)
|
||||
field(:asset_local_id, :binary_id)
|
||||
field(:monitor, :boolean, default: true)
|
||||
|
||||
belongs_to(:device, Device,
|
||||
foreign_key: :asset_local_id,
|
||||
|
|
|
@ -43,6 +43,7 @@ defmodule Farmbot.Asset.Regimen do
|
|||
|
||||
field(:name, :string)
|
||||
embeds_many(:regimen_items, Item, on_replace: :delete)
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -56,7 +57,7 @@ defmodule Farmbot.Asset.Regimen do
|
|||
|
||||
def changeset(regimen, params \\ %{}) do
|
||||
regimen
|
||||
|> cast(params, [:id, :name, :created_at, :updated_at])
|
||||
|> cast(params, [:id, :name, :monitor, :created_at, :updated_at])
|
||||
|> cast_embed(:regimen_items)
|
||||
|> validate_required([])
|
||||
end
|
||||
|
|
|
@ -17,6 +17,7 @@ defmodule Farmbot.Asset.Sensor do
|
|||
field(:pin, :integer)
|
||||
field(:mode, :integer)
|
||||
field(:label, :string)
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -31,7 +32,7 @@ defmodule Farmbot.Asset.Sensor do
|
|||
|
||||
def changeset(sensor, params \\ %{}) do
|
||||
sensor
|
||||
|> cast(params, [:id, :pin, :mode, :label, :created_at, :updated_at])
|
||||
|> cast(params, [:id, :pin, :mode, :label, :monitor, :created_at, :updated_at])
|
||||
|> validate_required([:id, :pin, :mode, :label])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,6 +20,7 @@ defmodule Farmbot.Asset.SensorReading do
|
|||
field(:x, :float)
|
||||
field(:y, :float)
|
||||
field(:z, :float)
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -38,7 +39,7 @@ defmodule Farmbot.Asset.SensorReading do
|
|||
|
||||
def changeset(sensor, params \\ %{}) do
|
||||
sensor
|
||||
|> cast(params, [:id, :mode, :pin, :value, :x, :y, :z, :created_at, :updated_at])
|
||||
|> cast(params, [:id, :mode, :pin, :value, :x, :y, :z, :monitor, :created_at, :updated_at])
|
||||
|> validate_required([])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,6 +18,7 @@ defmodule Farmbot.Asset.Sequence do
|
|||
field(:kind, :string)
|
||||
field(:args, :map)
|
||||
field(:body, {:array, :map})
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -33,7 +34,7 @@ defmodule Farmbot.Asset.Sequence do
|
|||
|
||||
def changeset(device, params \\ %{}) do
|
||||
device
|
||||
|> cast(params, [:id, :args, :name, :kind, :body, :created_at, :updated_at])
|
||||
|> cast(params, [:id, :args, :name, :kind, :body, :monitor, :created_at, :updated_at])
|
||||
|> validate_required([])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,7 +24,7 @@ defmodule Farmbot.Asset.Supervisor do
|
|||
Repo,
|
||||
{AssetSupervisor, module: FbosConfig},
|
||||
{AssetSupervisor, module: Device},
|
||||
{AssetSupervisor, module: PersistentRegimen, preload: [:farm_event, :regimen]},
|
||||
{AssetSupervisor, module: PersistentRegimen},
|
||||
{AssetSupervisor, module: FarmEvent},
|
||||
{AssetSupervisor, module: PinBinding},
|
||||
{AssetSupervisor, module: Peripheral},
|
||||
|
|
|
@ -13,6 +13,7 @@ defmodule Farmbot.Asset.Tool do
|
|||
)
|
||||
|
||||
field(:name, :string)
|
||||
field(:monitor, :boolean, default: true)
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -25,7 +26,7 @@ defmodule Farmbot.Asset.Tool do
|
|||
|
||||
def changeset(tool, params \\ %{}) do
|
||||
tool
|
||||
|> cast(params, [:id, :name, :created_at, :updated_at])
|
||||
|> cast(params, [:id, :name, :monitor, :created_at, :updated_at])
|
||||
|> validate_required([])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,23 +30,31 @@ defmodule Farmbot.AssetMonitor do
|
|||
# This is helpful for tests, but should probably be avoided
|
||||
@doc false
|
||||
def force_checkup do
|
||||
GenServer.call(__MODULE__, :force_checkup)
|
||||
GenServer.call(__MODULE__, :force_checkup, :infinity)
|
||||
end
|
||||
|
||||
def init(_args) do
|
||||
state = Map.new(order(), fn module -> {module, %{}} end)
|
||||
state = Map.put(state, :order, order())
|
||||
state = Map.put(state, :force_caller, nil)
|
||||
state = Map.put(state, :force_callers, [])
|
||||
{:ok, state, 0}
|
||||
end
|
||||
|
||||
def handle_call(:force_checkup, caller, state) do
|
||||
{:noreply, %{state | force_caller: caller}, 0}
|
||||
{:noreply, %{state | force_callers: state.force_callers ++ [caller]}, 0}
|
||||
end
|
||||
|
||||
def handle_info(:timeout, %{order: []} = state) do
|
||||
if state.force_caller, do: GenServer.reply(state.force_caller, :ok)
|
||||
{:noreply, %{state | order: order(), force_caller: nil}, @checkup_time_ms}
|
||||
state = %{state | order: order()}
|
||||
|
||||
case state.force_callers do
|
||||
[caller | rest] ->
|
||||
GenServer.reply(caller, :ok)
|
||||
{:noreply, %{state | force_callers: rest}, 0}
|
||||
|
||||
[] ->
|
||||
{:noreply, state, @checkup_time_ms}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_info(:timeout, state) do
|
||||
|
@ -70,13 +78,19 @@ defmodule Farmbot.AssetMonitor do
|
|||
Enum.reduce(expected, sub_state, fn %{local_id: id, updated_at: updated_at} = asset,
|
||||
sub_state ->
|
||||
cond do
|
||||
asset.monitor == false ->
|
||||
# Logger.debug("#{inspect(kind)} #{id} should not be monitored")
|
||||
Map.put(sub_state, id, updated_at)
|
||||
|
||||
is_nil(sub_state[id]) ->
|
||||
Logger.debug("#{inspect(kind)} #{id} needs to be started")
|
||||
asset = Repo.preload(asset, Farmbot.AssetWorker.preload(asset))
|
||||
Farmbot.AssetSupervisor.start_child(asset)
|
||||
Map.put(sub_state, id, updated_at)
|
||||
|
||||
compare_datetimes(updated_at, sub_state[id]) == :gt ->
|
||||
Logger.warn("#{inspect(kind)} #{id} needs to be updated")
|
||||
asset = Repo.preload(asset, Farmbot.AssetWorker.preload(asset))
|
||||
Farmbot.AssetSupervisor.update_child(asset)
|
||||
Map.put(sub_state, id, updated_at)
|
||||
|
||||
|
@ -91,7 +105,7 @@ defmodule Farmbot.AssetMonitor do
|
|||
Device,
|
||||
FbosConfig,
|
||||
FarmEvent,
|
||||
# Peripheral,
|
||||
Peripheral,
|
||||
PersistentRegimen,
|
||||
PinBinding,
|
||||
FarmwareInstallation,
|
||||
|
|
|
@ -57,7 +57,7 @@ defmodule Farmbot.AssetSupervisor do
|
|||
end
|
||||
|
||||
@doc false
|
||||
def worker_spec(%{local_id: id} = asset) do
|
||||
def worker_spec(%{local_id: id, monitor: true} = asset) do
|
||||
%{
|
||||
id: id,
|
||||
start: {AssetWorker, :start_link, [asset]}
|
||||
|
@ -73,11 +73,11 @@ defmodule Farmbot.AssetSupervisor do
|
|||
@doc false
|
||||
def init(args) do
|
||||
module = Keyword.fetch!(args, :module)
|
||||
preload = Keyword.get(args, :preload, [])
|
||||
|
||||
module
|
||||
|> Repo.all()
|
||||
|> Enum.map(&Repo.preload(&1, preload))
|
||||
|> Enum.filter(fn %{monitor: mon} -> mon == false end)
|
||||
|> Enum.map(&Repo.preload(&1, AssetWorker.preload(&1)))
|
||||
|> Enum.map(&worker_spec/1)
|
||||
|> Supervisor.init(strategy: :one_for_one)
|
||||
end
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
defprotocol Farmbot.AssetWorker do
|
||||
@doc "List of relational resources that need to be preloaded."
|
||||
def preload(asset)
|
||||
|
||||
@doc "GenServer childspec callback."
|
||||
def start_link(asset)
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@ defimpl Farmbot.AssetWorker, for: Farmbot.Asset.Device do
|
|||
use GenServer
|
||||
import Farmbot.Config, only: [update_config_value: 4]
|
||||
|
||||
def preload(%Device{}), do: []
|
||||
|
||||
def start_link(%Device{} = device) do
|
||||
GenServer.start_link(__MODULE__, [%Device{} = device])
|
||||
end
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
defimpl Farmbot.AssetWorker, for: Farmbot.Asset.FarmEvent do
|
||||
use GenServer
|
||||
require Logger
|
||||
|
||||
alias Farmbot.{
|
||||
Asset,
|
||||
Asset.FarmEvent,
|
||||
|
@ -6,9 +9,6 @@ defimpl Farmbot.AssetWorker, for: Farmbot.Asset.FarmEvent do
|
|||
Asset.Sequence
|
||||
}
|
||||
|
||||
require Logger
|
||||
use GenServer
|
||||
|
||||
defstruct [:farm_event, :datetime]
|
||||
alias __MODULE__, as: State
|
||||
|
||||
|
@ -18,12 +18,19 @@ defimpl Farmbot.AssetWorker, for: Farmbot.Asset.FarmEvent do
|
|||
config :farmbot_core, #{__MODULE__}, checkup_time_ms: 10_000
|
||||
""")
|
||||
|
||||
def preload(%FarmEvent{}), do: []
|
||||
|
||||
def start_link(farm_event) do
|
||||
GenServer.start_link(__MODULE__, [farm_event])
|
||||
end
|
||||
|
||||
def force_checkup(pid) when is_pid(pid) do
|
||||
Logger.warn("Forcing timeout on #{inspect(pid)}")
|
||||
send(pid, :timeout)
|
||||
end
|
||||
|
||||
def init([farm_event]) do
|
||||
Logger.disable(self())
|
||||
# Logger.disable(self())
|
||||
ensure_executable!(farm_event)
|
||||
now = DateTime.utc_now()
|
||||
|
||||
|
@ -115,12 +122,12 @@ defimpl Farmbot.AssetWorker, for: Farmbot.Asset.FarmEvent do
|
|||
|
||||
defp ensure_executed!(%FarmEvent{last_executed: nil} = event, %Regimen{} = exe, next_dt) do
|
||||
Logger.warn("Regimen: #{inspect(exe)} has not run before. Executing it.")
|
||||
Asset.upsert_persistent_regimen(exe, event, %{started_at: next_dt})
|
||||
Asset.upsert_persistent_regimen!(exe, event, %{started_at: next_dt})
|
||||
Asset.update_farm_event!(event, %{last_executed: next_dt})
|
||||
end
|
||||
|
||||
defp ensure_executed!(%FarmEvent{} = event, %Regimen{} = exe, _next_dt) do
|
||||
Asset.upsert_persistent_regimen(exe, event)
|
||||
Asset.upsert_persistent_regimen!(exe, event)
|
||||
event
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
defimpl Farmbot.AssetWorker, for: Farmbot.Asset.FarmwareEnv do
|
||||
alias Farmbot.Asset.FarmwareEnv
|
||||
use GenServer
|
||||
|
||||
alias Farmbot.Asset.FarmwareEnv
|
||||
|
||||
def preload(%FarmwareEnv{}), do: []
|
||||
|
||||
def start_link(%FarmwareEnv{} = env) do
|
||||
GenServer.start_link(__MODULE__, env)
|
||||
end
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
defimpl Farmbot.AssetWorker, for: Farmbot.Asset.FarmwareInstallation do
|
||||
use GenServer
|
||||
require Farmbot.Logger
|
||||
|
||||
alias Farmbot.Asset.Repo
|
||||
alias Farmbot.Asset.FarmwareInstallation, as: FWI
|
||||
require Farmbot.Logger
|
||||
|
||||
config = Application.get_env(:farmbot_core, __MODULE__)
|
||||
@install_dir config[:install_dir] || Mix.raise("Missing Install Dir")
|
||||
@error_retry_time_ms config[:error_retry_time_ms] || 30_000
|
||||
@manifest_name "manifest.json"
|
||||
|
||||
def preload(%FWI{}), do: []
|
||||
|
||||
def start_link(fwi) do
|
||||
GenServer.start_link(__MODULE__, [fwi])
|
||||
end
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
defimpl Farmbot.AssetWorker, for: Farmbot.Asset.FbosConfig do
|
||||
use GenServer
|
||||
require Logger
|
||||
|
||||
alias Farmbot.Asset.FbosConfig
|
||||
import Farmbot.Config, only: [update_config_value: 4]
|
||||
|
||||
def preload(%FbosConfig{}), do: []
|
||||
|
||||
def start_link(%FbosConfig{} = fbos_config) do
|
||||
GenServer.start_link(__MODULE__, [%FbosConfig{} = fbos_config])
|
||||
end
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
defimpl Farmbot.AssetWorker, for: Farmbot.Asset.Peripheral do
|
||||
use GenServer
|
||||
require Farmbot.Logger
|
||||
|
||||
alias Farmbot.Asset.Peripheral
|
||||
alias Farmbot.Core.CeleryScript
|
||||
import Farmbot.CeleryScript.Utils
|
||||
require Farmbot.Logger
|
||||
@retry_ms 5_000
|
||||
|
||||
def preload(%Peripheral{}), do: []
|
||||
|
||||
def start_link(peripheral) do
|
||||
GenServer.start_link(__MODULE__, [peripheral])
|
||||
end
|
||||
|
|
|
@ -1,13 +1,90 @@
|
|||
defimpl Farmbot.AssetWorker, for: Farmbot.Asset.PersistentRegimen do
|
||||
use GenServer
|
||||
require Logger
|
||||
require Farmbot.Logger
|
||||
import Farmbot.Config, only: [get_config_value: 3]
|
||||
|
||||
alias Farmbot.Asset
|
||||
alias Farmbot.Asset.{PersistentRegimen, FarmEvent, Regimen}
|
||||
alias Farmbot.Core.CeleryScript
|
||||
|
||||
@checkup_time_ms Application.get_env(:farmbot_core, __MODULE__)[:checkup_time_ms]
|
||||
@checkup_time_ms ||
|
||||
Mix.raise("""
|
||||
config :farmbot_core, #{__MODULE__}, checkup_time_ms: 10_000
|
||||
""")
|
||||
|
||||
def preload(%PersistentRegimen{}), do: [:farm_event, :regimen]
|
||||
|
||||
def start_link(persistent_regimen) do
|
||||
GenServer.start_link(__MODULE__, [persistent_regimen])
|
||||
end
|
||||
|
||||
def init([persistent_regimen]) do
|
||||
{:ok, persistent_regimen}
|
||||
with %Regimen{} <- persistent_regimen.regimen,
|
||||
%FarmEvent{} <- persistent_regimen.farm_event do
|
||||
{:ok, filter_items(persistent_regimen), 0}
|
||||
else
|
||||
_ -> {:stop, "Persistent Regimen not preloaded."}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_info(:timeout, %PersistentRegimen{next: nil} = pr) do
|
||||
persistent_regimen = filter_items(pr)
|
||||
calculate_next(persistent_regimen, 0)
|
||||
end
|
||||
|
||||
def handle_info(:timeout, %PersistentRegimen{} = pr) do
|
||||
# Check if pr.next is around 2 minutes in the past
|
||||
# positive if the first date/time comes after the second.
|
||||
comp = Timex.diff(DateTime.utc_now(), pr.next, :minutes)
|
||||
|
||||
cond do
|
||||
# now is more than 2 minutes past expected execution time
|
||||
comp > 2 ->
|
||||
Logger.warn(
|
||||
"Regimen: #{pr.regimen.name || "regimen"} too late: #{comp} minutes difference."
|
||||
)
|
||||
|
||||
calculate_next(pr)
|
||||
|
||||
true ->
|
||||
Logger.warn(
|
||||
"Regimen: #{pr.regimen.name || "regimen"} has not run before: #{comp} minutes difference."
|
||||
)
|
||||
|
||||
exe = Asset.get_sequence!(id: pr.next_sequence_id)
|
||||
CeleryScript.sequence(exe, fn _ -> :ok end)
|
||||
calculate_next(pr)
|
||||
end
|
||||
end
|
||||
|
||||
defp calculate_next(pr, checkup_time_ms \\ @checkup_time_ms)
|
||||
|
||||
defp calculate_next(%{regimen: %{regimen_items: [next | rest]} = reg} = pr, checkup_time_ms) do
|
||||
next_dt = Timex.shift(pr.epoch, milliseconds: next.time_offset)
|
||||
params = %{next: next_dt, next_sequence_id: next.sequence_id}
|
||||
# TODO(Connor) - This causes the active GenServer to be
|
||||
# Restarted due to the `AssetMonitor`
|
||||
pr = Asset.update_persistent_regimen!(pr, params)
|
||||
|
||||
pr = %{
|
||||
pr
|
||||
| regimen: %{reg | regimen_items: rest}
|
||||
}
|
||||
|
||||
{:noreply, pr, checkup_time_ms}
|
||||
end
|
||||
|
||||
defp calculate_next(%{regimen: %{regimen_items: []}} = pr, _) do
|
||||
Farmbot.Logger.success(1, "#{pr.regimen.name || "regimen"} has no more items.")
|
||||
{:noreply, pr, :hibernate}
|
||||
end
|
||||
|
||||
defp filter_items(%PersistentRegimen{regimen: %Regimen{} = reg} = pr) do
|
||||
items =
|
||||
reg.regimen_items
|
||||
|> Enum.sort(&(&1.time_offset <= &2.time_offset))
|
||||
|
||||
%{pr | regimen: %{reg | regimen_items: items}}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
defimpl Farmbot.AssetWorker, for: Farmbot.Asset.PinBinding do
|
||||
@gpio_handler Application.get_env(:farmbot_core, __MODULE__)[:gpio_handler]
|
||||
@error_retry_time_ms Application.get_env(:farmbot_core, __MODULE__)[:error_retry_time_ms]
|
||||
|
||||
use GenServer
|
||||
require Logger
|
||||
require Farmbot.Logger
|
||||
|
||||
import Farmbot.CeleryScript.Utils
|
||||
|
||||
alias Farmbot.{
|
||||
|
@ -13,6 +12,9 @@ defimpl Farmbot.AssetWorker, for: Farmbot.Asset.PinBinding do
|
|||
Asset
|
||||
}
|
||||
|
||||
@gpio_handler Application.get_env(:farmbot_core, __MODULE__)[:gpio_handler]
|
||||
@error_retry_time_ms Application.get_env(:farmbot_core, __MODULE__)[:error_retry_time_ms]
|
||||
|
||||
@gpio_handler ||
|
||||
Mix.raise("""
|
||||
config :farmbot_core, #{__MODULE__}, gpio_handler: MyModule
|
||||
|
@ -36,7 +38,7 @@ defimpl Farmbot.AssetWorker, for: Farmbot.Asset.PinBinding do
|
|||
"""
|
||||
@callback start_link(pin_number, trigger_fun) :: GenServer.on_start()
|
||||
|
||||
use GenServer
|
||||
def preload(%PinBinding{}), do: []
|
||||
|
||||
def start_link(%PinBinding{} = pin_binding) do
|
||||
GenServer.start_link(__MODULE__, [%PinBinding{} = pin_binding])
|
||||
|
|
|
@ -8,7 +8,7 @@ defmodule Farmbot.BotState.JobProgress do
|
|||
|
||||
defmodule Percent do
|
||||
@moduledoc "Percent job."
|
||||
defstruct status: :working, percent: 0, unit: :percent, type: :ota, time: nil
|
||||
defstruct status: :working, percent: 0, unit: :percent, type: :ota, time: nil, file_type: nil
|
||||
|
||||
defimpl Inspect, for: __MODULE__ do
|
||||
def inspect(%{percent: percent}, _) do
|
||||
|
@ -28,7 +28,7 @@ defmodule Farmbot.BotState.JobProgress do
|
|||
|
||||
defmodule Bytes do
|
||||
@moduledoc "Bytes job."
|
||||
defstruct status: :working, bytes: 0, unit: :bytes, type: :ota, time: nil
|
||||
defstruct status: :working, bytes: 0, unit: :bytes, type: :ota, time: nil, file_type: nil
|
||||
|
||||
defimpl Inspect, for: __MODULE__ do
|
||||
def inspect(%{bytes: bytes}, _) do
|
||||
|
|
|
@ -96,6 +96,10 @@ defmodule FarmbotCore.MixProject do
|
|||
["lib", "../test/support"]
|
||||
end
|
||||
|
||||
defp elixirc_paths(:dev) do
|
||||
["lib", "../test/support"]
|
||||
end
|
||||
|
||||
defp elixirc_paths(_), do: ["lib"]
|
||||
|
||||
defp aliases,
|
||||
|
|
|
@ -7,6 +7,7 @@ defmodule Farmbot.Asset.Repo.Migrations.CreateDevicesTable do
|
|||
add(:id, :id)
|
||||
add(:name, :string)
|
||||
add(:timezone, :string)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ defmodule Farmbot.Asset.Repo.Migrations.CreateToolsTable do
|
|||
add(:local_id, :binary_id, primary_key: true)
|
||||
add(:id, :id)
|
||||
add(:name, :string)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,7 @@ defmodule Farmbot.Asset.Repo.Migrations.CreatePeripheralsTable do
|
|||
add(:pin, :integer)
|
||||
add(:mode, :integer)
|
||||
add(:label, :string)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,7 @@ defmodule Farmbot.Asset.Repo.Migrations.CreateSensorsTable do
|
|||
add(:pin, :integer)
|
||||
add(:mode, :integer)
|
||||
add(:label, :string)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,6 +11,7 @@ defmodule Farmbot.Asset.Repo.Migrations.CreateSensorReadingsTable do
|
|||
add(:x, :float)
|
||||
add(:y, :float)
|
||||
add(:z, :float)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,6 +9,7 @@ defmodule Farmbot.Asset.Repo.Migrations.CreateSequencesTable do
|
|||
add(:kind, :string)
|
||||
add(:args, :map)
|
||||
add(:body, {:array, :map})
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,6 +7,7 @@ defmodule Farmbot.Asset.Repo.Migrations.CreateRegimensTable do
|
|||
add(:id, :id)
|
||||
add(:regimen_items, {:array, :map})
|
||||
add(:name, :string)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,7 @@ defmodule Farmbot.Asset.Repo.Migrations.CreatePinBindingsTable do
|
|||
add(:pin_num, :integer)
|
||||
add(:sequence_id, :integer)
|
||||
add(:special_action, :string)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,6 +14,7 @@ defmodule Farmbot.Asset.Repo.Migrations.CreatePointsTable do
|
|||
add(:x, :float)
|
||||
add(:y, :float)
|
||||
add(:z, :float)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,6 +12,7 @@ defmodule Farmbot.Asset.Repo.Migrations.CreateFarmEventsTable do
|
|||
add(:start_time, :utc_datetime)
|
||||
add(:time_unit, :string)
|
||||
add(:last_executed, :utc_datetime)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -93,6 +93,7 @@ defmodule Elixir.Farmbot.Asset.Repo.Migrations.CreateFirmwareConfigsTable do
|
|||
add(:encoder_invert_x, :float)
|
||||
add(:encoder_missed_steps_max_x, :float)
|
||||
add(:movement_invert_motor_y, :float)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,6 +19,7 @@ defmodule Elixir.Farmbot.Asset.Repo.Migrations.CreateFbosConfigsTable do
|
|||
add(:sequence_body_log, :boolean)
|
||||
add(:sequence_complete_log, :boolean)
|
||||
add(:sequence_init_log, :boolean)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,6 +7,7 @@ defmodule Elixir.Farmbot.Asset.Repo.Migrations.CreateFarmwareInstallationsTable
|
|||
add(:id, :id)
|
||||
add(:url, :string)
|
||||
add(:manifest, :map)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,6 +7,7 @@ defmodule Elixir.Farmbot.Asset.Repo.Migrations.CreateFarmwareEnvsTable do
|
|||
add(:id, :id)
|
||||
add(:key, :string)
|
||||
add(:value, :string)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,6 +12,7 @@ defmodule Elixir.Farmbot.Asset.Repo.Migrations.CreateDiagnosticDumpsTable do
|
|||
add(:firmware_state, :string)
|
||||
add(:network_interface, :string)
|
||||
add(:fbos_dmesg_dump, :string)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,6 +20,7 @@ defmodule Elixir.Farmbot.Asset.Repo.Migrations.CreateSyncsTable do
|
|||
add(:sequences, {:array, :map})
|
||||
add(:tools, {:array, :map})
|
||||
add(:now, :utc_datetime)
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,12 +5,17 @@ defmodule Farmbot.Asset.Repo.Migrations.CreatePersistentRegimensTable do
|
|||
create table("persistent_regimens", primary_key: false) do
|
||||
add(:local_id, :binary_id, primary_key: true)
|
||||
add(:started_at, :utc_datetime)
|
||||
add(:epoch, :utc_datetime)
|
||||
add(:next, :utc_datetime)
|
||||
add(:next_sequence_id, :id)
|
||||
add(:regimen_id, references("regimens", type: :binary_id, column: :local_id))
|
||||
add(:farm_event_id, references("farm_events", type: :binary_id, column: :local_id))
|
||||
add(:monitor, :boolean, default: true)
|
||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||
end
|
||||
|
||||
create(unique_index("persistent_regimens", [:local_id, :regimen_id, :farm_event_id]))
|
||||
create(unique_index("persistent_regimens", :started_at))
|
||||
create(unique_index("persistent_regimens", :epoch))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
defmodule Farmbot.AssetMonitorTest do
|
||||
use ExUnit.Case
|
||||
use ExUnit.Case, async: false
|
||||
alias Farmbot.Asset
|
||||
alias Farmbot.AssetSupervisor
|
||||
import Farmbot.TestSupport.AssetFixtures
|
||||
|
@ -10,7 +10,8 @@ defmodule Farmbot.AssetMonitorTest do
|
|||
seq = sequence()
|
||||
reg = regimen(%{regimen_items: [%{time_offset: 100, sequence_id: seq.id}]})
|
||||
event = regimen_event(reg)
|
||||
{:ok, pr} = Asset.upsert_persistent_regimen(reg, event)
|
||||
pr = Asset.upsert_persistent_regimen!(reg, event)
|
||||
pr = Asset.update_persistent_regimen!(pr, %{monitor: true})
|
||||
|
||||
Farmbot.AssetMonitor.force_checkup()
|
||||
|
||||
|
@ -34,6 +35,7 @@ defmodule Farmbot.AssetMonitorTest do
|
|||
end_time = Timex.shift(now, minutes: 10)
|
||||
|
||||
params = %{
|
||||
monitor: true,
|
||||
start_time: start_time,
|
||||
end_time: end_time,
|
||||
repeat: 1,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
defmodule Farmbot.AssetTest do
|
||||
use ExUnit.Case
|
||||
use ExUnit.Case, async: true
|
||||
alias Farmbot.Asset.{Repo, Regimen, PersistentRegimen}
|
||||
alias Farmbot.Asset
|
||||
import Farmbot.TestSupport.AssetFixtures
|
||||
|
@ -9,17 +9,17 @@ defmodule Farmbot.AssetTest do
|
|||
seq = sequence()
|
||||
reg = regimen(%{regimen_items: [%{time_offset: 100, sequence_id: seq.id}]})
|
||||
event = regimen_event(reg)
|
||||
assert {:ok, %PersistentRegimen{}} = Asset.upsert_persistent_regimen(reg, event)
|
||||
assert %PersistentRegimen{} = Asset.upsert_persistent_regimen!(reg, event)
|
||||
end
|
||||
|
||||
test "updates a persisten regimen" do
|
||||
seq = sequence()
|
||||
reg = regimen(%{name: "old", regimen_items: [%{time_offset: 100, sequence_id: seq.id}]})
|
||||
event = regimen_event(reg)
|
||||
{:ok, pr} = Asset.upsert_persistent_regimen(reg, event)
|
||||
pr = Asset.upsert_persistent_regimen!(reg, event)
|
||||
assert pr.regimen.name == "old"
|
||||
reg = Regimen.changeset(reg, %{name: "new"}) |> Repo.update!()
|
||||
{:ok, pr} = Asset.upsert_persistent_regimen(reg, event)
|
||||
pr = Asset.upsert_persistent_regimen!(reg, event)
|
||||
assert pr.regimen.name == "new"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,31 +1,12 @@
|
|||
defmodule Farmbot.FarmEventWorkerTest do
|
||||
use ExUnit.Case
|
||||
use ExUnit.Case, async: true
|
||||
alias Farmbot.Asset.FarmEvent
|
||||
|
||||
import Farmbot.TestSupport.AssetFixtures
|
||||
alias Farmbot.TestSupport.CeleryScript.TestIOLayer
|
||||
import Farmbot.TestSupport
|
||||
|
||||
describe "regimens" do
|
||||
test "always ensure a regimen is started" do
|
||||
seq = sequence()
|
||||
reg = regimen(%{regimen_items: [%{time_offset: 100, sequence_id: seq.id}]})
|
||||
|
||||
now = DateTime.utc_now()
|
||||
start_time = Timex.shift(now, minutes: -20)
|
||||
end_time = Timex.shift(now, minutes: 10)
|
||||
|
||||
params = %{
|
||||
start_time: start_time,
|
||||
end_time: end_time,
|
||||
repeat: 1,
|
||||
time_unit: "never"
|
||||
}
|
||||
|
||||
_event = regimen_event(reg, params)
|
||||
refute :lookup_persistent_regimen_after_sleep?
|
||||
end
|
||||
end
|
||||
# Regimen tests are in the PersistentRegimeWorker test
|
||||
|
||||
describe "sequences" do
|
||||
# TODO(Connor) - this test isn't really that good
|
||||
|
@ -45,9 +26,12 @@ defmodule Farmbot.FarmEventWorkerTest do
|
|||
time_unit: "never"
|
||||
}
|
||||
|
||||
assert %FarmEvent{} = sequence_event(seq, params)
|
||||
assert %FarmEvent{} = fe = sequence_event(seq, params)
|
||||
{:ok, pid} = Farmbot.AssetWorker.start_link(fe)
|
||||
Farmbot.AssetWorker.Farmbot.Asset.FarmEvent.force_checkup(pid)
|
||||
|
||||
# This is not really that useful.
|
||||
refute_receive ^ast, farm_event_timeout()
|
||||
refute_receive ^ast, farm_event_timeout() + 5000
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -67,8 +51,10 @@ defmodule Farmbot.FarmEventWorkerTest do
|
|||
time_unit: "minutely"
|
||||
}
|
||||
|
||||
assert %FarmEvent{} = sequence_event(seq, params)
|
||||
assert_receive ^ast, farm_event_timeout()
|
||||
assert %FarmEvent{} = fe = sequence_event(seq, params)
|
||||
{:ok, pid} = Farmbot.AssetWorker.start_link(fe)
|
||||
Farmbot.AssetWorker.Farmbot.Asset.FarmEvent.force_checkup(pid)
|
||||
assert_receive ^ast, farm_event_timeout() + 5000
|
||||
end
|
||||
|
||||
test "wont start an event after end_time" do
|
||||
|
@ -86,7 +72,9 @@ defmodule Farmbot.FarmEventWorkerTest do
|
|||
time_unit: "minutely"
|
||||
}
|
||||
|
||||
assert %FarmEvent{} = sequence_event(seq, params)
|
||||
assert %FarmEvent{} = fe = sequence_event(seq, params)
|
||||
{:ok, pid} = Farmbot.AssetWorker.start_link(fe)
|
||||
Farmbot.AssetWorker.Farmbot.Asset.FarmEvent.force_checkup(pid)
|
||||
# This is not really that useful.
|
||||
refute_receive ^ast
|
||||
end
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
defmodule Farmbot.FbosConfigWorkerTest do
|
||||
use ExUnit.Case
|
||||
use ExUnit.Case, async: true
|
||||
alias Farmbot.Asset.FbosConfig
|
||||
|
||||
import Farmbot.TestSupport.AssetFixtures
|
||||
|
||||
test "adds configs to bot state and config_storage" do
|
||||
conf =
|
||||
FbosConfig.changeset(%FbosConfig{}, %{
|
||||
%FbosConfig{} =
|
||||
conf =
|
||||
fbos_config(%{
|
||||
monitor: true,
|
||||
arduino_debug_messages: true,
|
||||
auto_sync: false,
|
||||
beta_opt_in: true,
|
||||
|
@ -12,19 +16,18 @@ defmodule Farmbot.FbosConfigWorkerTest do
|
|||
firmware_hardware: "farmduino_k14",
|
||||
firmware_input_log: false,
|
||||
firmware_output_log: false,
|
||||
id: 145,
|
||||
network_not_found_timer: nil,
|
||||
os_auto_update: false,
|
||||
sequence_body_log: true,
|
||||
sequence_complete_log: true,
|
||||
sequence_init_log: true
|
||||
})
|
||||
|> Farmbot.Asset.Repo.insert!()
|
||||
|
||||
:ok = Farmbot.AssetMonitor.force_checkup()
|
||||
{:ok, pid} = Farmbot.AssetWorker.start_link(conf)
|
||||
send(pid, :timeout)
|
||||
|
||||
# Wait for the timeout to be dispatched
|
||||
Process.sleep(100)
|
||||
Process.sleep(200)
|
||||
|
||||
state_conf = Farmbot.BotState.fetch().configuration
|
||||
assert state_conf.arduino_debug_messages == conf.arduino_debug_messages
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
defmodule Farmbot.PersistentRegimenWorkerTest do
|
||||
use ExUnit.Case, async: true
|
||||
alias Farmbot.{Asset.FarmEvent, Asset.PersistentRegimen}
|
||||
import Farmbot.TestSupport.AssetFixtures
|
||||
alias Farmbot.TestSupport.CeleryScript.TestIOLayer
|
||||
import Farmbot.TestSupport
|
||||
|
||||
test "regimen executes a sequence" do
|
||||
now = DateTime.utc_now()
|
||||
start_time = Timex.shift(now, minutes: -20)
|
||||
end_time = Timex.shift(now, minutes: 10)
|
||||
{:ok, epoch} = PersistentRegimen.build_epoch(now)
|
||||
offset = Timex.diff(now, epoch, :milliseconds) + 500
|
||||
|
||||
TestIOLayer.subscribe()
|
||||
ast = TestIOLayer.debug_ast()
|
||||
seq = sequence(%{body: [ast]})
|
||||
|
||||
reg = regimen(%{regimen_items: [%{time_offset: offset, sequence_id: seq.id}]})
|
||||
|
||||
params = %{
|
||||
start_time: start_time,
|
||||
end_time: end_time,
|
||||
repeat: 1,
|
||||
time_unit: "never"
|
||||
}
|
||||
|
||||
assert %FarmEvent{} = fe = regimen_event(reg, params)
|
||||
|
||||
{:ok, pid} = Farmbot.AssetWorker.start_link(fe)
|
||||
Farmbot.AssetWorker.Farmbot.Asset.FarmEvent.force_checkup(pid)
|
||||
assert_receive ^ast, farm_event_timeout() + persistent_regimen_timeout() + 5000
|
||||
end
|
||||
end
|
|
@ -4,4 +4,9 @@
|
|||
# Ecto.Adapters.SQL.Sandbox.mode(Farmbot.Config.Repo, :auto)
|
||||
# Ecto.Adapters.SQL.Sandbox.mode(Farmbot.Logger.Repo, {:shared, self()})
|
||||
# Ecto.Adapters.SQL.Sandbox.mode(Farmbot.Asset.Repo, :auto)
|
||||
tz = Timex.local().time_zone
|
||||
|
||||
Farmbot.Asset.Device.changeset(Farmbot.Asset.device(), %{timezone: tz})
|
||||
|> Farmbot.Asset.Repo.insert_or_update!()
|
||||
|
||||
ExUnit.start()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use Mix.Config
|
||||
config :farmbot_core, Farmbot.AssetWorker.Farmbot.Asset.FarmEvent, checkup_time_ms: 10_000
|
||||
config :farmbot_core, Farmbot.AssetWorker.Farmbot.Asset.PersistentRegimen, checkup_time_ms: 10_000
|
||||
|
||||
config :farmbot_core, Elixir.Farmbot.AssetWorker.Farmbot.Asset.PinBinding,
|
||||
gpio_handler: Farmbot.PinBindingWorker.StubGPIOHandler,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use Mix.Config
|
||||
|
||||
config :farmbot_core, Farmbot.AssetWorker.Farmbot.Asset.FarmEvent, checkup_time_ms: 10_000
|
||||
config :farmbot_core, Farmbot.AssetWorker.Farmbot.Asset.PersistentRegimen, checkup_time_ms: 10_000
|
||||
|
||||
config :farmbot_core, Farmbot.AssetWorker.Farmbot.Asset.FarmwareInstallation,
|
||||
error_retry_time_ms: 30_000,
|
||||
|
|
|
@ -1,19 +1,39 @@
|
|||
defmodule Farmbot.TestSupport.AssetFixtures do
|
||||
alias Farmbot.Asset.{Repo, Sequence, Regimen, FarmEvent}
|
||||
alias Farmbot.Asset.{Repo, FarmEvent, FbosConfig, Regimen, Sequence}
|
||||
|
||||
def fbos_config(params \\ %{}) do
|
||||
default = %{
|
||||
id: :rand.uniform(10000),
|
||||
monitor: false
|
||||
}
|
||||
|
||||
FbosConfig
|
||||
|> struct()
|
||||
|> FbosConfig.changeset(Map.merge(default, params))
|
||||
|> Repo.insert!()
|
||||
end
|
||||
|
||||
def sequence(params \\ %{}) do
|
||||
default = %{
|
||||
id: :rand.uniform(10000),
|
||||
monitor: false,
|
||||
kind: "sequence",
|
||||
args: %{},
|
||||
body: []
|
||||
}
|
||||
|
||||
Sequence
|
||||
|> struct()
|
||||
|> Sequence.changeset(
|
||||
Map.merge(%{id: :rand.uniform(10000), kind: "sequence", args: %{}, body: []}, params)
|
||||
)
|
||||
|> Sequence.changeset(Map.merge(default, params))
|
||||
|> Repo.insert!()
|
||||
end
|
||||
|
||||
def regimen(params \\ %{}) do
|
||||
default = %{id: :rand.uniform(10000), monitor: false, regimen_items: []}
|
||||
|
||||
Regimen
|
||||
|> struct()
|
||||
|> Regimen.changeset(Map.merge(%{id: :rand.uniform(10000), regimen_items: []}, params))
|
||||
|> Regimen.changeset(Map.merge(default, params))
|
||||
|> Repo.insert!()
|
||||
end
|
||||
|
||||
|
@ -24,6 +44,7 @@ defmodule Farmbot.TestSupport.AssetFixtures do
|
|||
Map.merge(
|
||||
%{
|
||||
id: :rand.uniform(1_000_000),
|
||||
monitor: false,
|
||||
executable_type: "Regimen",
|
||||
executable_id: regimen.id,
|
||||
start_time: now,
|
||||
|
@ -47,6 +68,7 @@ defmodule Farmbot.TestSupport.AssetFixtures do
|
|||
Map.merge(
|
||||
%{
|
||||
id: :rand.uniform(1_000_000),
|
||||
monitor: false,
|
||||
executable_type: "Sequence",
|
||||
executable_id: sequence.id,
|
||||
start_time: now,
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
defmodule Farmbot.TestSupport do
|
||||
alias Farmbot.AssetWorker.Farmbot.Asset.{FarmEvent, PersistentRegimen}
|
||||
def farm_event_timeout do
|
||||
Application.get_env(:farmbot_core, Farmbot.AssetWorker.Farmbot.Asset.FarmEvent)[
|
||||
Application.get_env(:farmbot_core, FarmEvent)[
|
||||
:checkup_time_ms
|
||||
] + asset_monitor_timeout()
|
||||
]
|
||||
end
|
||||
|
||||
def persistent_regimen_timeout do
|
||||
Application.get_env(:farmbot_core, PersistentRegimen)[
|
||||
:checkup_time_ms
|
||||
]
|
||||
end
|
||||
|
||||
def asset_monitor_timeout do
|
||||
Application.get_env(:farmbot_core, Farmbot.AssetMonitor)[:checkup_time_ms] + grace()
|
||||
end
|
||||
|
||||
def grace do
|
||||
5000
|
||||
Application.get_env(:farmbot_core, Farmbot.AssetMonitor)[:checkup_time_ms]
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue