defmodule AprsWeb.StatusLive.Index do @moduledoc """ LiveView for displaying real-time APRS-IS connection status """ use AprsWeb, :live_view # 30 seconds @refresh_interval 1_000 @impl true def mount(_params, _session, socket) do socket = assign(socket, page_title: "System Status", aprs_status: get_aprs_status(), version: get_app_version(), current_time: DateTime.utc_now(), auto_refresh: true, health_score: calculate_health_score(get_aprs_status()) ) if connected?(socket) do # Schedule the first refresh schedule_refresh() end {:ok, socket} end @impl true def handle_event("toggle_auto_refresh", _params, socket) do new_auto_refresh = !socket.assigns.auto_refresh socket = assign(socket, auto_refresh: new_auto_refresh) if new_auto_refresh do schedule_refresh() end {:noreply, socket} end @impl true def handle_event("refresh_now", _params, socket) do socket = refresh_status(socket) {:noreply, socket} end @impl true def handle_info(:refresh_status, socket) do socket = refresh_status(socket) # Schedule next refresh if auto-refresh is enabled if socket.assigns.auto_refresh do schedule_refresh() end {:noreply, socket} end @impl true def render(assigns) do ~H"""

APRS.me System Status

Last updated: {Calendar.strftime(@current_time, "%H:%M:%S UTC")}
<%= if not @aprs_status.connected do %>

APRS-IS Connection Issue

The system is currently disconnected from the APRS-IS network. This may be due to network issues or server maintenance.

<% end %>

APRS-IS Connection

Status: <%= if @aprs_status.connected do %> Connected <% else %> Disconnected <% end %>
Server: {@aprs_status.server}:{@aprs_status.port}
Login ID: {@aprs_status.login_id}
Connected Since: <%= if @aprs_status.connected_at do %> {Calendar.strftime(@aprs_status.connected_at, "%Y-%m-%d %H:%M:%S UTC")} <% else %> Not connected <% end %>
Uptime: {format_uptime(@aprs_status.uptime_seconds)}
Filter: {@aprs_status.filter}

Packet Statistics

Total Packets: {format_number(@aprs_status.packet_stats.total_packets)}
Packets/Sec: {@aprs_status.packet_stats.packets_per_second}
Last Packet: <%= if @aprs_status.packet_stats.last_packet_at do %> {format_time_ago(@aprs_status.packet_stats.last_packet_at)} <% else %> None <% end %>
Stored Packets: {format_number(@aprs_status.stored_packet_count)}
Connection Health:
<%= for i <- 1..5 do %> <% end %>
{@health_score}/5

{get_health_description(@health_score, @aprs_status.connected)}

Application Information

Version: {@version}
Current Time: {Calendar.strftime(@current_time, "%Y-%m-%d %H:%M:%S UTC")}

Auto-refresh: {if @auto_refresh, do: "ON", else: "OFF"} <%= if @auto_refresh do %> (every second) <% end %>

""" end # Private functions defp get_aprs_status do Aprs.Is.get_status() end defp get_app_version do :aprs |> Application.spec(:vsn) |> List.to_string() end defp refresh_status(socket) do aprs_status = get_aprs_status() assign(socket, aprs_status: aprs_status, current_time: DateTime.utc_now(), health_score: calculate_health_score(aprs_status) ) end defp schedule_refresh do Process.send_after(self(), :refresh_status, @refresh_interval) end defp format_uptime(seconds) when seconds <= 0, do: "Not connected" defp 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 defp get_health_description(score, connected) do case {score, connected} do {1, false} -> "Disconnected - Connection issues detected" {2, true} -> "Recently connected - Monitoring stability" {3, true} -> "Good - Connection stable for less than 1 hour" {4, true} -> "Very good - Connection stable for less than 1 day" {5, true} -> "Excellent - Long-term stable connection" _ -> "Unknown status" end end defp format_time_ago(datetime) do diff_seconds = DateTime.diff(DateTime.utc_now(), datetime) cond do diff_seconds < 60 -> "#{diff_seconds} seconds ago" diff_seconds < 3600 -> "#{div(diff_seconds, 60)} minutes ago" diff_seconds < 86_400 -> "#{div(diff_seconds, 3600)} hours ago" true -> "#{div(diff_seconds, 86_400)} days ago" end end defp format_number(number) when is_integer(number) do number |> Integer.to_string() |> String.reverse() |> String.replace(~r/(\d{3})(?=\d)/, "\\1,") |> String.reverse() end defp format_number(number), do: to_string(number) end