Add historical packet loading to mobile API
When mobile clients subscribe to bounds, the server now loads and sends historical packets immediately, followed by real-time streaming packets. This matches the web UI behavior and provides a complete view of APRS activity in the requested area. Optional parameters: - limit: Max historical packets to load (default: 1000, max: 5000) - hours_back: Hours of historical data (default: 1, max: 24) Generated with Claude Code https://claude.com/claude-code
This commit is contained in:
parent
f94ac8097c
commit
d6c3ada6c4
2 changed files with 92 additions and 20 deletions
|
|
@ -44,7 +44,7 @@ Currently, connections are anonymous. Future versions will support token-based a
|
|||
|
||||
### Subscribing to Geographic Bounds
|
||||
|
||||
Subscribe to receive packets within specific geographic bounds.
|
||||
Subscribe to receive packets within specific geographic bounds. Upon subscription, the server will immediately send historical packets within the bounds, followed by real-time streaming packets.
|
||||
|
||||
**Event:** `subscribe_bounds`
|
||||
|
||||
|
|
@ -54,10 +54,23 @@ Subscribe to receive packets within specific geographic bounds.
|
|||
"north": 33.2,
|
||||
"south": 33.0,
|
||||
"east": -96.0,
|
||||
"west": -96.2
|
||||
"west": -96.2,
|
||||
"limit": 1000,
|
||||
"hours_back": 1
|
||||
}
|
||||
```
|
||||
|
||||
**Payload Fields:**
|
||||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `north` | float | Yes | - | Northern boundary latitude |
|
||||
| `south` | float | Yes | - | Southern boundary latitude |
|
||||
| `east` | float | Yes | - | Eastern boundary longitude |
|
||||
| `west` | float | Yes | - | Western boundary longitude |
|
||||
| `limit` | integer | No | 1000 | Maximum historical packets to load (max: 5000) |
|
||||
| `hours_back` | integer | No | 1 | How many hours of historical data to load (max: 24) |
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
|
|
@ -67,7 +80,9 @@ Subscribe to receive packets within specific geographic bounds.
|
|||
"north": 33.2,
|
||||
"south": 33.0,
|
||||
"east": -96.0,
|
||||
"west": -96.2
|
||||
"west": -96.2,
|
||||
"limit": 2000,
|
||||
"hours_back": 2
|
||||
},
|
||||
"ref": "2"
|
||||
}
|
||||
|
|
@ -133,7 +148,7 @@ Update the geographic bounds (e.g., when user pans/zooms the map).
|
|||
|
||||
### Receiving Packets
|
||||
|
||||
Once subscribed, you'll receive real-time packets within your bounds.
|
||||
Once subscribed, you'll receive packets within your bounds. Historical packets are sent immediately after subscription, followed by real-time streaming packets as they arrive.
|
||||
|
||||
**Event:** `packet`
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,11 @@ defmodule AprsmeWeb.MobileChannel do
|
|||
end
|
||||
|
||||
@impl true
|
||||
def handle_in("subscribe_bounds", %{"north" => north, "south" => south, "east" => east, "west" => west}, socket) do
|
||||
def handle_in(
|
||||
"subscribe_bounds",
|
||||
%{"north" => north, "south" => south, "east" => east, "west" => west} = payload,
|
||||
socket
|
||||
) do
|
||||
bounds = %{
|
||||
north: ensure_float(north),
|
||||
south: ensure_float(south),
|
||||
|
|
@ -62,24 +66,28 @@ defmodule AprsmeWeb.MobileChannel do
|
|||
}
|
||||
|
||||
# Validate bounds
|
||||
with :ok <- validate_bounds(bounds) do
|
||||
# Generate unique client ID
|
||||
client_id = "mobile_#{:erlang.phash2(self())}"
|
||||
case validate_bounds(bounds) do
|
||||
:ok ->
|
||||
# Generate unique client ID
|
||||
client_id = "mobile_#{:erlang.phash2(self())}"
|
||||
|
||||
# Subscribe to StreamingPacketsPubSub with bounds
|
||||
Aprsme.StreamingPacketsPubSub.subscribe_to_bounds(self(), bounds)
|
||||
# Subscribe to StreamingPacketsPubSub with bounds
|
||||
Aprsme.StreamingPacketsPubSub.subscribe_to_bounds(self(), bounds)
|
||||
|
||||
# Store client info in socket
|
||||
socket =
|
||||
socket
|
||||
|> assign(:client_id, client_id)
|
||||
|> assign(:bounds, bounds)
|
||||
|> assign(:subscribed, true)
|
||||
# Store client info in socket
|
||||
socket =
|
||||
socket
|
||||
|> assign(:client_id, client_id)
|
||||
|> assign(:bounds, bounds)
|
||||
|> assign(:subscribed, true)
|
||||
|
||||
Logger.info("Mobile client #{client_id} subscribed to bounds: #{inspect(bounds)}")
|
||||
Logger.info("Mobile client #{client_id} subscribed to bounds: #{inspect(bounds)}")
|
||||
|
||||
# Load and send historical packets
|
||||
socket = load_historical_packets(socket, bounds, payload)
|
||||
|
||||
{:reply, {:ok, %{bounds: bounds, message: "Subscribed to packet stream"}}, socket}
|
||||
|
||||
{:reply, {:ok, %{bounds: bounds, message: "Subscribed to packet stream"}}, socket}
|
||||
else
|
||||
{:error, reason} ->
|
||||
{:reply, {:error, %{message: reason}}, socket}
|
||||
end
|
||||
|
|
@ -194,7 +202,7 @@ defmodule AprsmeWeb.MobileChannel do
|
|||
callsign: get_field(packet, :sender) || get_field(packet, :base_callsign),
|
||||
lat: to_float(lat),
|
||||
lng: to_float(lon),
|
||||
timestamp: get_field(packet, :received_at) |> format_timestamp(),
|
||||
timestamp: packet |> get_field(:received_at) |> format_timestamp(),
|
||||
symbol_table_id: get_field(packet, :symbol_table_id, "/"),
|
||||
symbol_code: get_field(packet, :symbol_code, ">"),
|
||||
comment: get_field(packet, :comment),
|
||||
|
|
@ -238,4 +246,53 @@ defmodule AprsmeWeb.MobileChannel do
|
|||
end
|
||||
|
||||
defp format_timestamp(_), do: nil
|
||||
|
||||
defp load_historical_packets(socket, bounds, payload) do
|
||||
# Get optional parameters from payload
|
||||
limit = Map.get(payload, "limit", 1000)
|
||||
hours_back = Map.get(payload, "hours_back", 1)
|
||||
|
||||
# Ensure reasonable limits
|
||||
limit = min(limit, 5000)
|
||||
hours_back = min(hours_back, 24)
|
||||
|
||||
# Query historical packets within bounds
|
||||
bounds_list = [
|
||||
bounds.west,
|
||||
bounds.south,
|
||||
bounds.east,
|
||||
bounds.north
|
||||
]
|
||||
|
||||
params = %{
|
||||
bounds: bounds_list,
|
||||
limit: limit,
|
||||
offset: 0,
|
||||
hours_back: hours_back
|
||||
}
|
||||
|
||||
Logger.debug("Mobile client loading historical packets with params: #{inspect(params)}")
|
||||
|
||||
packets =
|
||||
try do
|
||||
Aprsme.Packets.get_recent_packets(params)
|
||||
rescue
|
||||
error ->
|
||||
Logger.error("Error loading historical packets for mobile client: #{inspect(error)}")
|
||||
[]
|
||||
end
|
||||
|
||||
Logger.info("Loaded #{length(packets)} historical packets for mobile client")
|
||||
|
||||
# Send historical packets to client
|
||||
if length(packets) > 0 do
|
||||
# Convert packets to mobile format and send them
|
||||
Enum.each(packets, fn packet ->
|
||||
packet_data = build_mobile_packet(packet)
|
||||
push(socket, "packet", packet_data)
|
||||
end)
|
||||
end
|
||||
|
||||
socket
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue