diff --git a/lib/towerops_web/live/graph_live/show.ex b/lib/towerops_web/live/graph_live/show.ex index 99c79291..8fe8d705 100644 --- a/lib/towerops_web/live/graph_live/show.ex +++ b/lib/towerops_web/live/graph_live/show.ex @@ -2,6 +2,7 @@ defmodule ToweropsWeb.GraphLive.Show do @moduledoc false use ToweropsWeb, :live_view + alias Towerops.Agents alias Towerops.Devices alias Towerops.Monitoring alias Towerops.Snmp @@ -18,46 +19,64 @@ defmodule ToweropsWeb.GraphLive.Show do def handle_params(%{"id" => device_id, "sensor_type" => sensor_type} = params, _, socket) do organization = socket.assigns.current_scope.organization - # Verify device exists and user has access - case Devices.get_device(device_id) do - nil -> + with {:ok, device} <- find_device(device_id), + :ok <- verify_device_access(device, organization) do + socket = initialize_graph_view(socket, device_id, sensor_type, params) + {:noreply, socket} + else + {:error, :not_found} -> {:noreply, socket |> put_flash(:error, "Device not found") |> push_navigate(to: ~p"/devices")} - device -> - # Check if device belongs to current organization - if device.organization_id == organization.id do - maybe_subscribe_to_device(socket, device_id) - - range = Map.get(params, "range", "24h") - interface_id = Map.get(params, "interface_id") - sensor_id = Map.get(params, "sensor_id") - storage_id = Map.get(params, "storage_id") - - socket = - socket - |> assign(:device_id, device_id) - |> assign(:sensor_type, sensor_type) - |> assign(:range, range) - |> assign(:interface_id, interface_id) - |> assign(:sensor_id, sensor_id) - |> assign(:storage_id, storage_id) - |> assign(:is_live_mode, range == "live") - |> maybe_start_live_polling() - |> load_graph_data() - - {:noreply, socket} - else - {:noreply, - socket - |> put_flash(:error, "You don't have access to this device") - |> push_navigate(to: ~p"/devices")} - end + {:error, :access_denied} -> + {:noreply, + socket + |> put_flash(:error, "You don't have access to this device") + |> push_navigate(to: ~p"/devices")} end end + defp find_device(device_id) do + case Devices.get_device(device_id) do + nil -> {:error, :not_found} + device -> {:ok, device} + end + end + + defp verify_device_access(device, organization) do + if device.organization_id == organization.id do + :ok + else + {:error, :access_denied} + end + end + + defp initialize_graph_view(socket, device_id, sensor_type, params) do + maybe_subscribe_to_device(socket, device_id) + + range = Map.get(params, "range", "24h") + interface_id = Map.get(params, "interface_id") + sensor_id = Map.get(params, "sensor_id") + storage_id = Map.get(params, "storage_id") + + # Check if device has agent assignment + has_agent = Agents.get_device_assignment(device_id) != nil + + socket + |> assign(:device_id, device_id) + |> assign(:sensor_type, sensor_type) + |> assign(:range, range) + |> assign(:interface_id, interface_id) + |> assign(:sensor_id, sensor_id) + |> assign(:storage_id, storage_id) + |> assign(:is_live_mode, range == "live") + |> assign(:has_agent_assignment, has_agent) + |> maybe_start_live_polling() + |> load_graph_data() + end + defp maybe_subscribe_to_device(socket, device_id) do _ = if connected?(socket) do @@ -644,12 +663,19 @@ defmodule ToweropsWeb.GraphLive.Show do # Poll sensors and update buffer defp poll_and_update_buffer(socket, device) do - client_opts = build_snmp_client_opts(device) now = DateTime.utc_now() timestamp_ms = DateTime.to_unix(now, :millisecond) - # Poll sensors based on graph type - new_points = poll_sensors_for_live_mode(socket.assigns, client_opts, timestamp_ms) + # Check if device has agent assignment + new_points = + if socket.assigns.has_agent_assignment do + # Use agent-collected data from database + fetch_latest_agent_data(socket.assigns, timestamp_ms) + else + # Do direct SNMP polling + client_opts = build_snmp_client_opts(device) + poll_sensors_for_live_mode(socket.assigns, client_opts, timestamp_ms) + end # Update buffer with new points (rolling window) {updated_buffer, point_count} = @@ -669,6 +695,29 @@ defmodule ToweropsWeb.GraphLive.Show do }) end + # Fetch latest sensor data collected by agent + defp fetch_latest_agent_data(assigns, timestamp_ms) do + sensors = get_sensors_for_live_mode(assigns) + + sensors + |> Enum.map(fn sensor -> + # Get the most recent sensor reading from the database + case Snmp.get_latest_sensor_reading(sensor.id) do + nil -> + nil + + reading -> + %{ + sensor_id: sensor.id, + label: sensor.sensor_descr, + value: Float.round(reading.value || 0.0, 1), + timestamp: timestamp_ms + } + end + end) + |> Enum.reject(&is_nil/1) + end + # Build SNMP client options defp build_snmp_client_opts(device) do snmp_config = Devices.get_snmp_config(device)