diff --git a/angular-client/src/components/acceleration-graphs/acceleration-graphs.component.ts b/angular-client/src/components/acceleration-graphs/acceleration-graphs.component.ts index bd2e59fa..e5a1786a 100644 --- a/angular-client/src/components/acceleration-graphs/acceleration-graphs.component.ts +++ b/angular-client/src/components/acceleration-graphs/acceleration-graphs.component.ts @@ -41,15 +41,6 @@ export class AccelerationGraphs implements OnInit { y: y1 }); - //limits the data storage to 400 to prevent lag - if (this.xData.length > this.maxDataPoints) { - this.xData = this.xData.slice(1); - } - - if (this.yData.length > this.maxDataPoints) { - this.yData = this.yData.slice(1); - } - //checks if there is a new max this.xMax = Math.max(Math.abs(x1), this.xMax); this.yMax = Math.max(Math.abs(y1), this.yMax); diff --git a/angular-client/src/components/acceleration-over-time-display/acceleration-over-time-display.component.ts b/angular-client/src/components/acceleration-over-time-display/acceleration-over-time-display.component.ts index fda364f2..954ce462 100644 --- a/angular-client/src/components/acceleration-over-time-display/acceleration-over-time-display.component.ts +++ b/angular-client/src/components/acceleration-over-time-display/acceleration-over-time-display.component.ts @@ -10,16 +10,12 @@ import { GraphData } from 'src/utils/types.utils'; }) export default class AccelerationOverTimeDisplay { data: GraphData[] = []; - maxDataPoints = 100; constructor(private storage: Storage) {} ngOnInit() { this.storage.get(IdentifierDataType.ACCELERATION).subscribe((value) => { this.data.push({ x: new Date().getTime(), y: parseInt(value.values[0]) }); - if (this.data.length > 100) { - this.data.shift(); - } }); } } diff --git a/angular-client/src/components/graph/graph.component.ts b/angular-client/src/components/graph/graph.component.ts index dc33dfdc..aa4cac14 100644 --- a/angular-client/src/components/graph/graph.component.ts +++ b/angular-client/src/components/graph/graph.component.ts @@ -1,21 +1,11 @@ import { Component, Input, OnInit } from '@angular/core'; import * as ApexCharts from 'apexcharts'; -import { - ApexAxisChartSeries, - ApexXAxis, - ApexDataLabels, - ApexChart, - ApexMarkers, - ApexGrid, - ApexTooltip, - ApexFill -} from 'ng-apexcharts'; +import { ApexXAxis, ApexDataLabels, ApexChart, ApexMarkers, ApexGrid, ApexTooltip, ApexFill } from 'ng-apexcharts'; import { DialogService } from 'primeng/dynamicdialog'; import { GraphDialog } from '../graph-dialog/graph-dialog.component'; import { GraphData } from 'src/utils/types.utils'; type ChartOptions = { - series: ApexAxisChartSeries; chart: ApexChart; xaxis: ApexXAxis; yaxis: ApexYAxis; @@ -38,14 +28,12 @@ export class GraphComponent implements OnInit { @Input() color!: string; // Must be hex @Input() title?: string; @Input() graphContainerId!: string; + @Input({ required: false }) timeRangeSec!: number; options!: ChartOptions; chart!: ApexCharts; - series: ApexAxisChartSeries = [ - { - name: this.title, - data: [] - } - ]; + timeDiffMs: number = 0; + isSliding: boolean = false; + timeRangeMs: number = 120000; // 2 minutes in ms constructor(public dialogService: DialogService) {} @@ -61,23 +49,37 @@ export class GraphComponent implements OnInit { }; updateChart = () => { - this.series[0].data = this.data; - this.chart.updateSeries(this.series); + this.chart.updateSeries([ + { + name: 'Data Series', + data: Array.from(this.data) + } + ]); + + if (!this.isSliding && this.data.length > 2) { + this.timeDiffMs = this.data[this.data.length - 1].x - this.data[0].x; + } + + if (!this.isSliding && this.timeDiffMs > this.timeRangeMs) { + this.isSliding = true; + this.chart.updateOptions({ + ...this.options, + xaxis: { + ...this.options.xaxis, + range: this.timeRangeMs + } + }); + } + setTimeout(() => { this.updateChart(); - }, 1000); + }, 800); }; ngOnInit(): void { - this.series = [ - { - name: this.title, - data: this.data - } - ]; + this.timeRangeMs = (this.timeRangeSec ?? 120) * 1000; this.options = { - series: [], chart: { id: 'graph', type: 'line', @@ -116,7 +118,14 @@ export class GraphComponent implements OnInit { colors: '#FFFFFF' }, formatter: (value) => { - return '' + new Date(value).getHours() + ':' + new Date(value).getMinutes() + ':' + new Date(value).getSeconds(); + return ( + '' + + new Date(value).getHours() + + ':' + + ((new Date(value).getMinutes() < 10 ? '0' : '') + new Date(value).getMinutes()) + + ':' + + ((new Date(value).getSeconds() < 10 ? '0' : '') + new Date(value).getSeconds()) + ); } }, axisBorder: { @@ -163,7 +172,7 @@ export class GraphComponent implements OnInit { return; } - this.chart = new ApexCharts(chartContainer, this.options); + this.chart = new ApexCharts(chartContainer, { series: [{ data: [] }], ...this.options }); this.chart.render(); this.updateChart(); diff --git a/angular-client/src/components/speed-over-time-display/speed-over-time-display.component.ts b/angular-client/src/components/speed-over-time-display/speed-over-time-display.component.ts index 3a7181ef..bb9ce697 100644 --- a/angular-client/src/components/speed-over-time-display/speed-over-time-display.component.ts +++ b/angular-client/src/components/speed-over-time-display/speed-over-time-display.component.ts @@ -10,15 +10,11 @@ import { GraphData } from 'src/utils/types.utils'; }) export default class SpeedOverTimeDisplay implements OnInit { data: GraphData[] = []; - maxDataPoints = 100; constructor(private storage: Storage) {} ngOnInit() { this.storage.get(IdentifierDataType.SPEED).subscribe((value) => { this.data.push({ x: new Date().getTime(), y: parseInt(value.values[0]) }); - if (this.data.length > 100) { - this.data.shift(); - } }); } } diff --git a/angular-client/src/pages/graph-page/graph-page.component.ts b/angular-client/src/pages/graph-page/graph-page.component.ts index 502fe7f7..6e25c894 100644 --- a/angular-client/src/pages/graph-page/graph-page.component.ts +++ b/angular-client/src/pages/graph-page/graph-page.component.ts @@ -60,12 +60,12 @@ export default class GraphPage implements OnInit { this.clearDataType = () => { if (this.subscription) this.subscription.unsubscribe(); this.selectedDataType.next({ name: '', unit: '' }); - this.selectedDataTypeValuesSubject.next([]); + this.selectedDataTypeValuesSubject = new BehaviorSubject([]); }; this.setSelectedDataType = (dataType: DataType) => { this.selectedDataType.next(dataType); - this.selectedDataTypeValuesSubject.next([]); + this.selectedDataTypeValuesSubject = new BehaviorSubject([]); if (this.realTime) { if (this.subscription) this.subscription.unsubscribe(); const key = dataType.name; diff --git a/angular-client/src/pages/graph-page/graph/graph.component.ts b/angular-client/src/pages/graph-page/graph/graph.component.ts index f999b064..f55cc015 100644 --- a/angular-client/src/pages/graph-page/graph/graph.component.ts +++ b/angular-client/src/pages/graph-page/graph/graph.component.ts @@ -1,21 +1,10 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import * as ApexCharts from 'apexcharts'; -import { - ApexAxisChartSeries, - ApexXAxis, - ApexDataLabels, - ApexChart, - ApexMarkers, - ApexGrid, - ApexTooltip, - ApexFill -} from 'ng-apexcharts'; +import { ApexXAxis, ApexDataLabels, ApexChart, ApexMarkers, ApexGrid, ApexTooltip, ApexFill } from 'ng-apexcharts'; import { BehaviorSubject } from 'rxjs'; -import { convertUTCtoLocal } from 'src/utils/pipes.utils'; import { GraphData } from 'src/utils/types.utils'; type ChartOptions = { - series: ApexAxisChartSeries; chart: ApexChart; xaxis: ApexXAxis; yaxis: ApexYAxis; @@ -32,38 +21,55 @@ type ChartOptions = { templateUrl: './graph.component.html', styleUrls: ['./graph.component.css'] }) -export default class Graph implements OnInit { +export default class Graph implements OnChanges { @Input() valuesSubject!: BehaviorSubject; options!: ChartOptions; chart!: ApexCharts; previousDataLength: number = 0; - series: ApexAxisChartSeries = [ - { - name: 'Data Series', - data: [] - } - ]; + data!: Map; + timeDiffMs: number = 0; + isSliding: boolean = false; + timeRangeMs = 120000; updateChart = () => { - if (this.previousDataLength !== this.series[0].data.length) { - this.previousDataLength = this.series[0].data.length; - this.chart.updateSeries(this.series); + if (this.previousDataLength !== Array.from(this.data).length) { + this.previousDataLength = Array.from(this.data).length; + this.chart.updateSeries([ + { + name: 'Data Series', + data: Array.from(this.data) + } + ]); + + if (!this.isSliding && this.timeDiffMs > this.timeRangeMs) { + this.isSliding = true; + this.chart.updateOptions({ + ...this.options, + xaxis: { + ...this.options.xaxis, + range: this.timeRangeMs + } + }); + } } setTimeout(() => { this.updateChart(); - }, 1000); + }, 800); }; ngOnInit(): void { + this.data = new Map(); this.valuesSubject.subscribe((values: GraphData[]) => { - const mappedValues = values.map((value: GraphData) => [convertUTCtoLocal(value.x), +value.y.toFixed(3)]); - const newSeries = [ - { - name: 'Data Series', - data: mappedValues + values.forEach((value) => { + if (!this.data.has(value.x)) { + this.data.set(value.x, +value.y.toFixed(3)); } - ]; - this.series = newSeries; + }); + + if (!this.isSliding) { + const times = Array.from(this.data.keys()); + this.timeDiffMs = times[times.length - 1] - times[0]; + } }); const chartContainer = document.getElementById('chart-container'); @@ -73,7 +79,6 @@ export default class Graph implements OnInit { } this.options = { - series: [], chart: { id: 'graph', type: 'line', @@ -99,11 +104,21 @@ export default class Graph implements OnInit { size: 0 }, xaxis: { - type: 'datetime', + type: 'category', tickAmount: 6, labels: { style: { colors: '#fff' + }, + formatter: (value) => { + return ( + '' + + new Date(value).getHours() + + ':' + + ((new Date(value).getMinutes() < 10 ? '0' : '') + new Date(value).getMinutes()) + + ':' + + ((new Date(value).getSeconds() < 10 ? '0' : '') + new Date(value).getSeconds()) + ); } } }, @@ -136,10 +151,37 @@ export default class Graph implements OnInit { // Weird rendering stuff with apex charts, view link to see why https://github.com/apexcharts/react-apexcharts/issues/187 setTimeout(() => { - this.chart = new ApexCharts(chartContainer, this.options); + this.chart = new ApexCharts(chartContainer, { series: [{ data: [] }], ...this.options }); this.chart.render(); this.updateChart(); }, 0); } + + ngOnChanges(changes: SimpleChanges) { + this.data = new Map(); + this.isSliding = false; + + //set range to undefined + this.chart.updateOptions({ + ...this.options, + xaxis: { + ...this.options.xaxis, + range: undefined + } + }); + + this.valuesSubject.subscribe((values: GraphData[]) => { + values.forEach((value) => { + if (!this.data.has(value.x)) { + this.data.set(value.x, +value.y.toFixed(3)); + } + }); + + if (!this.isSliding) { + const times = Array.from(this.data.keys()); + this.timeDiffMs = times[times.length - 1] - times[0]; + } + }); + } }