slow build FIX + charts glitch FIX + code clean

This commit is contained in:
Patrick Haßel 2025-11-21 09:27:59 +01:00
parent a94d80753a
commit dc7e57f341
5 changed files with 93 additions and 123 deletions

View File

@ -9,12 +9,12 @@ import {registerLocaleData} from '@angular/common';
import localeDe from '@angular/common/locales/de'; import localeDe from '@angular/common/locales/de';
import localeDeExtra from '@angular/common/locales/extra/de'; import localeDeExtra from '@angular/common/locales/extra/de';
import {stompServiceFactory} from './common'; import {stompServiceFactory} from './common';
import {Chart, registerables} from 'chart.js'; import {BarController, BarElement, Chart, Legend, LinearScale, TimeScale, Tooltip} from 'chart.js';
import 'chartjs-adapter-date-fns'; import 'chartjs-adapter-date-fns';
registerLocaleData(localeDe, 'de-DE', localeDeExtra); registerLocaleData(localeDe, 'de-DE', localeDeExtra);
Chart.register(...registerables); Chart.register(TimeScale, LinearScale, BarController, BarElement, Tooltip, Legend);
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [

View File

@ -9,16 +9,6 @@
{{ menuService.title }} {{ menuService.title }}
</div> </div>
@if (locationService.id !== null) {
<div class="MainMenuItem" (click)="configService.locationId = locationService.id">
@if (configService.locationId === locationService.id) {
<fa-icon [icon]="faBookmarkSolid" class="bookmarkActive"></fa-icon>
} @else {
<fa-icon [icon]="faBookmarkRegular" class="bookmarkInactive"></fa-icon>
}
</div>
}
<div class="MainMenuItem" routerLink="/Location"> <div class="MainMenuItem" routerLink="/Location">
<fa-icon [icon]="faHome"></fa-icon> <fa-icon [icon]="faHome"></fa-icon>
</div> </div>

View File

@ -1,13 +1,12 @@
import {Component, OnDestroy, OnInit} from '@angular/core'; import {Component, OnDestroy, OnInit} from '@angular/core';
import {Router, RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router'; import {Router, RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router';
import {FaIconComponent} from '@fortawesome/angular-fontawesome'; import {FaIconComponent} from '@fortawesome/angular-fontawesome';
import {faBars, faBookmark as faBookmarkSolid, faGears} from '@fortawesome/free-solid-svg-icons'; import {faBars, faGears} from '@fortawesome/free-solid-svg-icons';
import {MenuService} from './menu-service'; import {MenuService} from './menu-service';
import {Location} from './location/Location'; import {Location} from './location/Location';
import {LocationService} from './location/location-service'; import {LocationService} from './location/location-service';
import {WebsocketService} from './common'; import {WebsocketService} from './common';
import {faBookmark as faBookmarkRegular, faHome} from '@fortawesome/free-regular-svg-icons'; import {faHome} from '@fortawesome/free-regular-svg-icons';
import {ConfigService} from './config.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -21,10 +20,6 @@ export class App implements OnInit, OnDestroy {
protected readonly location = location; protected readonly location = location;
protected readonly faBookmarkRegular = faBookmarkRegular;
protected readonly faBookmarkSolid = faBookmarkSolid;
protected readonly faGears = faGears; protected readonly faGears = faGears;
protected readonly faBars = faBars; protected readonly faBars = faBars;
@ -35,7 +30,6 @@ export class App implements OnInit, OnDestroy {
constructor( constructor(
readonly locationService: LocationService, readonly locationService: LocationService,
readonly configService: ConfigService,
readonly menuService: MenuService, readonly menuService: MenuService,
readonly router: Router, readonly router: Router,
readonly ws: WebsocketService, readonly ws: WebsocketService,

View File

@ -1,4 +1,4 @@
import {Component, Input, ViewChild} from '@angular/core'; import {AfterViewInit, Component, Inject, Input, LOCALE_ID, ViewChild} from '@angular/core';
import {Interval} from '../../../series/Interval'; import {Interval} from '../../../series/Interval';
import {PointService} from '../../../point/point-service'; import {PointService} from '../../../point/point-service';
import {Location} from '../../Location'; import {Location} from '../../Location';
@ -7,7 +7,6 @@ import {ChartConfiguration} from 'chart.js';
import {de} from 'date-fns/locale'; import {de} from 'date-fns/locale';
import {PointSeries} from '../../../point/PointSeries'; import {PointSeries} from '../../../point/PointSeries';
import {formatNumber} from '@angular/common'; import {formatNumber} from '@angular/common';
import {PointResponse} from '../../../point/PointResponse';
const COLOR_BACK_PURCHASE = "#ffb9b9"; const COLOR_BACK_PURCHASE = "#ffb9b9";
const COLOR_BACK_DELIVER = "#ff59ff"; const COLOR_BACK_DELIVER = "#ff59ff";
@ -23,7 +22,10 @@ const COLOR_BACK_CONSUME = "#ffc07a";
templateUrl: './energy-charts.html', templateUrl: './energy-charts.html',
styleUrl: './energy-charts.less', styleUrl: './energy-charts.less',
}) })
class EnergyCharts { export class EnergyCharts implements AfterViewInit {
@ViewChild(BaseChartDirective)
chart?: BaseChartDirective;
@Input() @Input()
unit: string = ""; unit: string = "";
@ -37,62 +39,112 @@ class EnergyCharts {
@Input() @Input()
minY: number | undefined = undefined; minY: number | undefined = undefined;
private _offset!: number | null; @Input()
interval!: Interval;
@Input() @Input()
set offset(value: number | null) { location!: Location;
this._offset = value;
this.fetch();
}
@ViewChild(BaseChartDirective)
chart?: BaseChartDirective;
private _interval!: Interval | null;
@Input() @Input()
set interval(value: Interval | null) { offset: number = 0;
this._interval = value;
this.fetch();
}
private _location!: Location | null; protected data: ChartConfiguration['data'] = {
datasets: [],
};
@Input() protected options: ChartConfiguration['options'] = {
set location(value: Location | null) { responsive: true,
this._location = value; maintainAspectRatio: false,
this.fetch(); animation: false,
scales: {
x: {
type: 'time',
time: {
displayFormats: {
minute: "HH:mm",
hour: "HH:mm",
day: "dd.MM"
},
},
adapters: {
date: {
locale: de
},
},
bounds: 'ticks',
},
y: {
max: this.maxY,
min: this.minY,
} }
},
interaction: {
mode: 'index',
intersect: false
},
plugins: {
legend: {
display: false,
},
tooltip: {
mode: 'index',
intersect: false,
itemSort: (a, b) => b.datasetIndex - a.datasetIndex,
callbacks: {
title: (items) => {
const date = new Date(items[0].parsed.x || 0);
const d = date.toLocaleString(this.locale, {
dateStyle: 'long',
});
const t = date.toLocaleString(this.locale, {
timeStyle: 'long',
});
return `${d}\n${t}`;
},
label: ((ctx: any) => {
const groups = /^.*Energie (?<name>.+)\[(?<unit>.+)]$/.exec(ctx.dataset.label || '')?.groups;
if (groups) {
const value = ctx.parsed.y === null ? '-' : formatNumber(Math.abs(ctx.parsed.y), this.locale, '0.2-2');
return `${value} ${groups['unit']} ${groups['name']}`;
}
return ctx.dataset.label;
}) as any,
},
},
},
};
constructor( constructor(
readonly pointService: PointService, readonly pointService: PointService,
@Inject(LOCALE_ID) readonly locale: string,
) { ) {
// //
} }
fetch(): void { ngAfterViewInit(): void {
if (!this._location || !this._interval || this._offset == null) {
return;
}
const series = [ const series = [
this._location.energyPurchase, this.location.energyPurchase,
this._location.energyDeliver, this.location.energyDeliver,
this._location.energyProduce, this.location.energyProduce,
]; ];
const location = this._location; const location = this.location;
const interval = this._interval; const interval = this.interval;
const offset = this._offset; const offset = this.offset;
this.pointService.relative(series, interval, offset, 1, interval.inner, result => { this.pointService.relative(series, interval, offset, 1, interval.inner, result => {
this.data.datasets.length = 0;
const xScale = this.chart?.chart?.options?.scales?.['x'];
if (xScale) {
xScale.min = result ? result.begin.getTime() : undefined;
xScale.max = result ? result.end.getTime() - (this.interval?.inner?.millis || 0) : undefined;
}
const energyPurchase = result.series.filter(s => s.series.id === location.energyPurchase?.id)[0] || null; const energyPurchase = result.series.filter(s => s.series.id === location.energyPurchase?.id)[0] || null;
const energyDeliver = result.series.filter(s => s.series.id === location.energyDeliver?.id)[0] || null; const energyDeliver = result.series.filter(s => s.series.id === location.energyDeliver?.id)[0] || null;
const energyProduce = result.series.filter(s => s.series.id === location.energyProduce?.id)[0] || null; const energyProduce = result.series.filter(s => s.series.id === location.energyProduce?.id)[0] || null;
const energySelf = energyProduce?.merge(energyDeliver, "Energie Selbst", (a, b) => a - b) || null; const energySelf = energyProduce?.merge(energyDeliver, "Energie Selbst", (a, b) => a - b) || null;
this.data.datasets.length = 0;
this.add(energyDeliver, COLOR_BACK_DELIVER, -1, "a"); this.add(energyDeliver, COLOR_BACK_DELIVER, -1, "a");
this.add(energySelf, COLOR_BACK_SELF, 1, "a"); this.add(energySelf, COLOR_BACK_SELF, 1, "a");
this.add(energyPurchase, COLOR_BACK_PURCHASE, 1, "a"); this.add(energyPurchase, COLOR_BACK_PURCHASE, 1, "a");
this.options = this.newOptions(result); this.chart?.chart?.update();
this.chart?.update();
}); });
} }
@ -114,69 +166,4 @@ class EnergyCharts {
}); });
} }
protected data: ChartConfiguration['data'] = {
datasets: [],
};
protected options: ChartConfiguration['options'] = {};
private newOptions(result: PointResponse): ChartConfiguration['options'] {
return {
responsive: true,
maintainAspectRatio: false,
animation: false,
scales: {
x: {
type: 'time',
min: result.begin.getTime(),
max: result.end.getTime() - (this._interval?.inner?.millis || 0),
time: {
displayFormats: {
minute: "HH:mm",
hour: "HH:mm",
day: "dd.MM"
},
},
adapters: {
date: {
locale: de
},
},
bounds: 'ticks',
},
y: {
max: this.maxY,
min: this.minY,
} }
},
interaction: {
mode: 'index',
intersect: false
},
plugins: {
legend: {
display: false,
},
tooltip: {
mode: 'index',
intersect: false,
itemSort: (a, b) => b.datasetIndex - a.datasetIndex,
callbacks: {
label: (ctx) => {
const groups = /^.*Energie (?<name>.+)\[(?<unit>.+)]$/.exec(ctx.dataset.label || '')?.groups;
if (groups) {
const value = ctx.parsed.y === null ? '-' : formatNumber(Math.abs(ctx.parsed.y), 'de-DE', '0.2-2');
return `${value} ${groups['unit']} ${groups['name']}`;
}
return ctx.dataset.label;
},
},
},
},
};
}
}
export default EnergyCharts

View File

@ -7,14 +7,13 @@ import {PointService} from '../../point/point-service';
import {SeriesService} from '../../series/series-service'; import {SeriesService} from '../../series/series-service';
import {Subscription} from 'rxjs'; import {Subscription} from 'rxjs';
import {Value} from '../../series/Value'; import {Value} from '../../series/Value';
import EnergyCharts from './charts/energy-charts';
import {ConfigService} from '../../config.service'; import {ConfigService} from '../../config.service';
import {EnergyCharts} from './charts/energy-charts';
@Component({ @Component({
selector: 'app-location-energy', selector: 'app-location-energy',
imports: [ imports: [
EnergyCharts, EnergyCharts,
EnergyCharts
], ],
templateUrl: './location-energy.html', templateUrl: './location-energy.html',
styleUrl: './location-energy.less', styleUrl: './location-energy.less',