PathLive: live-refresh the forecast when new grid scores land
Subscribe to "propagation:updated" on connected mount and, when the grid worker finishes a forecast-hour step, re-read point_forecast for the path midpoint using the stashed band_mhz and source/destination. Terrain, HRRR profiles, and the loss/power budget are independent of grid scores and stay as-is. Before this, a /path page rendered once at mount and only refreshed on manual reload.
This commit is contained in:
parent
662d7232e5
commit
790eef1726
2 changed files with 85 additions and 0 deletions
|
|
@ -29,6 +29,10 @@ defmodule MicrowavepropWeb.PathLive do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def mount(_params, _session, socket) do
|
def mount(_params, _session, socket) do
|
||||||
|
if connected?(socket) do
|
||||||
|
Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "propagation:updated")
|
||||||
|
end
|
||||||
|
|
||||||
{:ok,
|
{:ok,
|
||||||
assign(socket,
|
assign(socket,
|
||||||
page_title: "Path Calculator",
|
page_title: "Path Calculator",
|
||||||
|
|
@ -202,6 +206,19 @@ defmodule MicrowavepropWeb.PathLive do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_info({:propagation_updated, _valid_times}, socket) do
|
||||||
|
case socket.assigns.result do
|
||||||
|
%{band_mhz: band_mhz, source: src, destination: dst} = result ->
|
||||||
|
midlat = (src.lat + dst.lat) / 2
|
||||||
|
midlon = (src.lon + dst.lon) / 2
|
||||||
|
forecast = Propagation.point_forecast(band_mhz, midlat, midlon)
|
||||||
|
{:noreply, assign(socket, result: %{result | forecast: forecast})}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp compute_path(source, dest, band_mhz, station_params) do
|
defp compute_path(source, dest, band_mhz, station_params) do
|
||||||
with {:ok, src} <- resolve_location(source),
|
with {:ok, src} <- resolve_location(source),
|
||||||
{:ok, dst} <- resolve_location(dest) do
|
{:ok, dst} <- resolve_location(dest) do
|
||||||
|
|
|
||||||
68
test/microwaveprop_web/live/path_live_test.exs
Normal file
68
test/microwaveprop_web/live/path_live_test.exs
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
defmodule MicrowavepropWeb.PathLiveTest do
|
||||||
|
use MicrowavepropWeb.ConnCase, async: false
|
||||||
|
|
||||||
|
import Phoenix.LiveViewTest
|
||||||
|
|
||||||
|
alias Microwaveprop.Propagation
|
||||||
|
alias Microwaveprop.Propagation.ScoreCache
|
||||||
|
alias Microwaveprop.Terrain.ElevationClient
|
||||||
|
|
||||||
|
describe "propagation_updated handler" do
|
||||||
|
setup do
|
||||||
|
ScoreCache.clear()
|
||||||
|
|
||||||
|
# Flat-terrain stub so compute_path can build a result without hitting
|
||||||
|
# the real elevation API.
|
||||||
|
Req.Test.stub(ElevationClient, fn conn ->
|
||||||
|
params = Plug.Conn.fetch_query_params(conn).query_params
|
||||||
|
lat_count = params["latitude"] |> String.split(",") |> length()
|
||||||
|
Req.Test.json(conn, %{"elevation" => List.duplicate(200.0, lat_count)})
|
||||||
|
end)
|
||||||
|
|
||||||
|
on_exit(fn -> ScoreCache.clear() end)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
test "re-reads and re-renders the forecast when new scores arrive", %{conn: conn} do
|
||||||
|
# Path midpoint is (33.0, -97.0) — the point point_forecast queries.
|
||||||
|
{mid_lat, mid_lon} = {33.0, -97.0}
|
||||||
|
|
||||||
|
# Seed three hourly valid_times with a LOW score of 30.
|
||||||
|
base =
|
||||||
|
DateTime.utc_now()
|
||||||
|
|> DateTime.truncate(:second)
|
||||||
|
|> Map.put(:minute, 0)
|
||||||
|
|> Map.put(:second, 0)
|
||||||
|
|
||||||
|
times = for h <- 0..2, do: DateTime.add(base, h * 3600, :second)
|
||||||
|
|
||||||
|
Enum.each(times, fn t ->
|
||||||
|
Propagation.replace_scores(
|
||||||
|
[%{lat: mid_lat, lon: mid_lon, valid_time: t, band_mhz: 10_000, score: 30, factors: nil}],
|
||||||
|
t
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, lv, _html} =
|
||||||
|
live(conn, ~p"/path?source=32.5,-97.0&destination=33.5,-97.0&band=10000")
|
||||||
|
|
||||||
|
initial = render(lv)
|
||||||
|
assert initial =~ "Propagation Forecast"
|
||||||
|
assert initial =~ ~r/Best:.*?<span[^>]*>\s*30\s*<\/span>/s
|
||||||
|
|
||||||
|
# Publish a new forecast: same times, new score of 80.
|
||||||
|
Enum.each(times, fn t ->
|
||||||
|
Propagation.replace_scores(
|
||||||
|
[%{lat: mid_lat, lon: mid_lon, valid_time: t, band_mhz: 10_000, score: 80, factors: nil}],
|
||||||
|
t
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
send(lv.pid, {:propagation_updated, times})
|
||||||
|
|
||||||
|
refreshed = render(lv)
|
||||||
|
assert refreshed =~ ~r/Best:.*?<span[^>]*>\s*80\s*<\/span>/s
|
||||||
|
refute refreshed =~ ~r/Best:.*?<span[^>]*>\s*30\s*<\/span>/s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Reference in a new issue