Created
October 2, 2019 20:21
-
-
Save Jwsonic/fe161cdeebd76612226bffe557c2740e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Sutro.ServiceMode.Controller do | |
@moduledoc """ | |
A Controller process that waits for various service mode messages to come in. | |
It also includes various helper methods for sending messages to the correct process. | |
""" | |
require Logger | |
require ServiceModeConstants | |
alias Sutro.ServiceMode.{ServiceSession, ServiceSessionManager} | |
@doc """ | |
route_message routes a MQTT message to the correct service mode controller process. | |
It will attempt to start that process if it's not already started. | |
This logic probably belongs in the hypotheitcal MagicRegistry or a new router module. | |
It is mostly likely directly called from EventConsumer. | |
""" | |
def route_message( | |
%{"session" => ServiceModeConstants.service_session_prefix() <> session_id} = message | |
) do | |
Logger.metadata(session_id: session_id) | |
Logger.info("Routing message: #{inspect(message)}") | |
with {session_id, _rest} <- Integer.parse(session_id), | |
{:ok, pid} <- locate_process(session_id) do | |
send(pid, {:message, message}) | |
else | |
:error -> Logger.error("Invalid session id") | |
end | |
end | |
@doc """ | |
Starts a Controller process for a given service mode session. | |
""" | |
def start(session_id) do | |
Logger.metadata(session_id: session_id) | |
with %ServiceSession{} = session <- | |
ServiceSessionManager.get_session(session_id) || :no_session, | |
:ok <- register_for_messages(session_id) do | |
do_service_mode(session) | |
else | |
:no_session -> Logger.error("No such service mode session") | |
{:error, error} -> Logger.error(error) | |
end | |
end | |
# Retract requested handler | |
defp do_service_mode( | |
%ServiceSession{ | |
status: ServiceModeConstants.in_progress(), | |
step: ServiceModeConstants.screw_requested() | |
} = session | |
) do | |
receive do | |
{:message, %{"screwRetract" => "ack"}} -> | |
# Ack means the screw is retracting and we should wait for it's response | |
update_and_continue(session, %{step: ServiceModeConstants.screw_retracting()}) | |
after | |
screw_ack_timeout(session) -> | |
time_out_and_finish(session) | |
end | |
end | |
# Autoretracted handler | |
defp do_service_mode( | |
%ServiceSession{ | |
status: ServiceModeConstants.in_progress(), | |
step: ServiceModeConstants.screw_autoretracted() | |
} = session | |
) do | |
receive do | |
{:message, %{"battery" => _battery}} -> | |
# Battery response means move to the pool_removal step | |
update_and_continue(session, %{step: ServiceModeConstants.pool_removal()}) | |
after | |
battery_timeout(session) -> | |
# Autoretract + no battery response means move to the change_cartridge | |
update_and_continue(session, %{step: ServiceModeConstants.change_cartridge()}) | |
end | |
end | |
## | |
# Other step handlers would be here... | |
## | |
# There's nothing more to be done with the session | |
defp do_service_mode(_session), do: :noop | |
# Make some changes, log any errors, then continue processing | |
defp update_and_continue(session, changes) do | |
case ServiceSessionManager.update_service_session(session, changes) do | |
{:ok, session} -> | |
# Continue with the service mode process chain | |
do_service_mode(session) | |
{:error, error} -> | |
Logger.error(error) | |
end | |
end | |
defp time_out_and_finish(session) do | |
case ServiceSessionManager.update_service_session(session, %{ | |
status: ServiceModeConstants.timed_out() | |
}) do | |
{:error, error} -> | |
Logger.error(error) | |
_ -> | |
# We've marked the session as timed out. We won't continue down the chain | |
:done | |
end | |
end | |
# Returns the number of millseconds we need to wait before the battery has timed out | |
defp battery_timeout(%ServiceSession{inserted_at: inserted_at}) do | |
inserted_at | |
|> Timex.shift(seconds: ServiceModeConstants.battery_check_timeout_seconds()) | |
|> Timex.diff(Timex.now(), :milliseconds) | |
|> max(0) | |
end | |
# This would have actual logic, like battery_timeout | |
defp screw_ack_timeout(_session), do: 10_000 | |
# This would register the process for messages | |
defp register_for_messages(_session_id), do: :ok | |
# This looks up a registered process, or starts it if one isn't running. | |
# MagicRegistry to come later | |
defp locate_process(session_id) do | |
case MagicRegistry.find(session_id) do | |
{:ok, pid} -> {:ok, pid} | |
:no_pid -> MagicRegistry.start_process(session_id) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment