Fix rover planner: manual Calculate button, non-blocking computation
- Add explicit "Calculate Coverage" button instead of auto-triggering - Move coverage computation to background Task so LV stays responsive (users can keep adding/removing stations while it computes) - Remove all auto-compute triggers from station add/remove/band change - URL loading resolves stations but doesn't auto-calculate - Button shows "Finding best roving locations..." spinner while computing
This commit is contained in:
parent
961ae0782a
commit
a13d5a47f2
1 changed files with 69 additions and 35 deletions
|
|
@ -80,17 +80,24 @@ defmodule MicrowavepropWeb.RoverLive do
|
||||||
idx = String.to_integer(idx_str)
|
idx = String.to_integer(idx_str)
|
||||||
stations = List.delete_at(socket.assigns.stations, idx)
|
stations = List.delete_at(socket.assigns.stations, idx)
|
||||||
socket = assign(socket, stations: stations, coverage: [], selected_grid: nil)
|
socket = assign(socket, stations: stations, coverage: [], selected_grid: nil)
|
||||||
if length(stations) >= 2, do: send(self(), :compute_coverage)
|
|
||||||
socket = push_event(socket, "stations_updated", %{stations: station_data(stations)})
|
socket = push_event(socket, "stations_updated", %{stations: station_data(stations)})
|
||||||
{:noreply, push_url(socket)}
|
{:noreply, push_url(socket)}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("select_band", %{"band" => band_str}, socket) do
|
def handle_event("select_band", %{"band" => band_str}, socket) do
|
||||||
socket = assign(socket, band: String.to_integer(band_str), coverage: [], selected_grid: nil)
|
socket = assign(socket, band: String.to_integer(band_str), coverage: [], selected_grid: nil)
|
||||||
if length(socket.assigns.stations) >= 2, do: send(self(), :compute_coverage)
|
|
||||||
{:noreply, push_url(socket)}
|
{:noreply, push_url(socket)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("calculate", _params, socket) do
|
||||||
|
if length(socket.assigns.stations) >= 2 and not socket.assigns.computing do
|
||||||
|
send(self(), :compute_coverage)
|
||||||
|
{:noreply, assign(socket, computing: true)}
|
||||||
|
else
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def handle_event("select_grid", %{"grid" => grid}, socket) do
|
def handle_event("select_grid", %{"grid" => grid}, socket) do
|
||||||
detail = Enum.find(socket.assigns.coverage, &(&1.grid == grid))
|
detail = Enum.find(socket.assigns.coverage, &(&1.grid == grid))
|
||||||
{:noreply, assign(socket, selected_grid: detail)}
|
{:noreply, assign(socket, selected_grid: detail)}
|
||||||
|
|
@ -112,7 +119,6 @@ defmodule MicrowavepropWeb.RoverLive do
|
||||||
|> assign(stations: stations, resolving: false)
|
|> assign(stations: stations, resolving: false)
|
||||||
|> push_event("stations_updated", %{stations: station_data(stations)})
|
|> push_event("stations_updated", %{stations: station_data(stations)})
|
||||||
|
|
||||||
if length(stations) >= 2, do: send(self(), :compute_coverage)
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -123,7 +129,6 @@ defmodule MicrowavepropWeb.RoverLive do
|
||||||
stations = socket.assigns.stations ++ [station]
|
stations = socket.assigns.stations ++ [station]
|
||||||
socket = assign(socket, stations: stations, resolving: false)
|
socket = assign(socket, stations: stations, resolving: false)
|
||||||
socket = push_event(socket, "stations_updated", %{stations: station_data(stations)})
|
socket = push_event(socket, "stations_updated", %{stations: station_data(stations)})
|
||||||
if length(stations) >= 2, do: send(self(), :compute_coverage)
|
|
||||||
{:noreply, push_url(socket)}
|
{:noreply, push_url(socket)}
|
||||||
|
|
||||||
{:error, _reason} ->
|
{:error, _reason} ->
|
||||||
|
|
@ -134,11 +139,52 @@ defmodule MicrowavepropWeb.RoverLive do
|
||||||
def handle_info(:compute_coverage, socket) do
|
def handle_info(:compute_coverage, socket) do
|
||||||
stations = socket.assigns.stations
|
stations = socket.assigns.stations
|
||||||
band_mhz = socket.assigns.band
|
band_mhz = socket.assigns.band
|
||||||
|
|
||||||
|
if length(stations) < 2 do
|
||||||
|
{:noreply, socket}
|
||||||
|
else
|
||||||
|
# Run in a background task so the LV stays responsive
|
||||||
|
pid = self()
|
||||||
|
|
||||||
|
Task.start(fn ->
|
||||||
|
result = do_compute_coverage(stations, band_mhz)
|
||||||
|
send(pid, {:coverage_result, result})
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:noreply, assign(socket, computing: true)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_info({:coverage_result, coverage}, socket) do
|
||||||
|
stations = socket.assigns.stations
|
||||||
|
|
||||||
|
grid_data =
|
||||||
|
Enum.map(coverage, fn c ->
|
||||||
|
%{
|
||||||
|
grid: c.grid,
|
||||||
|
lat: c.lat,
|
||||||
|
lon: c.lon,
|
||||||
|
coverage_score: c.coverage_score,
|
||||||
|
stations_in_range: c.stations_in_range,
|
||||||
|
workable_count: c.workable_count,
|
||||||
|
total_stations: length(stations),
|
||||||
|
prop_score: c.prop_score,
|
||||||
|
best_hour: c.best_hour
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
socket =
|
||||||
|
socket
|
||||||
|
|> assign(coverage: coverage, computing: false, selected_grid: nil)
|
||||||
|
|> push_event("coverage_updated", %{grids: grid_data})
|
||||||
|
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_compute_coverage(stations, band_mhz) do
|
||||||
band_config = BandConfig.get(band_mhz) || BandConfig.get(10_000)
|
band_config = BandConfig.get(band_mhz) || BandConfig.get(10_000)
|
||||||
max_range = band_config.extended_range_km || 500
|
max_range = band_config.extended_range_km || 500
|
||||||
|
|
||||||
socket = assign(socket, computing: true)
|
|
||||||
|
|
||||||
# Find the bounding box around all stations, expanded by max range
|
# Find the bounding box around all stations, expanded by max range
|
||||||
lats = Enum.map(stations, & &1.lat)
|
lats = Enum.map(stations, & &1.lat)
|
||||||
lons = Enum.map(stations, & &1.lon)
|
lons = Enum.map(stations, & &1.lon)
|
||||||
|
|
@ -180,28 +226,7 @@ defmodule MicrowavepropWeb.RoverLive do
|
||||||
end)
|
end)
|
||||||
|> Enum.sort_by(& &1.coverage_score, :desc)
|
|> Enum.sort_by(& &1.coverage_score, :desc)
|
||||||
|
|
||||||
# Push to JS for rendering
|
coverage
|
||||||
grid_data =
|
|
||||||
Enum.map(coverage, fn c ->
|
|
||||||
%{
|
|
||||||
grid: c.grid,
|
|
||||||
lat: c.lat,
|
|
||||||
lon: c.lon,
|
|
||||||
coverage_score: c.coverage_score,
|
|
||||||
stations_in_range: c.stations_in_range,
|
|
||||||
workable_count: c.workable_count,
|
|
||||||
total_stations: length(stations),
|
|
||||||
prop_score: c.prop_score,
|
|
||||||
best_hour: c.best_hour
|
|
||||||
}
|
|
||||||
end)
|
|
||||||
|
|
||||||
socket =
|
|
||||||
socket
|
|
||||||
|> assign(coverage: coverage, computing: false, selected_grid: nil)
|
|
||||||
|> push_event("coverage_updated", %{grids: grid_data})
|
|
||||||
|
|
||||||
{:noreply, socket}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# ── Coverage Scoring ──
|
# ── Coverage Scoring ──
|
||||||
|
|
@ -433,16 +458,25 @@ defmodule MicrowavepropWeb.RoverLive do
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= if @computing do %>
|
<%= if length(@stations) >= 2 do %>
|
||||||
<div class="text-xs text-center py-3">
|
<button
|
||||||
<span class="loading loading-spinner loading-sm"></span>
|
type="button"
|
||||||
<div class="mt-1">Computing coverage areas...</div>
|
phx-click="calculate"
|
||||||
</div>
|
class="btn btn-primary btn-sm w-full"
|
||||||
|
disabled={@computing}
|
||||||
|
>
|
||||||
|
<%= if @computing do %>
|
||||||
|
<span class="loading loading-spinner loading-sm"></span>
|
||||||
|
Finding best roving locations...
|
||||||
|
<% else %>
|
||||||
|
Calculate Coverage
|
||||||
|
<% end %>
|
||||||
|
</button>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= if @stations != [] and !@computing and @coverage == [] do %>
|
<%= if length(@stations) == 1 do %>
|
||||||
<div class="text-xs opacity-50 text-center py-2">
|
<div class="text-xs opacity-50 text-center py-2">
|
||||||
Add at least 2 stations to see coverage
|
Add at least 2 stations to calculate
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue