diff --git a/lib/elevator/communicator.ex b/lib/elevator/communicator.ex index b613850..c02c917 100644 --- a/lib/elevator/communicator.ex +++ b/lib/elevator/communicator.ex @@ -102,7 +102,7 @@ defmodule Elevator.Communicator do # Delete node from status map on disconnect @impl true def handle_info({:nodedown, node}, peer_status_map) do - {:noreply, %{peer_status_map | connected_nodes: Map.delete(peer_status_map, node)}} + {:noreply, Map.delete(peer_status_map, node)} end # Calls -------------------------------------------------- diff --git a/lib/elevator/fsm/state.ex b/lib/elevator/fsm/state.ex index 4fc5ecb..91b7278 100644 --- a/lib/elevator/fsm/state.ex +++ b/lib/elevator/fsm/state.ex @@ -10,7 +10,7 @@ defmodule Elevator.FSM.State do defstruct behavior: :moving, between_floors: true, direction: :down, - door_open_time_ms: Time.utc_now(), + door_open_time: Time.utc_now(), floor: :unknown, last_floor_time: Time.utc_now(), motor_timed_out: false, @@ -26,7 +26,7 @@ defmodule Elevator.FSM.State do between_floors: boolean(), obstructed: boolean(), motor_timed_out: boolean(), - door_open_time_ms: Time.t(), + door_open_time: Time.t(), last_floor_time: Time.t() } @@ -85,15 +85,16 @@ defmodule Elevator.FSM.State do # Casts -------------------------------------------------- @impl true - def handle_cast({:set_floor, floor}, state) do + def handle_cast({:set_floor, new_floor}, state) do new_state = - case floor do + case new_floor do :between_floors -> %{state | between_floors: true} - _ -> - %{state | between_floors: false, floor: floor, last_floor_time: Time.utc_now()} + floor -> + %{state | between_floors: false, floor: floor} end + |> detect_and_update_last_floor_time(state) {:noreply, new_state} end @@ -111,7 +112,11 @@ defmodule Elevator.FSM.State do @impl true def handle_cast({:set_behavior, behavior}, state) do - {:noreply, %{state | behavior: behavior}} + new_state = + %{state | behavior: behavior} + |> detect_and_update_last_floor_time(state) + + {:noreply, new_state} end @impl true @@ -120,7 +125,7 @@ defmodule Elevator.FSM.State do if state.between_floors do state else - %{state | behavior: :door_open, door_open_time_ms: Time.utc_now()} + %{state | behavior: :door_open, door_open_time: Time.utc_now()} end {:noreply, new_state} @@ -143,4 +148,14 @@ defmodule Elevator.FSM.State do def handle_call(:operational?, _from, state) do {:reply, not (state.motor_timed_out or state.obstructed), state} end + + @spec detect_and_update_last_floor_time(t(), t()) :: t() + defp detect_and_update_last_floor_time(new_state, old_state) do + if old_state.between_floors != new_state.between_floors or + (old_state.behavior != :moving and new_state.behavior == :moving) do + %{new_state | last_floor_time: Time.utc_now()} + else + new_state + end + end end diff --git a/lib/elevator/fsm/transition.ex b/lib/elevator/fsm/transition.ex index 29dc200..bfc7411 100644 --- a/lib/elevator/fsm/transition.ex +++ b/lib/elevator/fsm/transition.ex @@ -149,13 +149,16 @@ defmodule Elevator.FSM.Transition do defp decide_and_update_state(_state, _orders), do: :ok defp check_motor_timeout(state) do - timed_out = Time.diff(Time.utc_now(), state.last_floor_time, :millisecond) > @motor_timeout_ms + timed_out = + state.behavior == :moving and + Time.diff(Time.utc_now(), state.last_floor_time, :millisecond) > @motor_timeout_ms + State.set_motor_timed_out(timed_out) end defp check_door_timer(state) when state.behavior == :door_open do timed_out = - Time.diff(Time.utc_now(), state.door_open_time_ms, :millisecond) > + Time.diff(Time.utc_now(), state.door_open_time, :millisecond) > Elevator.door_open_duration_ms() cond do diff --git a/lib/elevator/hall_orders.ex b/lib/elevator/hall_orders.ex index 21637c3..78f0400 100644 --- a/lib/elevator/hall_orders.ex +++ b/lib/elevator/hall_orders.ex @@ -106,11 +106,11 @@ defmodule Elevator.HallOrders do @impl true def handle_call(:get_handling_orders, _from, order_map) do - confirmed_orders = + handling_orders = Enum.filter(order_map, &match?({_, {:handling, _}}, &1)) |> orders_by_floor() - {:reply, confirmed_orders, order_map} + {:reply, handling_orders, order_map} end @impl true @@ -184,7 +184,7 @@ defmodule Elevator.HallOrders do # Return the orders where we have the lowest cost among serving nodes. # Only consider orders where all serving nodes have a cost. - @spec my_orders_from_order_map(hall_order_map()) :: %{floor() => MapSet.t(hall_button())} + @spec my_orders_from_order_map(hall_order_map()) :: %{floor() => MapSet.t(hall_button_type())} defp my_orders_from_order_map(order_map) do who_can_serve = Communicator.who_can_serve() diff --git a/lib/elevator/hall_orders/cost.ex b/lib/elevator/hall_orders/cost.ex index 4ef4722..9367fd6 100644 --- a/lib/elevator/hall_orders/cost.ex +++ b/lib/elevator/hall_orders/cost.ex @@ -16,7 +16,7 @@ defmodule Elevator.HallOrders.Cost do @type cost_map :: Elevator.HallOrders.hall_order_cost_map() @doc """ - Comupte the cost (time to serve) of a candidate hall order by simulating single elevator logic. + Compute the cost (time to serve) of a candidate hall order by simulating single elevator logic. """ @spec compute_cost({floor(), hall_button_type()}, %{floor() => MapSet.t(hall_button_type())}) :: non_neg_integer() @@ -53,7 +53,7 @@ defmodule Elevator.HallOrders.Cost do Returns if we are supposed to take the order given the cost map. Assumes who_can_serve is a subset of cost_map keys. """ - @spec assigned_to_me?(cost_map(), MapSet.t(node())) :: node() + @spec assigned_to_me?(cost_map(), MapSet.t(node())) :: boolean() def assigned_to_me?(cost_map, who_can_serve) do {min_node, _} = Enum.filter(cost_map, fn {node, _} -> MapSet.member?(who_can_serve, node) end) diff --git a/lib/elevator/hall_orders/order.ex b/lib/elevator/hall_orders/order.ex index 1d7b347..6deee3f 100644 --- a/lib/elevator/hall_orders/order.ex +++ b/lib/elevator/hall_orders/order.ex @@ -57,6 +57,7 @@ defmodule Elevator.HallOrders.Order do def update_from_button_press(order_state), do: order_state + @spec update_from_button_press(hall_order_state()) :: hall_order_state() def update_from_arrived_at_floor({:handling, _}) do ensure_self_in_barriers({:arrived, MapSet.new()}) end diff --git a/lib/elevator/hardware/outputs.ex b/lib/elevator/hardware/outputs.ex index 749bef1..5580a0c 100644 --- a/lib/elevator/hardware/outputs.ex +++ b/lib/elevator/hardware/outputs.ex @@ -14,12 +14,13 @@ defmodule Elevator.Hardware.Outputs do Driver.set_motor_direction(:stop) end - @spec set_outputs(FSM.State.t(), Elevator.OrderUtils.combined_order_map()) :: any() + @spec set_outputs(FSM.State.t(), Elevator.OrderUtils.combined_order_map()) :: :ok def set_outputs(state, light_orders) do set_door_light(state) set_motors(state) set_floor_light(state) set_order_lights(light_orders) + :ok end defp set_motors(elevator_state) do diff --git a/lib/elevator/order_utils.ex b/lib/elevator/order_utils.ex index 98212c0..0b7c6bb 100644 --- a/lib/elevator/order_utils.ex +++ b/lib/elevator/order_utils.ex @@ -10,13 +10,13 @@ defmodule Elevator.OrderUtils do } @spec orders_above?(combined_order_map(), Elevator.floor()) :: boolean() - def orders_above?(reqs, floor) do - Enum.any?(reqs, fn {f, _} -> f > floor end) + def orders_above?(orders, floor) do + Enum.any?(orders, fn {f, _} -> f > floor end) end @spec orders_below?(combined_order_map(), Elevator.floor()) :: boolean() - def orders_below?(reqs, floor) do - Enum.any?(reqs, fn {f, _} -> f < floor end) + def orders_below?(orders, floor) do + Enum.any?(orders, fn {f, _} -> f < floor end) end @spec combine_hall_and_cab(