From 53d1a643b95b067438bbc48934069d761785ec91 Mon Sep 17 00:00:00 2001 From: Rory& Date: Sat, 20 Jan 2024 16:12:11 +0100 Subject: Add graph, add start/stop/kill/restart --- SystemdCtl.Client/Pages/Graphs/TimelineGraph.razor | 70 ++++++++++++++++++++++ SystemdCtl.Client/Pages/ServiceManage.razor | 50 ++++++++++++---- 2 files changed, 110 insertions(+), 10 deletions(-) create mode 100644 SystemdCtl.Client/Pages/Graphs/TimelineGraph.razor (limited to 'SystemdCtl.Client/Pages') diff --git a/SystemdCtl.Client/Pages/Graphs/TimelineGraph.razor b/SystemdCtl.Client/Pages/Graphs/TimelineGraph.razor new file mode 100644 index 0000000..9c53986 --- /dev/null +++ b/SystemdCtl.Client/Pages/Graphs/TimelineGraph.razor @@ -0,0 +1,70 @@ +@using ArcaneLibs + + @if (Data.Count > 0) { + $"{x.Key},{x.Value}"))" style="fill:none;stroke:black;stroke-width:3"/> + @* Y min/max labels *@ + + @(ValueFormatter.Invoke(_minValue)) + @(ValueFormatter.Invoke(_maxValue)) + + @* outline *@ + + } + + +@code { + + [Parameter] + public Dictionary Data { get; set; } + + [Parameter] + public int Width { get; set; } = 100; + + [Parameter] + public int Height { get; set; } = 100; + + [Parameter] + public double? MinValue { get; set; } + + [Parameter] + public double? MaxValue { get; set; } + + //value formatter + [Parameter] + public Func ValueFormatter { get; set; } = x => x.ToString("X2"); + + private double _minValue => MinValue ?? (Data.Count > 0 ? Data.Values.Min() : 0); + private double _maxValue => MaxValue ?? (Data.Count > 0 ? Data.Values.Max() : 0); + + private Dictionary _points = []; + + protected override async Task OnParametersSetAsync() { + await RebuildGraph(); + await base.OnParametersSetAsync(); + } + + private async Task RebuildGraph() { + if (Data.Count == 0) return; + _points.Clear(); + var startTime = Data.Keys.Min(x => x).Ticks; + var endTime = Data.Keys.Max(x => x).Ticks; + var minValue = _minValue; + var maxValue = _maxValue; + Console.WriteLine($"Start: {startTime}, End: {endTime}, Min: {minValue}, Max: {maxValue}"); + foreach (var item in Data) { + _points.Add(Map(item.Key.Ticks, startTime, endTime, 0, Width), + Map(item.Value, minValue, maxValue, Height, 0)); + } + } + + public static double Map( + double value, + double originalStart, + double originalEnd, + double newStart, + double newEnd) { + double num = (newEnd - newStart) / (originalEnd - originalStart); + return newStart + (value - originalStart) * num; + } + +} \ No newline at end of file diff --git a/SystemdCtl.Client/Pages/ServiceManage.razor b/SystemdCtl.Client/Pages/ServiceManage.razor index 851f1b6..39be89e 100644 --- a/SystemdCtl.Client/Pages/ServiceManage.razor +++ b/SystemdCtl.Client/Pages/ServiceManage.razor @@ -6,6 +6,8 @@ @using SystemdCtl.Client.Abstractions @using System.Diagnostics.Metrics @using System.Text.Json +@using ArcaneLibs +@using SystemdCtl.Client.Pages.Graphs @* @attribute [StreamRendering] *@ @rendermode InteractiveWebAssembly @inject NavigationManager NavigationManager @@ -33,6 +35,19 @@ +
+
+

Statistics

+
+
+

CPU usage: @ServiceInfo?.CpuUsagePercent.ToString("P2")

+ +

Memory usage: @Util.SI_BytesToString(ServiceInfo?.MemoryCurrent ?? 0) (peak: @Util.SI_BytesToString(ServiceInfo?.MemoryPeak ?? 0))

+ +
+
+
+
@* //simple log view *@
@@ -72,17 +87,22 @@ private async Task Run() { if (!IsClient) return; - int history = 100; - LogLines.Clear(); GetServiceDataTask(); + GetLogs(); + } + + private JsonSerializerOptions jso = new() { + DefaultBufferSize = 1, + AllowTrailingCommas = true + }; + + private async Task GetLogs() { + int history = 100; var Http = new StreamingHttpClient() { BaseAddress = new Uri(NavigationManager.BaseUri) }; - var jso = new JsonSerializerOptions() { - DefaultBufferSize = 1, - AllowTrailingCommas = true - }; + + LogLines.Clear(); var _items = Http.GetAsyncEnumerableFromJsonAsync($"/api/unit/{ServiceName}/logs?contextLines={history}", jso); await foreach (var item in _items) { - // LogLines.RemoveAll(x=>x.SystemdInvocationId != item.SystemdInvocationId); LogLines.Add(item); if (LogLines.Count > history) { LogLines.RemoveAt(0); @@ -95,10 +115,17 @@ private async Task GetServiceDataTask() { if (!IsClient) return; var Http = new StreamingHttpClient() { BaseAddress = new Uri(NavigationManager.BaseUri) }; - while (true) { - ServiceInfo = await Http.GetFromJsonAsync($"/api/unit/{ServiceName}/data"); + + var stream = Http.GetAsyncEnumerableFromJsonAsync($"/api/unit/{ServiceName}/dataStream", jso); + await foreach (var item in stream) { + ServiceInfo = item; + CpuUsageHistory.Add(ServiceInfo.Timestamp, ServiceInfo.CpuUsagePercent); + MemoryUsageHistory.Add(ServiceInfo.Timestamp, ServiceInfo.MemoryCurrent); + CpuUsageHistory = CpuUsageHistory.Where((x, y) => x.Key > DateTime.Now.AddMinutes(-1)) + .OrderBy(x => x.Key).ToDictionary(); + MemoryUsageHistory = MemoryUsageHistory.Where((x, y) => x.Key > DateTime.Now.AddMinutes(-1)).OrderBy(x => x.Key).ToDictionary(); + StateHasChanged(); - await Task.Delay(TimeSpan.FromSeconds(5)); } } @@ -130,4 +157,7 @@ await Http.GetAsync($"/api/unit/{ServiceName}/kill", null); } + public Dictionary CpuUsageHistory { get; set; } = []; + public Dictionary MemoryUsageHistory { get; set; } = []; + } \ No newline at end of file -- cgit 1.4.1