From fd163bf46b0dc0686efaf37979400b72870027b5 Mon Sep 17 00:00:00 2001
From: Ruslan Rabadanov <ruslanrabadanov2101@gmail.com>
Date: Fri, 7 Mar 2025 20:55:23 +0100
Subject: [PATCH] FE-6 Fix Y axis for protocol chart; move component logic to
 chart service

---
 .../dashboard-chart-frames.component.ts       |  45 ++----
 ...hboard-chart-information-rate.component.ts |  44 ++----
 .../dashboard-chart-protocol.component.ts     |  64 ++-------
 src/app/interceptor/mock-interceptor.ts       |   4 +-
 src/app/service/.gitkeep                      |   0
 src/app/service/chart.service.ts              | 131 ++++++++++++++++++
 6 files changed, 158 insertions(+), 130 deletions(-)
 delete mode 100644 src/app/service/.gitkeep
 create mode 100644 src/app/service/chart.service.ts

diff --git a/src/app/components/dashboard-chart-frames/dashboard-chart-frames.component.ts b/src/app/components/dashboard-chart-frames/dashboard-chart-frames.component.ts
index b713d95..7c79268 100644
--- a/src/app/components/dashboard-chart-frames/dashboard-chart-frames.component.ts
+++ b/src/app/components/dashboard-chart-frames/dashboard-chart-frames.component.ts
@@ -15,6 +15,7 @@ import { MatFormField, MatLabel } from '@angular/material/form-field';
 import { MatInput } from '@angular/material/input';
 import { MatSlider, MatSliderThumb } from '@angular/material/slider';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { ChartService } from '../../service/chart.service';
 
 @Component({
   selector: 'app-dashboard-chart-frames',
@@ -34,40 +35,20 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 })
 export class DashboardChartFramesComponent {
   readonly #dashboardApi = inject(DashboardApi);
-  recordsLimit = input<number>(20);
+  readonly #chartService = inject(ChartService);
+  recordsLimit = input<number>(60);
   recordsInterval = signal<number>(1);
   framesHistory = signal<ChartFrames[]>([]);
   data = computed(() => {
     const frames = this.framesHistory();
     if (!frames) return;
-    return {
-      labels: this.getFrameLabels().map(date => date.toLocaleTimeString()),
-      datasets: [
-        {
-          label: 'Valid Frames',
-          data: frames.map(frame => frame.valid),
-          fill: true,
-          backgroundColor: 'rgb(76, 175, 80, 0.2)',
-          tension: 0.4,
-          borderColor: '#4CAF50',
-        },
-        {
-          label: 'Invalid Frames',
-          data: frames.map(frame => frame.invalid),
-          fill: true,
-          tension: 0.4,
-          borderColor: '#f34d52',
-          backgroundColor: 'rgb(243, 77, 82, 0.2)',
-        },
-      ],
-    };
+    return this.#chartService.getFramesChartDataConfig(
+      frames,
+      this.recordsInterval()
+    );
   });
 
-  options = {
-    animation: {
-      duration: 0,
-    },
-  };
+  readonly options = this.#chartService.getDefaultChartOptions();
 
   private stopFetching = new Subject();
 
@@ -93,14 +74,4 @@ export class DashboardChartFramesComponent {
         });
     });
   }
-
-  private getFrameLabels(): Date[] {
-    const labelsNumber = this.framesHistory().length;
-    const now = new Date().getTime();
-    return Array.from(
-      { length: labelsNumber },
-      (_, index) =>
-        new Date(now - (labelsNumber - index) * 1000 * this.recordsInterval())
-    );
-  }
 }
diff --git a/src/app/components/dashboard-chart-information-rate/dashboard-chart-information-rate.component.ts b/src/app/components/dashboard-chart-information-rate/dashboard-chart-information-rate.component.ts
index c60ee31..99d01f1 100644
--- a/src/app/components/dashboard-chart-information-rate/dashboard-chart-information-rate.component.ts
+++ b/src/app/components/dashboard-chart-information-rate/dashboard-chart-information-rate.component.ts
@@ -9,13 +9,13 @@ import {
 } from '@angular/core';
 import { UIChart } from 'primeng/chart';
 import { DashboardApi } from '../../api/dashboard.api';
