deploy to staging dokku fix

This commit is contained in:
Graham McIntire 2025-07-11 13:12:52 -05:00
parent 6a97ba7e45
commit 80602bf2a6
No known key found for this signature in database

View file

@ -71,31 +71,42 @@ defmodule Aprsme.Is do
last_second_timestamp: System.system_time(:second) last_second_timestamp: System.system_time(:second)
} }
with {:ok, socket} <- connect_to_aprs_is(server, port), # Initialize state without requiring immediate connection
:ok <- send_login_string(socket, aprs_user_id, aprs_passcode, default_filter) do state = %{
timer = create_timer(@aprs_timeout) server: server,
keepalive_timer = create_keepalive_timer(@keepalive_interval) port: port,
socket: nil,
timer: nil,
keepalive_timer: nil,
connected_at: connected_at,
packet_stats: packet_stats,
buffer: "",
login_params: %{
user_id: aprs_user_id,
passcode: aprs_passcode,
filter: default_filter
}
}
{:ok, # Try to connect initially, but don't fail if it doesn't work
%{ case connect_to_aprs_is(server, port) do
server: server, {:ok, socket} ->
port: port, case send_login_string(socket, aprs_user_id, aprs_passcode, default_filter) do
socket: socket, :ok ->
timer: timer, timer = create_timer(@aprs_timeout)
keepalive_timer: keepalive_timer, keepalive_timer = create_keepalive_timer(@keepalive_interval)
connected_at: connected_at, {:ok, %{state | socket: socket, timer: timer, keepalive_timer: keepalive_timer}}
packet_stats: packet_stats,
buffer: "", error ->
login_params: %{ Logger.warning("Failed to login to APRS-IS: #{inspect(error)}, will retry")
user_id: aprs_user_id, schedule_reconnect(5000)
passcode: aprs_passcode, {:ok, state}
filter: default_filter end
}
}} error ->
else Logger.warning("Unable to establish initial connection to APRS-IS: #{inspect(error)}, will retry")
_ -> schedule_reconnect(5000)
Logger.error("Unable to establish connection or log in to APRS-IS") {:ok, state}
{:stop, :aprsme_connection_failed}
end end
end end
@ -213,22 +224,38 @@ defmodule Aprsme.Is do
@impl true @impl true
def handle_call({:send_message, message}, _from, state) do def handle_call({:send_message, message}, _from, state) do
next_ack_number = :ets.update_counter(:aprsme, :message_number, 1) case state.socket do
# Append ack number nil ->
message = message <> "{" <> to_string(next_ack_number) <> "\r" Logger.warning("Attempted to send message while not connected to APRS-IS")
{:reply, {:error, :not_connected}, state}
Logger.info("Sending message: #{inspect(message)}") socket ->
:gen_tcp.send(state.socket, message) next_ack_number = :ets.update_counter(:aprsme, :message_number, 1)
{:reply, :ok, state} # Append ack number
message = message <> "{" <> to_string(next_ack_number) <> "\r"
Logger.info("Sending message: #{inspect(message)}")
case :gen_tcp.send(socket, message) do
:ok ->
{:reply, :ok, state}
error ->
Logger.error("Failed to send message to APRS-IS: #{inspect(error)}")
{:reply, {:error, error}, state}
end
end
end end
def handle_call(:get_status, _from, state) do def handle_call(:get_status, _from, state) do
connected = state.socket != nil
status = %{ status = %{
connected: true, connected: connected,
server: server_to_string(state.server), server: server_to_string(state.server),
port: state.port, port: state.port,
connected_at: state.connected_at, connected_at: if(connected, do: state.connected_at, else: nil),
uptime_seconds: DateTime.diff(DateTime.utc_now(), state.connected_at), uptime_seconds: if(connected, do: DateTime.diff(DateTime.utc_now(), state.connected_at), else: 0),
login_id: state.login_params.user_id, login_id: state.login_params.user_id,
filter: state.login_params.filter, filter: state.login_params.filter,
packet_stats: state.packet_stats, packet_stats: state.packet_stats,
@ -240,21 +267,36 @@ defmodule Aprsme.Is do
@impl true @impl true
def handle_info(:aprsme_no_message_timeout, state) do def handle_info(:aprsme_no_message_timeout, state) do
Logger.error("Socket timeout detected. Killing genserver.") case state.socket do
{:stop, :aprsme_timeout, state} nil ->
Logger.debug("Timeout occurred while not connected - ignoring")
{:noreply, state}
_socket ->
Logger.error("Socket timeout detected. Killing genserver.")
{:stop, :aprsme_timeout, state}
end
end end
def handle_info(:send_keepalive, state) do def handle_info(:send_keepalive, state) do
# Send a comment line as keepalive (APRS-IS standard) case state.socket do
case :gen_tcp.send(state.socket, "# keepalive\r\n") do nil ->
:ok -> Logger.debug("Skipping keepalive - not connected to APRS-IS")
Logger.debug("Sent keepalive") # Don't schedule another keepalive if we're not connected
keepalive_timer = create_keepalive_timer(@keepalive_interval) {:noreply, state}
{:noreply, %{state | keepalive_timer: keepalive_timer}}
{:error, reason} -> socket ->
Logger.error("Failed to send keepalive: #{inspect(reason)}") # Send a comment line as keepalive (APRS-IS standard)
{:stop, :normal, state} case :gen_tcp.send(socket, "# keepalive\r\n") do
:ok ->
Logger.debug("Sent keepalive")
keepalive_timer = create_keepalive_timer(@keepalive_interval)
{:noreply, %{state | keepalive_timer: keepalive_timer}}
{:error, reason} ->
Logger.error("Failed to send keepalive: #{inspect(reason)}")
{:stop, :normal, state}
end
end end
end end
@ -332,12 +374,49 @@ defmodule Aprsme.Is do
def handle_info({:tcp_closed, _socket}, state) do def handle_info({:tcp_closed, _socket}, state) do
Logger.warning("Socket has been closed by remote server - will reconnect") Logger.warning("Socket has been closed by remote server - will reconnect")
{:stop, :normal, state} # Cancel any existing timers
if state.timer, do: Process.cancel_timer(state.timer)
if state.keepalive_timer, do: Process.cancel_timer(state.keepalive_timer)
# Schedule reconnect
schedule_reconnect(5000)
{:noreply, %{state | socket: nil, timer: nil, keepalive_timer: nil}}
end end
def handle_info({:tcp_error, _socket, reason}, state) do def handle_info({:tcp_error, _socket, reason}, state) do
Logger.error("Connection error: #{inspect(reason)}") Logger.error("Connection error: #{inspect(reason)} - will reconnect")
{:stop, :normal, state} # Cancel any existing timers
if state.timer, do: Process.cancel_timer(state.timer)
if state.keepalive_timer, do: Process.cancel_timer(state.keepalive_timer)
# Schedule reconnect
schedule_reconnect(5000)
{:noreply, %{state | socket: nil, timer: nil, keepalive_timer: nil}}
end
def handle_info(:reconnect, state) do
Logger.info("Attempting to reconnect to APRS-IS...")
case connect_to_aprs_is(state.server, state.port) do
{:ok, socket} ->
case send_login_string(socket, state.login_params.user_id, state.login_params.passcode, state.login_params.filter) do
:ok ->
Logger.info("Successfully reconnected to APRS-IS")
timer = create_timer(@aprs_timeout)
keepalive_timer = create_keepalive_timer(@keepalive_interval)
{:noreply, %{state | socket: socket, timer: timer, keepalive_timer: keepalive_timer}}
error ->
Logger.warning("Failed to login to APRS-IS: #{inspect(error)}, will retry in 10 seconds")
schedule_reconnect(10000)
{:noreply, state}
end
error ->
Logger.warning("Unable to reconnect to APRS-IS: #{inspect(error)}, will retry in 10 seconds")
schedule_reconnect(10000)
{:noreply, state}
end
end end
# Extract complete lines from buffer, returning {complete_lines, remaining_buffer} # Extract complete lines from buffer, returning {complete_lines, remaining_buffer}
@ -519,4 +598,9 @@ defmodule Aprsme.Is do
end end
defp struct_to_map(value), do: value defp struct_to_map(value), do: value
@spec schedule_reconnect(non_neg_integer()) :: reference()
defp schedule_reconnect(delay) do
Process.send_after(self(), :reconnect, delay)
end
end end