defmodule ToweropsWeb.GraphLive.Show do @moduledoc false use ToweropsWeb, :live_view alias Towerops.Equipment alias Towerops.Snmp @impl true def mount(_params, _session, socket) do {:ok, socket} end @impl true def handle_params(%{"id" => equipment_id, "sensor_type" => sensor_type} = params, _, socket) do if connected?(socket) do Phoenix.PubSub.subscribe(Towerops.PubSub, "equipment:#{equipment_id}") end range = Map.get(params, "range", "24h") socket = socket |> assign(:equipment_id, equipment_id) |> assign(:sensor_type, sensor_type) |> assign(:range, range) |> load_graph_data() {:noreply, socket} end @impl true def handle_event("change_range", %{"range" => range}, socket) do {:noreply, push_patch(socket, to: ~p"/orgs/#{socket.assigns.current_organization.slug}/equipment/#{socket.assigns.equipment_id}/graph/#{socket.assigns.sensor_type}?range=#{range}" )} end # Private functions defp load_graph_data(socket) do equipment_id = socket.assigns.equipment_id sensor_type = socket.assigns.sensor_type range = socket.assigns.range equipment = Equipment.get_equipment!(equipment_id) sensor_types = get_sensor_types_for_chart(sensor_type) chart_data = load_sensor_chart_data(equipment_id, sensor_types, range) {title, unit, auto_scale} = get_chart_config(sensor_type) socket |> assign(:equipment, equipment) |> assign(:page_title, "#{equipment.name} - #{title}") |> assign(:chart_title, title) |> assign(:chart_data, chart_data) |> assign(:unit, unit) |> assign(:auto_scale, auto_scale) end defp get_sensor_types_for_chart("processors"), do: ["cpu_load"] defp get_sensor_types_for_chart("memory"), do: ["memory_usage"] defp get_sensor_types_for_chart("storage"), do: ["disk_usage"] defp get_sensor_types_for_chart("temperature"), do: ["temperature", "cpu_temperature", "celsius"] defp get_sensor_types_for_chart("voltage"), do: ["voltage", "volts"] defp get_sensor_types_for_chart(_), do: [] defp get_chart_config("processors"), do: {"Processor Usage", "%", false} defp get_chart_config("memory"), do: {"Memory Usage", "%", false} defp get_chart_config("storage"), do: {"Storage Usage", "%", false} defp get_chart_config("temperature"), do: {"Temperature", "°C", true} defp get_chart_config("voltage"), do: {"Voltage", "V", true} defp get_chart_config(_), do: {"Sensor Data", "", false} defp load_sensor_chart_data(equipment_id, sensor_types, range) do case Snmp.get_device_with_associations(equipment_id) do nil -> nil device -> # Find all sensors of the specified types sensors = device.sensors |> Enum.filter(&(&1.sensor_type in sensor_types)) |> Enum.sort_by(& &1.sensor_index) if Enum.empty?(sensors) do nil else # Get readings based on range since = get_datetime_from_range(range) datasets = Enum.map(sensors, fn sensor -> readings = sensor.id |> Snmp.get_sensor_readings( since: since, limit: get_limit_for_range(range) ) |> Enum.reverse() %{ label: sensor.sensor_descr, data: Enum.map(readings, fn reading -> %{ x: DateTime.to_unix(reading.checked_at, :millisecond), y: if(reading.value, do: Float.round(reading.value, 1)) } end) } end) Jason.encode!(%{datasets: datasets}) end end end defp get_datetime_from_range("1h"), do: DateTime.add(DateTime.utc_now(), -1, :hour) defp get_datetime_from_range("6h"), do: DateTime.add(DateTime.utc_now(), -6, :hour) defp get_datetime_from_range("12h"), do: DateTime.add(DateTime.utc_now(), -12, :hour) defp get_datetime_from_range("24h"), do: DateTime.add(DateTime.utc_now(), -24, :hour) defp get_datetime_from_range("7d"), do: DateTime.add(DateTime.utc_now(), -7, :day) defp get_datetime_from_range("30d"), do: DateTime.add(DateTime.utc_now(), -30, :day) defp get_datetime_from_range(_), do: DateTime.add(DateTime.utc_now(), -24, :hour) defp get_limit_for_range("1h"), do: 360 defp get_limit_for_range("6h"), do: 720 defp get_limit_for_range("12h"), do: 1440 defp get_limit_for_range("24h"), do: 2880 defp get_limit_for_range("7d"), do: 10_080 defp get_limit_for_range("30d"), do: 43_200 defp get_limit_for_range(_), do: 2880 end