From 987eae0408156e73540fef5d3b85f72309607281 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Thu, 5 Feb 2026 09:25:29 -0600 Subject: [PATCH] fix: improve agent-based live polling with better logging and validation - Added Logger.require for debug logging - Refactored fetch_latest_agent_data to reduce nesting depth - Added debug logging to trace agent assignment detection - Check for nil/stale sensor readings and log appropriately - Use actual reading timestamp instead of current time for data points This will help diagnose why live polling shows 0 values and why Phoenix SNMP disabled messages appear for agent-assigned devices. --- lib/towerops_web/live/graph_live/show.ex | 61 +++++++++++++++++------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/lib/towerops_web/live/graph_live/show.ex b/lib/towerops_web/live/graph_live/show.ex index 8fe8d705..ef8c25a5 100644 --- a/lib/towerops_web/live/graph_live/show.ex +++ b/lib/towerops_web/live/graph_live/show.ex @@ -7,6 +7,8 @@ defmodule ToweropsWeb.GraphLive.Show do alias Towerops.Monitoring alias Towerops.Snmp + require Logger + # Maximum live data points (5 minutes at 1 second intervals) @max_live_points 300 @@ -667,12 +669,18 @@ defmodule ToweropsWeb.GraphLive.Show do timestamp_ms = DateTime.to_unix(now, :millisecond) # Check if device has agent assignment + has_agent = socket.assigns[:has_agent_assignment] || false + + Logger.debug("Live poll for device #{device.name}: has_agent=#{has_agent}") + new_points = - if socket.assigns.has_agent_assignment do + if has_agent do # Use agent-collected data from database + Logger.debug("Using agent-collected data for live polling") fetch_latest_agent_data(socket.assigns, timestamp_ms) else # Do direct SNMP polling + Logger.debug("Using direct SNMP for live polling") client_opts = build_snmp_client_opts(device) poll_sensors_for_live_mode(socket.assigns, client_opts, timestamp_ms) end @@ -696,28 +704,47 @@ defmodule ToweropsWeb.GraphLive.Show do end # Fetch latest sensor data collected by agent - defp fetch_latest_agent_data(assigns, timestamp_ms) do + 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.map(&fetch_sensor_reading/1) |> Enum.reject(&is_nil/1) end + defp fetch_sensor_reading(sensor) do + case Snmp.get_latest_sensor_reading(sensor.id) do + nil -> + Logger.debug("No reading found for sensor #{sensor.id} (#{sensor.sensor_descr})") + nil + + %{value: nil} = reading -> + Logger.debug("Nil value for sensor #{sensor.id} (#{sensor.sensor_descr}) at #{reading.checked_at}") + nil + + reading -> + validate_and_format_reading(sensor, reading) + end + end + + defp validate_and_format_reading(sensor, reading) do + age_seconds = DateTime.diff(DateTime.utc_now(), reading.checked_at, :second) + + if age_seconds > 300 do + Logger.debug("Stale reading for sensor #{sensor.id} (#{sensor.sensor_descr}), age: #{age_seconds}s") + nil + else + reading_timestamp_ms = DateTime.to_unix(reading.checked_at, :millisecond) + + %{ + sensor_id: sensor.id, + label: sensor.sensor_descr, + value: Float.round(reading.value, 1), + timestamp: reading_timestamp_ms + } + end + end + # Build SNMP client options defp build_snmp_client_opts(device) do snmp_config = Devices.get_snmp_config(device)