From 0fe5dfac45bab7a67ee2a7a3699f8cafddf5d3c1 Mon Sep 17 00:00:00 2001 From: Adrian Kumpf Date: Sun, 29 Sep 2019 12:16:59 +0200 Subject: [PATCH] Restart Vehicles supervisor after potential eid change --- lib/teslamate/vehicles.ex | 14 +++++++++++-- lib/teslamate/vehicles/vehicle/vehicle.ex | 25 +++++++++++++++++++++-- test/support/mocks/vehicles.ex | 6 ++++++ test/support/vehicle_case.ex | 3 +++ test/teslamate/vehicles/vehicle_test.exs | 13 ++++++++++++ 5 files changed, 57 insertions(+), 4 deletions(-) diff --git a/lib/teslamate/vehicles.ex b/lib/teslamate/vehicles.ex index 28700f0c..6ea3af3a 100644 --- a/lib/teslamate/vehicles.ex +++ b/lib/teslamate/vehicles.ex @@ -32,6 +32,11 @@ defmodule TeslaMate.Vehicles do ) end + def kill do + Logger.warn("Restarting #{__MODULE__} supervisor") + __MODULE__ |> Process.whereis() |> Process.exit(:kill) + end + def restart do with :ok <- Supervisor.stop(@name, :normal), :ok <- block_until_started(250) do @@ -61,8 +66,13 @@ defmodule TeslaMate.Vehicles do end defp fallback_vehicles do - Log.list_cars() - |> Enum.map(&%TeslaApi.Vehicle{id: &1.eid, vin: &1.vin, vehicle_id: &1.vid}) + vehicles = + Log.list_cars() + |> Enum.map(&%TeslaApi.Vehicle{id: &1.eid, vin: &1.vin, vehicle_id: &1.vid}) + + Logger.warn("Using fallback vehicles:\n\n#{inspect(vehicles, pretty: true)}") + + vehicles end defp create_or_update!(%TeslaApi.Vehicle{} = vehicle) do diff --git a/lib/teslamate/vehicles/vehicle/vehicle.ex b/lib/teslamate/vehicles/vehicle/vehicle.ex index 768fd9a3..d51e0688 100644 --- a/lib/teslamate/vehicles/vehicle/vehicle.ex +++ b/lib/teslamate/vehicles/vehicle/vehicle.ex @@ -4,7 +4,7 @@ defmodule TeslaMate.Vehicles.Vehicle do require Logger alias __MODULE__.Summary - alias TeslaMate.{Api, Log, Settings, Convert} + alias TeslaMate.{Vehicles, Api, Log, Settings, Convert} alias TeslaMate.Vehicles.Identification alias TeslaApi.Vehicle.State.{Climate, VehicleState, Drive, Charge} @@ -61,6 +61,7 @@ defmodule TeslaMate.Vehicles.Vehicle do log: Keyword.get(opts, :deps_log, Log), api: Keyword.get(opts, :deps_api, Api), settings: Keyword.get(opts, :deps_settings, Settings), + vehicles: Keyword.get(opts, :deps_vehicles, Vehicles), pubsub: Keyword.get(opts, :deps_pubsub, Phoenix.PubSub) } @@ -81,6 +82,14 @@ defmodule TeslaMate.Vehicles.Vehicle do :ok = call(deps.settings, :subscribe_to_changes) + fuse_name = fuse_name(:vehicle_not_found, data) + fuse_opts = {{:standard, 8, :timer.minutes(10)}, {:reset, :timer.minutes(5)}} + + case :fuse.install(fuse_name, fuse_opts) do + :reset -> Logger.info("Reset fuse: #{inspect(fuse_name)}") + :ok -> :ok + end + {:ok, :start, data, {:next_event, :internal, :fetch}} end @@ -227,7 +236,15 @@ defmodule TeslaMate.Vehicles.Vehicle do {:error, :vehicle_not_found} -> Logger.error("Error / :vehicle_not_found", car_id: data.car.id) - {:keep_state_and_data, schedule_fetch(60)} + + fuse_name = fuse_name(:vehicle_not_found, data) + + case :fuse.ask(fuse_name, :sync) do + :blown -> true = call(data.deps.vehicles, :kill) + :ok -> :ok = :fuse.melt(fuse_name) + end + + {:keep_state_and_data, schedule_fetch(30)} {:error, reason} -> Logger.warn("Error / #{inspect(reason)}", car_id: data.car.id) @@ -801,6 +818,10 @@ defmodule TeslaMate.Vehicles.Vehicle do defp determince_interval(n) when not is_nil(n) and n > 0, do: round(725 / n) |> min(30) defp determince_interval(_), do: 15 + defp fuse_name(:vehicle_not_found, %Data{car: car}) do + :"#{__MODULE__}_#{car.id}_not_found" + end + defp notify_subscribers do {:next_event, :internal, :notify_subscribers} end diff --git a/test/support/mocks/vehicles.ex b/test/support/mocks/vehicles.ex index 0e7a0029..9a0711aa 100644 --- a/test/support/mocks/vehicles.ex +++ b/test/support/mocks/vehicles.ex @@ -10,6 +10,7 @@ defmodule VehiclesMock do GenServer.start_link(__MODULE__, opts, name: Keyword.fetch!(opts, :name)) end + def kill(name), do: GenServer.call(name, :kill) def restart(name), do: GenServer.call(name, :restart) def subscribe(name, car_id), do: GenServer.call(name, {:subscribe, car_id}) @@ -27,6 +28,11 @@ defmodule VehiclesMock do {:reply, :ok, state} end + def handle_call(:kill, _from, %State{pid: pid} = state) do + send(pid, {VehiclesMock, :kill}) + {:reply, true, state} + end + def handle_call(:restart, _from, %State{pid: pid} = state) do send(pid, {VehiclesMock, :restart}) {:reply, :ok, state} diff --git a/test/support/vehicle_case.ex b/test/support/vehicle_case.ex index 0e43669c..ac763d86 100644 --- a/test/support/vehicle_case.ex +++ b/test/support/vehicle_case.ex @@ -17,11 +17,13 @@ defmodule TeslaMate.VehicleCase do log_name = :"log_#{name}" api_name = :"api_#{name}" settings_name = :"settings_#{name}" + vehicles_name = :"vehicles_#{name}" pubsub_name = :"pubsub_#{name}" {:ok, _pid} = start_supervised({LogMock, name: log_name, pid: self()}) {:ok, _pid} = start_supervised({ApiMock, name: api_name, events: events, pid: self()}) {:ok, _pid} = start_supervised({SettingsMock, name: settings_name, pid: self()}) + {:ok, _pid} = start_supervised({VehiclesMock, name: vehicles_name, pid: self()}) {:ok, _pid} = start_supervised({PubSubMock, name: pubsub_name, pid: self()}) opts = @@ -52,6 +54,7 @@ defmodule TeslaMate.VehicleCase do deps_log: {LogMock, log_name}, deps_api: {ApiMock, api_name}, deps_settings: {SettingsMock, settings_name}, + deps_vehicles: {VehiclesMock, vehicles_name}, deps_pubsub: {PubSubMock, pubsub_name} )} ) diff --git a/test/teslamate/vehicles/vehicle_test.exs b/test/teslamate/vehicles/vehicle_test.exs index 94e7c45c..b75f7d09 100644 --- a/test/teslamate/vehicles/vehicle_test.exs +++ b/test/teslamate/vehicles/vehicle_test.exs @@ -211,4 +211,17 @@ defmodule TeslaMate.Vehicles.VehicleTest do refute_receive _ end end + + describe "error handling" do + @tag :capture_log + test "restarts if the eid changed", %{test: name} do + events = [ + {:error, :vehicle_not_found} + ] + + :ok = start_vehicle(name, events) + + assert_receive {VehiclesMock, :kill}, 2500 + end + end end