-import { ChartFrames } from '../../models/chart-frames';
 import { interval, shareReplay, Subject, switchMap, takeUntil } from 'rxjs';
 import { ChartInformationRate } from '../../models/chart-information-rate';
 import { MatFormField, MatLabel } from '@angular/material/form-field';
 import { MatInput } from '@angular/material/input';
 import { MatSlider, MatSliderThumb } from '@angular/material/slider';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { ChartService } from '../../service/chart.service';
 
 @Component({
   selector: 'app-dashboard-chart-information-rate',
@@ -35,38 +35,20 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 })
 export class DashboardChartInformationRateComponent {
   readonly #dashboardApi = inject(DashboardApi);
-  recordsLimit = input<number>(20);
+  readonly #chartService = inject(ChartService);
+  recordsLimit = input<number>(60);
   recordsInterval = signal<number>(1);
   irHistory = signal<ChartInformationRate[]>([]);
   data = computed(() => {
     const informationRates = this.irHistory();
     if (!informationRates) return;
-    return {
-      labels: this.getFrameLabels().map(date => date.toLocaleTimeString()),
-      datasets: [
-        {
-          label: 'Current IR',
-          data: informationRates.map(ir => ir.current),
-          fill: true,
-          backgroundColor: 'rgba(144,147,239,0.32)',
-          tension: 0.4,
-          borderColor: '#5b70f8',
-        },
-        {
-          label: 'Average IR',
-          data: informationRates.map(ir => ir.average),
-          tension: 0.4,
-          borderColor: '#535353',
-        },
-      ],
-    };
+    return this.#chartService.getIRChartDataConfig(
+      informationRates,
+      this.recordsInterval()
+    );
   });
 
-  options = {
-    animation: {
-      duration: 0,
-    },
-  };
+  readonly options = this.#chartService.getDefaultChartOptions();
 
   private stopFetching = new Subject();
 
@@ -93,14 +75,4 @@ export class DashboardChartInformationRateComponent {
         });
     });
   }
-
-  private getFrameLabels(): Date[] {
-    const labelsNumber = this.irHistory().length;
-    const now = new Date().getTime();
-    return Array.from(
-      { length: labelsNumber },
-      (_, index) =>
-        new Date(now - (labelsNumber - index) * 1000 * this.recordsInterval())
-    );
-  }
 }
diff --git a/src/app/components/dashboard-chart-protocol/dashboard-chart-protocol.component.ts b/src/app/components/dashboard-chart-protocol/dashboard-chart-protocol.component.ts
index 39c14d3..aae9f5d 100644
--- a/src/app/components/dashboard-chart-protocol/dashboard-chart-protocol.component.ts
+++ b/src/app/components/dashboard-chart-protocol/dashboard-chart-protocol.component.ts
@@ -13,9 +13,9 @@ import { MatInput } from '@angular/material/input';
 import { MatSlider, MatSliderThumb } from '@angular/material/slider';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { DashboardApi } from '../../api/dashboard.api';
-import { ChartInformationRate } from '../../models/chart-information-rate';
-import { interval, shareReplay, Subject, switchMap, takeUntil } from 'rxjs';
 import { ChartProtocol } from '../../models/chart-protocol';
+import { interval, shareReplay, Subject, switchMap, takeUntil } from 'rxjs';
+import { ChartService } from '../../service/chart.service';
 
 @Component({
   selector: 'app-dashboard-chart-protocol',
@@ -35,57 +35,21 @@ import { ChartProtocol } from '../../models/chart-protocol';
 })
 export class DashboardChartProtocolComponent {
   readonly #dashboardApi = inject(DashboardApi);
+  readonly #chartService = inject(ChartService);
   protocolName = input.required<string>();
-  recordsLimit = input<number>(20);
+  recordsLimit = input<number>(60);
   recordsInterval = signal<number>(1);
   protocolHistory = signal<ChartProtocol[]>([]);
   data = computed(() => {
     const protocols = this.protocolHistory();
     if (!protocols) return;
-    console.log(protocols);
-    return {
-      labels: this.getFrameLabels().map(date => date.toLocaleTimeString()),
-      datasets: [
-        {
-          label: 'Bytes',
-          data: protocols.map(protocol => protocol.bytes),
-          yAxisId: 'y',
-          tension: 0.4,
-          borderColor: '#4464e3',
-        },
-        // @todo: fix Y1 axis
-        {
-          label: 'Packets',
-          data: protocols.map(protocol => protocol.packets),
-          yAxisId: 'y1',
-          tension: 0.4,
-          borderColor: '#61aa48',
-        },
-      ],
-    };
+    return this.#chartService.getProtocolChartDataConfig(
+      protocols,
+      this.recordsInterval()
+    );
   });
 
-  options = {
-    animation: {
-      duration: 0,
-    },
-    stacked: false,
-    scales: {
-      y: {
-        type: 'linear',
-        display: true,
-        position: 'left',
-      },
-      y1: {
-        type: 'linear',
-        display: true,
-        position: 'right',
-        grid: {
-          drawOnChartArea: false,
-        },
-      },
-    },
-  };
+  readonly options = this.#chartService.getMultipleYAxesChartOptions();
 
   private stopFetching = new Subject();
 
@@ -114,14 +78,4 @@ export class DashboardChartProtocolComponent {
         });
     });
   }
-
-  private getFrameLabels(): Date[] {
-    const labelsNumber = this.protocolHistory().length;
-    const now = new Date().getTime();
-    return Array.from(
-      { length: labelsNumber },
-      (_, index) =>
-        new Date(now - (labelsNumber - index) * 1000 * this.recordsInterval())
-    );
-  }
 }
