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
|
### 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`
|
**Event:** `subscribe_bounds`
|
||||||
|
|
||||||
|
|
@ -54,10 +54,23 @@ Subscribe to receive packets within specific geographic bounds.
|
||||||
"north": 33.2,
|
"north": 33.2,
|
||||||
"south": 33.0,
|
"south": 33.0,
|
||||||
"east": -96.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:**
|
**Example:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -67,7 +80,9 @@ Subscribe to receive packets within specific geographic bounds.
|
||||||
"north": 33.2,
|
"north": 33.2,
|
||||||
"south": 33.0,
|
"south": 33.0,
|
||||||
"east": -96.0,
|
"east": -96.0,
|
||||||
"west": -96.2
|
"west": -96.2,
|
||||||
|
"limit": 2000,
|
||||||
|
"hours_back": 2
|
||||||
},
|
},
|
||||||
"ref": "2"
|
"ref": "2"
|
||||||
}
|
}
|
||||||
|
|
@ -133,7 +148,7 @@ Update the geographic bounds (e.g., when user pans/zooms the map).
|
||||||
|
|
||||||
### Receiving Packets
|
### 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`
|
**Event:** `packet`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,11 @@ defmodule AprsmeWeb.MobileChannel do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@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 = %{
|
bounds = %{
|
||||||
north: ensure_float(north),
|
north: ensure_float(north),
|
||||||
south: ensure_float(south),
|
south: ensure_float(south),
|
||||||
|
|
@ -62,24 +66,28 @@ defmodule AprsmeWeb.MobileChannel do
|
||||||
}
|
}
|
||||||
|
|
||||||
# Validate bounds
|
# Validate bounds
|
||||||
with :ok <- validate_bounds(bounds) do
|
case validate_bounds(bounds) do
|
||||||
# Generate unique client ID
|
:ok ->
|
||||||
client_id = "mobile_#{:erlang.phash2(self())}"
|
# Generate unique client ID
|
||||||
|
client_id = "mobile_#{:erlang.phash2(self())}"
|
||||||
|
|
||||||
# Subscribe to StreamingPacketsPubSub with bounds
|
# Subscribe to StreamingPacketsPubSub with bounds
|
||||||
Aprsme.StreamingPacketsPubSub.subscribe_to_bounds(self(), bounds)
|
Aprsme.StreamingPacketsPubSub.subscribe_to_bounds(self(), bounds)
|
||||||
|
|
||||||
# Store client info in socket
|
# Store client info in socket
|
||||||
socket =
|
socket =
|
||||||
socket
|
socket
|
||||||
|> assign(:client_id, client_id)
|
|> assign(:client_id, client_id)
|
||||||
|> assign(:bounds, bounds)
|
|> assign(:bounds, bounds)
|
||||||
|> assign(:subscribed, true)
|
|> 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} ->
|
{:error, reason} ->
|
||||||
{:reply, {:error, %{message: reason}}, socket}
|
{:reply, {:error, %{message: reason}}, socket}
|
||||||
end
|
end
|
||||||
|
|
@ -194,7 +202,7 @@ defmodule AprsmeWeb.MobileChannel do
|
||||||
callsign: get_field(packet, :sender) || get_field(packet, :base_callsign),
|
callsign: get_field(packet, :sender) || get_field(packet, :base_callsign),
|
||||||
lat: to_float(lat),
|
lat: to_float(lat),
|
||||||
lng: to_float(lon),
|
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_table_id: get_field(packet, :symbol_table_id, "/"),
|
||||||
symbol_code: get_field(packet, :symbol_code, ">"),
|
symbol_code: get_field(packet, :symbol_code, ">"),
|
||||||
comment: get_field(packet, :comment),
|
comment: get_field(packet, :comment),
|
||||||
|
|
@ -238,4 +246,53 @@ defmodule AprsmeWeb.MobileChannel do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp format_timestamp(_), do: nil
|
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
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue