{gettext("Last Packet:")}
<%= if @aprs_status.packet_stats.last_packet_at do %>
{format_time_ago(@aprs_status.packet_stats.last_packet_at)}
<% else %>
{gettext("None")}
<% end %>
<%= if Map.has_key?(@aprs_status.cluster_info, :all_nodes) do %>
{gettext("Cluster Nodes:")}
<%= for node_name <- @aprs_status.cluster_info.all_nodes do %>
<%= if node_name == @aprs_status.cluster_info.leader_node do %>
<% end %>
{node_name}
<% end %>
{gettext("Leader node")}
{gettext("highlighted")}
<% end %>
{gettext(
"Status is aggregated from all nodes in the cluster. Only the leader node maintains the APRS-IS connection."
)}
<% end %>
"""
end
# Private functions
defp get_aprs_status do
LeaderElection.get_cluster_aprs_status()
rescue
error ->
require Logger
Logger.error("Error getting APRS status: #{inspect(error)}")
# Return a default status when database is unavailable
default_status()
end
defp get_cached_aprs_status do
# Try to get cached status for instant load
case Aprsme.Cache.get(:query_cache, "aprs_status") do
{:ok, status} when not is_nil(status) ->
status
_ ->
# Fallback to direct query if cache miss
status = get_aprs_status()
Aprsme.Cache.put(:query_cache, "aprs_status", status)
status
end
end
defp default_status do
%{
connected: false,
server: "unknown",
port: 0,
connected_at: nil,
uptime_seconds: 0,
login_id: "unknown",
filter: "unknown",
packet_stats: %{
total_packets: 0,
packets_per_second: 0,
last_packet_at: nil
},
stored_packet_count: 0,
oldest_packet_timestamp: nil
}
end
defp schedule_refresh do
Process.send_after(self(), :refresh_status, @refresh_interval)
end
def format_uptime(seconds) when seconds <= 0, do: gettext("Not connected")
def format_uptime(seconds) do
days = div(seconds, 86_400)
hours = div(rem(seconds, 86_400), 3600)
minutes = div(rem(seconds, 3600), 60)
secs = rem(seconds, 60)
cond do
days > 0 -> "#{days}d #{hours}h #{minutes}m #{secs}s"
hours > 0 -> "#{hours}h #{minutes}m #{secs}s"
minutes > 0 -> "#{minutes}m #{secs}s"
true -> "#{secs}s"
end
end
defp calculate_health_score(aprs_status) do
cond do
not aprs_status.connected -> 1
# Less than 5 minutes
aprs_status.uptime_seconds < 300 -> 2
# Less than 1 hour
aprs_status.uptime_seconds < 3600 -> 3
# Less than 1 day
aprs_status.uptime_seconds < 86_400 -> 4
# More than 1 day
true -> 5
end
end
def get_health_description(score, connected) do
case {score, connected} do
{1, false} -> gettext("Disconnected - Connection issues detected")
{2, true} -> gettext("Recently connected - Monitoring stability")
{3, true} -> gettext("Good - Connection stable for less than 1 hour")
{4, true} -> gettext("Very good - Connection stable for less than 1 day")
{5, true} -> gettext("Excellent - Long-term stable connection")
_ -> gettext("Unknown status")
end
end
def format_time_ago(nil), do: gettext("Never")
def format_time_ago(datetime) do
diff_seconds = DateTime.diff(DateTime.utc_now(), datetime)
cond do
diff_seconds < 60 -> gettext("%{count} seconds ago", count: diff_seconds)
diff_seconds < 3600 -> gettext("%{count} minutes ago", count: div(diff_seconds, 60))
diff_seconds < 86_400 -> gettext("%{count} hours ago", count: div(diff_seconds, 3600))
true -> gettext("%{count} days ago", count: div(diff_seconds, 86_400))
end
end
def format_number(number) when is_integer(number) do
number
|> Integer.to_string()
|> String.reverse()
|> String.replace(~r/(\d{3})(?=\d)/, "\\1,")
|> String.reverse()
end
def format_number(number), do: to_string(number)
end