diff --git a/src/app/interceptor/mock-interceptor.ts b/src/app/interceptor/mock-interceptor.ts
index 3947067..0ce7c4c 100644
--- a/src/app/interceptor/mock-interceptor.ts
+++ b/src/app/interceptor/mock-interceptor.ts
@@ -7,7 +7,7 @@ import { ChartInformationRate } from '../models/chart-information-rate';
 import { ChartProtocol } from '../models/chart-protocol';
 
 export const MockInterceptor: HttpInterceptorFn = (req, next) => {
-  const { url, method } = req;
+  const { url } = req;
 
   for (const element of urls) {
     let body = (element.json() as any).default;
@@ -52,7 +52,7 @@ function getRandomProtocolRecord(): { data: ChartProtocol } {
   return {
     data: {
       packets: packets,
-      bytes: packets * Math.floor(Math.random() * 512),
+      bytes: packets * Math.floor(Math.random() * Math.sqrt(packets) * 16),
     },
   };
 }
diff --git a/src/app/service/.gitkeep b/src/app/service/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/src/app/service/chart.service.ts b/src/app/service/chart.service.ts
new file mode 100644
index 0000000..861c9e0
--- /dev/null
+++ b/src/app/service/chart.service.ts
@@ -0,0 +1,131 @@
+import { Injectable } from '@angular/core';
+import { ChartInformationRate } from '../models/chart-information-rate';
+import { ChartFrames } from '../models/chart-frames';
+import { ChartProtocol } from '../models/chart-protocol';
+
+@Injectable({
+  providedIn: 'root',
+})
+export class ChartService {
+  getChartLabels(
+    labelsNumber: number,
+    labelTimeInterval: number,
+    labelsStep: number = 5
+  ): string[] {
+    const now = new Date().getTime();
+    return Array.from({ length: labelsNumber }, (_, index) => {
+      if (index % labelsStep && index != 0 && index != labelsNumber - 1) {
+        return '';
+      }
+      return new Date(
+        now - (labelsNumber - index) * 1000 * labelTimeInterval
+      ).toLocaleTimeString();
+    });
+  }
+
+  getIRChartDataConfig(
+    informationRates: ChartInformationRate[],
+    recordsInterval: number
+  ) {
+    return {
+      labels: this.getChartLabels(informationRates.length, recordsInterval),
+      datasets: [
+        {
+          label: 'Current IR',
+          data: informationRates.map(ir => ir.current),
+          fill: true,
+          backgroundColor: 'rgba(144,147,239,0.32)',
+          tension: 0.4,
+          borderColor: '#5b70f8',
+        },
+        {
+          label: 'Average IR',
+          data: informationRates.map(ir => ir.average),
+          tension: 0.4,
+          borderColor: '#535353',
+        },
+      ],
+    };
+  }
+
+  getFramesChartDataConfig(frames: ChartFrames[], recordsInterval: number) {
+    return {
+      labels: this.getChartLabels(frames.length, recordsInterval),
+      datasets: [
+        {
+          label: 'Valid Frames',
+          data: frames.map(frame => frame.valid),
+          fill: true,
+          backgroundColor: 'rgb(76, 175, 80, 0.2)',
+          tension: 0.4,
+          borderColor: '#4CAF50',
+        },
+        {
+          label: 'Invalid Frames',
+          data: frames.map(frame => frame.invalid),
+          fill: true,
+          tension: 0.4,
+          borderColor: '#f34d52',
+          backgroundColor: 'rgb(243, 77, 82, 0.2)',
+        },
+      ],
+    };
+  }
+
+  getProtocolChartDataConfig(
+    protocols: ChartProtocol[],
+    recordsInterval: number
+  ) {
+    return {
+      labels: this.getChartLabels(protocols.length, recordsInterval),
+      datasets: [
+        {
+          label: 'Bytes',
+          fill: false,
+          yAxisID: 'y',
+          tension: 0.4,
+          data: protocols.map(protocol => protocol.bytes),
+        },
+        {
+          label: 'Packets',
+          fill: false,
+          yAxisID: 'y1',
+          tension: 0.4,
+          data: protocols.map(protocol => protocol.packets),
+        },
+      ],
+    };
+  }
+
+  getDefaultChartOptions() {
+    return {
+      animation: {
+        duration: 0,
+      },
+    };
+  }
+
+  getMultipleYAxesChartOptions() {
+    return {
+      animation: {
+        duration: 0,
+      },
+      stacked: false,
+      scales: {
+        y: {
+          type: 'linear',
+          display: true,
+          position: 'left',
+        },
+        y1: {
+          type: 'linear',
+          display: true,
+          position: 'right',
+          grid: {
+            drawOnChartArea: false,
+          },
+        },
+      },
+    };
+  }
+}
-- 
GitLab