diff --git a/.dialyzer_ignore.exs b/.dialyzer_ignore.exs new file mode 100644 index 0000000..d86b1ed --- /dev/null +++ b/.dialyzer_ignore.exs @@ -0,0 +1,6 @@ +[ + {{"lib/parser.ex", 1}, :pattern_match, "The pattern can never match the type."}, + {{"lib/parser.ex", 893}, :pattern_match, "The pattern can never match the type."}, + {{"lib/parser.ex", 324}, :pattern_match_cov, + "The pattern :variable_ can never match, because previous clauses completely cover the type {:error, <<_::224>>}."} +] diff --git a/lib/aprs/packets.ex b/lib/aprs/packets.ex index 1198a51..e833b2b 100644 --- a/lib/aprs/packets.ex +++ b/lib/aprs/packets.ex @@ -89,6 +89,16 @@ defmodule Aprs.Packets do |> Packet.changeset(packet_attrs) |> Repo.insert() do {:ok, packet} -> + symbol_table = + Map.get(packet_attrs, :symbol_table_id) || + get_in(packet_attrs, [:data_extended, :symbol_table_id]) || "/" + + symbol_code = + Map.get(packet_attrs, :symbol_code) || + get_in(packet_attrs, [:data_extended, :symbol_code]) || ">" + + Logger.debug("SYMBOL TABLE: #{inspect(symbol_table)}") + Logger.debug("SYMBOL CODE: #{inspect(symbol_code)}") {:ok, packet} {:error, changeset} -> diff --git a/lib/aprs_web/live/map_live/callsign_view.ex b/lib/aprs_web/live/map_live/callsign_view.ex index ff99f3f..2509dc6 100644 --- a/lib/aprs_web/live/map_live/callsign_view.ex +++ b/lib/aprs_web/live/map_live/callsign_view.ex @@ -749,9 +749,16 @@ defmodule AprsWeb.MapLive.CallsignView do defp has_position_data?(packet) do case packet.data_extended do - %MicE{} -> true - %{latitude: lat, longitude: lon} when not is_nil(lat) and not is_nil(lon) -> true - _ -> false + %MicE{} -> + true + + %{latitude: lat, longitude: lon} when not is_nil(lat) and not is_nil(lon) -> + true + + _ -> + lat = Map.get(packet, :lat) || Map.get(packet, "lat") + lon = Map.get(packet, :lon) || Map.get(packet, "lon") + not is_nil(lat) and not is_nil(lon) end end @@ -793,19 +800,16 @@ defmodule AprsWeb.MapLive.CallsignView do defp get_symbol_table_id(data) do cond do - is_map(data) && Map.has_key?(data, :symbol_table_id) && data.symbol_table_id -> - data.symbol_table_id + is_map(data) && (Map.get(data, :symbol_table_id) || Map.get(data, "symbol_table_id")) -> + Map.get(data, :symbol_table_id) || Map.get(data, "symbol_table_id") - is_map(data) && Map.has_key?(data, "symbol_table_id") && data["symbol_table_id"] -> - data["symbol_table_id"] - - is_map(data) && Map.has_key?(data, :packet) && Map.has_key?(data.packet, :symbol_table_id) && - data.packet.symbol_table_id -> - data.packet.symbol_table_id + is_map(data) && Map.has_key?(data, :packet) && + (Map.get(data.packet, :symbol_table_id) || Map.get(data.packet, "symbol_table_id")) -> + Map.get(data.packet, :symbol_table_id) || Map.get(data.packet, "symbol_table_id") is_map(data) && Map.has_key?(data, "packet") && - Map.has_key?(data["packet"], "symbol_table_id") && data["packet"]["symbol_table_id"] -> - data["packet"]["symbol_table_id"] + (Map.get(data["packet"], :symbol_table_id) || Map.get(data["packet"], "symbol_table_id")) -> + Map.get(data["packet"], :symbol_table_id) || Map.get(data["packet"], "symbol_table_id") true -> "/" @@ -814,19 +818,16 @@ defmodule AprsWeb.MapLive.CallsignView do defp get_symbol_code(data) do cond do - is_map(data) && Map.has_key?(data, :symbol_code) && data.symbol_code -> - data.symbol_code + is_map(data) && (Map.get(data, :symbol_code) || Map.get(data, "symbol_code")) -> + Map.get(data, :symbol_code) || Map.get(data, "symbol_code") - is_map(data) && Map.has_key?(data, "symbol_code") && data["symbol_code"] -> - data["symbol_code"] + is_map(data) && Map.has_key?(data, :packet) && + (Map.get(data.packet, :symbol_code) || Map.get(data.packet, "symbol_code")) -> + Map.get(data.packet, :symbol_code) || Map.get(data.packet, "symbol_code") - is_map(data) && Map.has_key?(data, :packet) && Map.has_key?(data.packet, :symbol_code) && - data.packet.symbol_code -> - data.packet.symbol_code - - is_map(data) && Map.has_key?(data, "packet") && Map.has_key?(data["packet"], "symbol_code") && - data["packet"]["symbol_code"] -> - data["packet"]["symbol_code"] + is_map(data) && Map.has_key?(data, "packet") && + (Map.get(data["packet"], :symbol_code) || Map.get(data["packet"], "symbol_code")) -> + Map.get(data["packet"], :symbol_code) || Map.get(data["packet"], "symbol_code") true -> ">" diff --git a/lib/aprs_web/live/map_live/enhanced.ex b/lib/aprs_web/live/map_live/enhanced.ex index e9050a9..ba4fee6 100644 --- a/lib/aprs_web/live/map_live/enhanced.ex +++ b/lib/aprs_web/live/map_live/enhanced.ex @@ -442,13 +442,19 @@ defmodule AprsWeb.MapLive.Enhanced do data_extended = packet.data_extended || %{} callsign = packet.base_callsign <> if packet.ssid, do: "-#{packet.ssid}", else: "" + symbol_table_id = + Map.get(data_extended, :symbol_table_id) || Map.get(data_extended, "symbol_table_id") || "/" + + symbol_code = + Map.get(data_extended, :symbol_code) || Map.get(data_extended, "symbol_code") || ">" + %{ id: callsign, callsign: callsign, lat: packet.lat, lng: packet.lon, - symbol_table: data_extended["symbol_table_id"] || "/", - symbol_code: data_extended["symbol_code"] || ">", + symbol_table: symbol_table_id, + symbol_code: symbol_code, historical: false, popup: build_popup_content(packet, callsign, false), timestamp: DateTime.to_unix(packet.created_at, :millisecond) @@ -521,18 +527,18 @@ defmodule AprsWeb.MapLive.Enhanced do end) |> Enum.map(fn {callsign, _} -> callsign end) - # Remove old markers from client + # Only update the client if the marker is present socket = - Enum.reduce(old_markers, socket, fn callsign, acc_socket -> - push_event(acc_socket, "remove_marker", %{id: callsign}) - end) - - # Update server state - active_markers = - socket.assigns.active_markers - |> Enum.reject(fn {callsign, _} -> callsign in old_markers end) - |> Map.new() + if old_markers == [] do + socket + else + Enum.reduce(old_markers, socket, fn callsign, acc_socket -> + push_event(acc_socket, "remove_marker", %{id: callsign}) + end) + end + # Use Map.drop/2 for better performance + active_markers = Map.drop(socket.assigns.active_markers, old_markers) assign(socket, :active_markers, active_markers) end @@ -615,9 +621,9 @@ defmodule AprsWeb.MapLive.Enhanced do end defp has_position_data?(packet) do - packet.has_position == true and - packet.lat != nil and - packet.lon != nil + lat = Map.get(packet, :lat) || Map.get(packet, "lat") + lon = Map.get(packet, :lon) || Map.get(packet, "lon") + not is_nil(lat) and not is_nil(lon) end defp within_bounds?(packet, bounds) do diff --git a/lib/aprs_web/live/map_live/index.ex b/lib/aprs_web/live/map_live/index.ex index 55523cd..353b81a 100644 --- a/lib/aprs_web/live/map_live/index.ex +++ b/lib/aprs_web/live/map_live/index.ex @@ -310,7 +310,7 @@ defmodule AprsWeb.MapLive.Index do push_event(acc, "remove_marker", %{id: k}) end) - # Fetch the latest 100 packets within the new bounds and push to client + # Fetch the latest packets within the new bounds and push to client (historical only) bounds_list = [map_bounds.west, map_bounds.south, map_bounds.east, map_bounds.north] now = DateTime.utc_now() one_hour_ago = DateTime.add(now, -3600, :second) @@ -335,6 +335,7 @@ defmodule AprsWeb.MapLive.Index do socket end + # Only update visible_packets with the historical packets for the new bounds assign(socket, map_bounds: map_bounds, visible_packets: new_visible_packets) end @@ -351,8 +352,6 @@ defmodule AprsWeb.MapLive.Index do def handle_info({:postgres_packet, packet}, socket), do: handle_info_postgres_packet(packet, socket) - def handle_info(:flush_packet_buffer, socket), do: handle_info_flush_packet_buffer(socket) - def handle_info(%Phoenix.Socket.Broadcast{topic: "aprs_messages", event: "packet", payload: packet}, socket), do: handle_info({:postgres_packet, packet}, socket) @@ -416,7 +415,8 @@ defmodule AprsWeb.MapLive.Index do all_packets = Map.put(socket.assigns.all_packets, callsign_key, packet) socket = assign(socket, all_packets: all_packets) - if is_nil(lat) or is_nil(lon), + # Only add marker if it is within bounds and not already present + if is_nil(lat) or is_nil(lon) or Map.has_key?(socket.assigns.visible_packets, callsign_key), do: {:noreply, socket}, else: handle_valid_postgres_packet(packet, lat, lon, socket) end @@ -691,62 +691,34 @@ defmodule AprsWeb.MapLive.Index do """ end - # Handle cleanup of old packets + # Clean up expired markers from visible_packets and client, but do not re-query the DB defp handle_cleanup_old_packets(socket) do + # Schedule next cleanup + Process.send_after(self(), :cleanup_old_packets, 60_000) + one_hour_ago = DateTime.add(DateTime.utc_now(), -3600, :second) - packets_to_remove = get_packets_to_remove(socket, one_hour_ago) - visible_packets = get_visible_packets(socket, one_hour_ago) - - socket = - if map_size(visible_packets) == map_size(socket.assigns.visible_packets) do - assign(socket, packet_age_threshold: one_hour_ago) - else - update_socket_for_removed_packets( - socket, - visible_packets, - one_hour_ago, - packets_to_remove - ) - end - - if connected?(socket), do: Process.send_after(self(), :cleanup_old_packets, 60_000) - {:noreply, socket} - end - - defp get_packets_to_remove(socket, one_hour_ago) do - socket.assigns.visible_packets - |> Enum.reject(fn {_key, packet} -> - packet_within_time_threshold?(packet, one_hour_ago) && - within_bounds?(packet, socket.assigns.map_bounds) - end) - |> Enum.map(fn {callsign, _packet} -> callsign end) - end - - defp get_visible_packets(socket, one_hour_ago) do - socket.assigns.visible_packets - |> Enum.filter(fn {_key, packet} -> - packet_within_time_threshold?(packet, one_hour_ago) && - within_bounds?(packet, socket.assigns.map_bounds) - end) - |> Map.new() - end - - defp update_socket_for_removed_packets(socket, visible_packets, one_hour_ago, packets_to_remove) do - socket = - socket - |> push_event("refresh_markers", %{}) - |> assign( - visible_packets: visible_packets, - packet_age_threshold: one_hour_ago - ) - - if Enum.any?(packets_to_remove) do - Enum.reduce(packets_to_remove, socket, fn callsign, acc_socket -> - push_event(acc_socket, "remove_marker", %{id: callsign}) + # Remove expired packets from visible_packets + updated_visible_packets = + socket.assigns.visible_packets + |> Enum.filter(fn {_key, packet} -> + packet_within_time_threshold?(packet, one_hour_ago) end) - else - socket - end + |> Map.new() + + # Remove expired markers from the client + expired_keys = + socket.assigns.visible_packets + |> Enum.reject(fn {_key, packet} -> + packet_within_time_threshold?(packet, one_hour_ago) + end) + |> Enum.map(fn {key, _} -> key end) + + socket = + Enum.reduce(expired_keys, socket, fn key, acc -> + push_event(acc, "remove_marker", %{id: key}) + end) + + assign(socket, visible_packets: updated_visible_packets) end # Check if a packet is within the time threshold (not too old) @@ -879,12 +851,18 @@ defmodule AprsWeb.MapLive.Index do @spec build_packet_map(map() | struct(), number(), number(), map() | nil) :: map() defp build_packet_map(packet, lat, lon, data_extended) do + data_extended = data_extended || %{} callsign = generate_callsign(packet) - symbol_table_id = get_in(data_extended, ["symbol_table_id"]) || "/" - symbol_code = get_in(data_extended, ["symbol_code"]) || ">" + + symbol_table_id = + Map.get(data_extended, :symbol_table_id) || Map.get(data_extended, "symbol_table_id") || "/" + + symbol_code = + Map.get(data_extended, :symbol_code) || Map.get(data_extended, "symbol_code") || ">" symbol_description = - get_in(data_extended, ["symbol_description"]) || "Symbol: #{symbol_table_id}#{symbol_code}" + Map.get(data_extended, :symbol_description) || Map.get(data_extended, "symbol_description") || + "Symbol: #{symbol_table_id}#{symbol_code}" timestamp = cond do @@ -898,7 +876,7 @@ defmodule AprsWeb.MapLive.Index do "" end - comment = get_in(data_extended, ["comment"]) || "" + comment = Map.get(data_extended, :comment) || Map.get(data_extended, "comment") || "" popup = """