feat: add send_trail_line_for_tracked_callsign function
- Filters packets by tracked callsign and time threshold - Sorts packets chronologically by received_at - Pushes show_trail_line event with coordinates - Returns socket unchanged when no packets found
This commit is contained in:
parent
d3ef999ab9
commit
6add35b11a
2 changed files with 186 additions and 0 deletions
|
|
@ -74,6 +74,56 @@ defmodule AprsmeWeb.MapLive.DisplayManager do
|
|||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Send trail line data for tracked callsign.
|
||||
Connects all position points chronologically, regardless of distance.
|
||||
"""
|
||||
@spec send_trail_line_for_tracked_callsign(Socket.t()) :: Socket.t()
|
||||
def send_trail_line_for_tracked_callsign(socket) do
|
||||
callsign = socket.assigns.tracked_callsign
|
||||
threshold = socket.assigns.packet_age_threshold
|
||||
|
||||
packets =
|
||||
socket.assigns.visible_packets
|
||||
|> Map.values()
|
||||
|> Enum.filter(fn packet ->
|
||||
sender = Map.get(packet, :sender) || Map.get(packet, "sender")
|
||||
received_at = Map.get(packet, :received_at) || Map.get(packet, "received_at")
|
||||
|
||||
String.upcase(sender) == String.upcase(callsign) &&
|
||||
DateTime.compare(received_at, threshold) != :lt
|
||||
end)
|
||||
|> Enum.sort_by(
|
||||
fn packet ->
|
||||
received_at = Map.get(packet, :received_at) || Map.get(packet, "received_at")
|
||||
DateTime.to_unix(received_at, :microsecond)
|
||||
end,
|
||||
:asc
|
||||
)
|
||||
|
||||
if Enum.empty?(packets) do
|
||||
socket
|
||||
else
|
||||
points =
|
||||
Enum.map(packets, fn packet ->
|
||||
lat = Map.get(packet, :lat) || Map.get(packet, "lat")
|
||||
lon = Map.get(packet, :lon) || Map.get(packet, "lon")
|
||||
received_at = Map.get(packet, :received_at) || Map.get(packet, "received_at")
|
||||
|
||||
%{
|
||||
lat: Aprsme.EncodingUtils.to_float(lat) || 0.0,
|
||||
lng: Aprsme.EncodingUtils.to_float(lon) || 0.0,
|
||||
timestamp: DateTime.to_iso8601(received_at)
|
||||
}
|
||||
end)
|
||||
|
||||
LiveView.push_event(socket, "show_trail_line", %{
|
||||
callsign: callsign,
|
||||
points: points
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Send heat map data for filtered packets.
|
||||
"""
|
||||
|
|
|
|||
136
test/aprsme_web/live/map_live/display_manager_test.exs
Normal file
136
test/aprsme_web/live/map_live/display_manager_test.exs
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
defmodule AprsmeWeb.MapLive.DisplayManagerTest do
|
||||
use Aprsme.DataCase, async: true
|
||||
|
||||
alias AprsmeWeb.MapLive.DisplayManager
|
||||
|
||||
setup do
|
||||
# Create a basic socket mock for testing
|
||||
socket = %Phoenix.LiveView.Socket{
|
||||
assigns: %{
|
||||
visible_packets: %{},
|
||||
tracked_callsign: nil,
|
||||
packet_age_threshold: nil,
|
||||
map_bounds: nil,
|
||||
map_zoom: 10,
|
||||
historical_packets: %{}
|
||||
},
|
||||
private: %{live_temp: %{}}
|
||||
}
|
||||
|
||||
{:ok, socket: socket}
|
||||
end
|
||||
|
||||
describe "send_trail_line_for_tracked_callsign/1" do
|
||||
test "pushes trail line event with sorted points", %{socket: socket} do
|
||||
packet1 = %{
|
||||
id: "1",
|
||||
sender: "W5ISP-9",
|
||||
lat: 30.123,
|
||||
lon: -97.456,
|
||||
received_at: ~U[2024-01-01 10:00:00Z]
|
||||
}
|
||||
|
||||
packet2 = %{
|
||||
id: "2",
|
||||
sender: "W5ISP-9",
|
||||
lat: 30.124,
|
||||
lon: -97.457,
|
||||
received_at: ~U[2024-01-01 10:05:00Z]
|
||||
}
|
||||
|
||||
packet3 = %{
|
||||
id: "3",
|
||||
sender: "W5ISP-9",
|
||||
lat: 30.125,
|
||||
lon: -97.458,
|
||||
received_at: ~U[2024-01-01 10:10:00Z]
|
||||
}
|
||||
|
||||
visible_packets = %{"W5ISP-9:1" => packet1, "W5ISP-9:2" => packet2, "W5ISP-9:3" => packet3}
|
||||
|
||||
socket =
|
||||
Map.put(socket, :assigns, %{
|
||||
socket.assigns
|
||||
| visible_packets: visible_packets,
|
||||
tracked_callsign: "W5ISP-9",
|
||||
packet_age_threshold: ~U[2024-01-01 09:00:00Z]
|
||||
})
|
||||
|
||||
result_socket = DisplayManager.send_trail_line_for_tracked_callsign(socket)
|
||||
|
||||
push_events = get_in(result_socket.private, [:live_temp, :push_events]) || []
|
||||
assert length(push_events) == 1
|
||||
|
||||
[[event_name, event_data]] = push_events
|
||||
assert event_name == "show_trail_line"
|
||||
|
||||
assert event_data == %{
|
||||
callsign: "W5ISP-9",
|
||||
points: [
|
||||
%{lat: 30.123, lng: -97.456, timestamp: "2024-01-01T10:00:00Z"},
|
||||
%{lat: 30.124, lng: -97.457, timestamp: "2024-01-01T10:05:00Z"},
|
||||
%{lat: 30.125, lng: -97.458, timestamp: "2024-01-01T10:10:00Z"}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
test "filters by packet age threshold", %{socket: socket} do
|
||||
packet1 = %{
|
||||
id: "1",
|
||||
sender: "W5ISP-9",
|
||||
lat: 30.123,
|
||||
lon: -97.456,
|
||||
received_at: ~U[2024-01-01 09:00:00Z]
|
||||
}
|
||||
|
||||
# Too old
|
||||
packet2 = %{
|
||||
id: "2",
|
||||
sender: "W5ISP-9",
|
||||
lat: 30.124,
|
||||
lon: -97.457,
|
||||
received_at: ~U[2024-01-01 10:05:00Z]
|
||||
}
|
||||
|
||||
# Within threshold
|
||||
|
||||
visible_packets = %{"W5ISP-9:1" => packet1, "W5ISP-9:2" => packet2}
|
||||
|
||||
socket =
|
||||
Map.put(socket, :assigns, %{
|
||||
socket.assigns
|
||||
| visible_packets: visible_packets,
|
||||
tracked_callsign: "W5ISP-9",
|
||||
packet_age_threshold: ~U[2024-01-01 10:00:00Z]
|
||||
})
|
||||
|
||||
result_socket = DisplayManager.send_trail_line_for_tracked_callsign(socket)
|
||||
|
||||
push_events = get_in(result_socket.private, [:live_temp, :push_events]) || []
|
||||
assert length(push_events) == 1
|
||||
|
||||
[[event_name, event_data]] = push_events
|
||||
assert event_name == "show_trail_line"
|
||||
|
||||
assert event_data == %{
|
||||
callsign: "W5ISP-9",
|
||||
points: [%{lat: 30.124, lng: -97.457, timestamp: "2024-01-01T10:05:00Z"}]
|
||||
}
|
||||
end
|
||||
|
||||
test "returns socket unchanged when no packets", %{socket: socket} do
|
||||
socket =
|
||||
Map.put(socket, :assigns, %{
|
||||
socket.assigns
|
||||
| visible_packets: %{},
|
||||
tracked_callsign: "W5ISP-9",
|
||||
packet_age_threshold: ~U[2024-01-01 10:00:00Z]
|
||||
})
|
||||
|
||||
result_socket = DisplayManager.send_trail_line_for_tracked_callsign(socket)
|
||||
|
||||
push_events = get_in(result_socket.private, [:live_temp, :push_events]) || []
|
||||
assert push_events == []
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue