Make traffic graph header clickable and add dedicated graph page
This commit is contained in:
parent
3643700d73
commit
2212bf9134
2 changed files with 106 additions and 10 deletions
|
|
@ -73,14 +73,24 @@
|
|||
<!-- Overall Traffic Chart -->
|
||||
<%= if @traffic_chart_data do %>
|
||||
<div class="mb-6 bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
|
||||
<div class="px-4 py-3 border-b border-zinc-200 dark:border-zinc-700">
|
||||
<h3 class="text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
Overall Traffic (24 Hours)
|
||||
</h3>
|
||||
<p class="text-xs text-zinc-600 dark:text-zinc-400 mt-1">
|
||||
Combined traffic across all interfaces
|
||||
</p>
|
||||
</div>
|
||||
<.link
|
||||
navigate={
|
||||
~p"/orgs/#{@current_organization.slug}/equipment/#{@equipment.id}/graph/traffic"
|
||||
}
|
||||
class="block px-4 py-3 border-b border-zinc-200 dark:border-zinc-700 hover:bg-zinc-50 dark:hover:bg-zinc-750 transition-colors"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
Overall Traffic (24 Hours)
|
||||
</h3>
|
||||
<p class="text-xs text-zinc-600 dark:text-zinc-400 mt-1">
|
||||
Combined traffic across all interfaces
|
||||
</p>
|
||||
</div>
|
||||
<.icon name="hero-arrow-right" class="h-4 w-4 text-zinc-400" />
|
||||
</div>
|
||||
</.link>
|
||||
<div class="p-4">
|
||||
<div
|
||||
id="traffic-chart"
|
||||
|
|
|
|||
|
|
@ -45,8 +45,14 @@ defmodule ToweropsWeb.GraphLive.Show do
|
|||
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)
|
||||
|
||||
chart_data =
|
||||
if sensor_type == "traffic" do
|
||||
load_traffic_chart_data(equipment_id, range)
|
||||
else
|
||||
sensor_types = get_sensor_types_for_chart(sensor_type)
|
||||
load_sensor_chart_data(equipment_id, sensor_types, range)
|
||||
end
|
||||
|
||||
{title, unit, auto_scale} = get_chart_config(sensor_type)
|
||||
|
||||
|
|
@ -64,6 +70,7 @@ defmodule ToweropsWeb.GraphLive.Show do
|
|||
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("traffic"), do: nil
|
||||
defp get_sensor_types_for_chart(_), do: []
|
||||
|
||||
defp get_chart_config("processors"), do: {"Processor Usage", "%", false}
|
||||
|
|
@ -71,6 +78,7 @@ defmodule ToweropsWeb.GraphLive.Show do
|
|||
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("traffic"), do: {"Overall Traffic", "bps", true}
|
||||
defp get_chart_config(_), do: {"Sensor Data", "", false}
|
||||
|
||||
defp load_sensor_chart_data(equipment_id, sensor_types, range) do
|
||||
|
|
@ -133,4 +141,82 @@ defmodule ToweropsWeb.GraphLive.Show do
|
|||
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
|
||||
|
||||
defp load_traffic_chart_data(equipment_id, range) do
|
||||
case Snmp.get_device_with_associations(equipment_id) do
|
||||
nil ->
|
||||
nil
|
||||
|
||||
device ->
|
||||
if Enum.empty?(device.interfaces) do
|
||||
nil
|
||||
else
|
||||
since = get_datetime_from_range(range)
|
||||
limit = get_limit_for_range(range)
|
||||
|
||||
# Get all stats for all interfaces
|
||||
all_stats =
|
||||
device.interfaces
|
||||
|> Enum.flat_map(fn interface ->
|
||||
stats =
|
||||
interface.id
|
||||
|> Snmp.get_interface_stats(since: since, limit: limit)
|
||||
|> Enum.reverse()
|
||||
|
||||
Enum.map(stats, fn stat ->
|
||||
{stat.checked_at, stat.if_in_octets, stat.if_out_octets}
|
||||
end)
|
||||
end)
|
||||
|> Enum.group_by(&elem(&1, 0))
|
||||
|> Enum.map(fn {timestamp, stats} ->
|
||||
total_in =
|
||||
Enum.reduce(stats, 0, fn {_, in_octets, _}, acc -> acc + (in_octets || 0) end)
|
||||
|
||||
total_out =
|
||||
Enum.reduce(stats, 0, fn {_, _, out_octets}, acc -> acc + (out_octets || 0) end)
|
||||
|
||||
{timestamp, total_in, total_out}
|
||||
end)
|
||||
|> Enum.sort_by(&elem(&1, 0), DateTime)
|
||||
|
||||
if Enum.empty?(all_stats) do
|
||||
nil
|
||||
else
|
||||
# Calculate bits per second from counter differences
|
||||
in_data =
|
||||
all_stats
|
||||
|> Enum.chunk_every(2, 1, :discard)
|
||||
|> Enum.map(fn [{t1, in1, _out1}, {t2, in2, _out2}] ->
|
||||
time_diff = t2 |> DateTime.diff(t1, :second) |> max(1)
|
||||
bps = ((in2 - in1) * 8 / time_diff) |> max(0) |> Float.round(2)
|
||||
|
||||
%{
|
||||
x: DateTime.to_unix(t2, :millisecond),
|
||||
y: bps
|
||||
}
|
||||
end)
|
||||
|
||||
out_data =
|
||||
all_stats
|
||||
|> Enum.chunk_every(2, 1, :discard)
|
||||
|> Enum.map(fn [{t1, _in1, out1}, {t2, _in2, out2}] ->
|
||||
time_diff = t2 |> DateTime.diff(t1, :second) |> max(1)
|
||||
bps = ((out2 - out1) * 8 / time_diff) |> max(0) |> Float.round(2)
|
||||
|
||||
%{
|
||||
x: DateTime.to_unix(t2, :millisecond),
|
||||
y: bps
|
||||
}
|
||||
end)
|
||||
|
||||
datasets = [
|
||||
%{label: "Inbound", data: in_data},
|
||||
%{label: "Outbound", data: out_data}
|
||||
]
|
||||
|
||||
Jason.encode!(%{datasets: datasets})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue