From f7c30d71d2ad92016119920bdea9eb4868463d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Fri, 21 Nov 2025 11:54:06 +0100 Subject: [PATCH] locationPower update REFACTOR --- src/main/angular/src/app/app.html | 16 +-- src/main/angular/src/app/app.ts | 9 +- src/main/angular/src/app/location/Location.ts | 121 ++++-------------- .../app/location/detail/location-detail.ts | 17 ++- .../location/energy/charts/energy-charts.ts | 10 +- .../app/location/energy/location-energy.ts | 13 +- .../src/app/location/location-service.ts | 33 ----- .../app/location/power/location-power.html | 22 ++-- .../src/app/location/power/location-power.ts | 78 ++++++++++- src/main/angular/src/app/menu-service.ts | 17 ++- .../src/app/series/SeriesListResponse.ts | 22 ++++ src/main/angular/src/app/series/Value.ts | 10 +- .../src/app/settings/settings-component.ts | 4 +- 13 files changed, 185 insertions(+), 187 deletions(-) create mode 100644 src/main/angular/src/app/series/SeriesListResponse.ts diff --git a/src/main/angular/src/app/app.html b/src/main/angular/src/app/app.html index 8f78f8b..e5e0c41 100644 --- a/src/main/angular/src/app/app.html +++ b/src/main/angular/src/app/app.html @@ -20,16 +20,12 @@ -@if (ws.connected) { - - diff --git a/src/main/angular/src/app/app.ts b/src/main/angular/src/app/app.ts index 00b13bc..2a582f3 100644 --- a/src/main/angular/src/app/app.ts +++ b/src/main/angular/src/app/app.ts @@ -1,4 +1,4 @@ -import {Component, OnDestroy, OnInit} from '@angular/core'; +import {Component, OnInit} from '@angular/core'; import {Router, RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router'; import {FaIconComponent} from '@fortawesome/angular-fontawesome'; import {faBars, faGears} from '@fortawesome/free-solid-svg-icons'; @@ -14,7 +14,7 @@ import {faHome} from '@fortawesome/free-regular-svg-icons'; templateUrl: './app.html', styleUrl: './app.less' }) -export class App implements OnInit, OnDestroy { +export class App implements OnInit { protected readonly faHome = faHome; @@ -39,11 +39,6 @@ export class App implements OnInit, OnDestroy { ngOnInit(): void { this.locationService.findAll(list => this.locationList = list); - this.menuService.title = "Orte"; - } - - ngOnDestroy(): void { - this.menuService.title = ""; } navigate(url: string): void { diff --git a/src/main/angular/src/app/location/Location.ts b/src/main/angular/src/app/location/Location.ts index da420b6..836bcae 100644 --- a/src/main/angular/src/app/location/Location.ts +++ b/src/main/angular/src/app/location/Location.ts @@ -1,66 +1,26 @@ import {or, validateNumber, validateString} from '../common'; import {Series} from '../series/Series'; -import {Value} from '../series/Value'; export class Location { - powerSelf: Value = Value.NULL; - - powerPurchasePercentConsume: Value = Value.NULL; - - powerProducePercentConsume: Value = Value.NULL; - - powerDeliveryPercentConsume: Value = Value.NULL; - - powerDeliveryPercentProduce: Value = Value.NULL; - - powerSelfPercentConsume: Value = Value.NULL; - - powerSelfPercentProduce: Value = Value.NULL; - constructor( readonly id: number, readonly name: string, readonly latitude: number, readonly longitude: number, - private _energyPurchase: Series | null, - private _energyDeliver: Series | null, - private _energyProduce: Series | null, - private _powerPurchase: Series | null, - private _powerDeliver: Series | null, - private _powerProduce: Series | null, - private _outsideTemperature: Series | null, - private _outsideHumidityRelative: Series | null, - private _outsideHumidityAbsolute: Series | null, - private _powerConsume: Value = Value.NULL, + readonly energyPurchase: Series | null, + readonly energyDeliver: Series | null, + readonly energyProduce: Series | null, + readonly powerPurchase: Series | null, + readonly powerDeliver: Series | null, + readonly powerProduce: Series | null, + readonly outsideTemperature: Series | null, + readonly outsideHumidityRelative: Series | null, + readonly outsideHumidityAbsolute: Series | null, ) { - this.updateConsume(); + // } - readonly updateSeries = (series: Series) => { - if (series.equals(this._energyPurchase)) { - this._energyPurchase = series; - } - if (series.equals(this._energyDeliver)) { - this._energyDeliver = series; - } - if (series.equals(this._energyProduce)) { - this._energyProduce = series; - } - if (series.equals(this._powerProduce)) { - this._powerProduce = series; - this.updateConsume(); - } - if (series.equals(this._powerPurchase)) { - this._powerPurchase = series; - this.updateConsume(); - } - if (series.equals(this._powerDeliver)) { - this._powerDeliver = series; - this.updateConsume(); - } - }; - static fromJson(json: any): Location { return new Location( validateNumber(json.id), @@ -79,55 +39,18 @@ export class Location { ); } - private updateConsume() { - this._powerConsume = Value.ZERO.plus(this._powerPurchase?.value, true).plus(this._powerProduce?.value, true).minus(this._powerDeliver?.value, true); - this.powerSelf = Value.ZERO.plus(this.powerProduce?.value.minus(this.powerDeliver?.value, true), true); - this.powerPurchasePercentConsume = Value.ZERO.plus(this.powerPurchase?.value.percent(this.powerConsume, "%", 0), true); - this.powerProducePercentConsume = Value.ZERO.plus(this.powerProduce?.value.percent(this.powerConsume, "%", 0), true); - this.powerDeliveryPercentConsume = Value.ZERO.plus(this.powerDeliver?.value.percent(this.powerConsume, "%", 0), true); - this.powerDeliveryPercentProduce = Value.ZERO.plus(this.powerDeliver?.value.percent(this.powerProduce?.value, "%", 0), true); - this.powerSelfPercentConsume = Value.ZERO.plus(this.powerSelf.percent(this.powerConsume, "%", 0), true); - this.powerSelfPercentProduce = Value.ZERO.plus(this.powerSelf.percent(this.powerProduce?.value, "%", 0), true); - } - - get energyPurchase(): Series | null { - return this._energyPurchase; - } - - get energyDeliver(): Series | null { - return this._energyDeliver; - } - - get energyProduce(): Series | null { - return this._energyProduce; - } - - get powerPurchase(): Series | null { - return this._powerPurchase; - } - - get powerDeliver(): Series | null { - return this._powerDeliver; - } - - get powerProduce(): Series | null { - return this._powerProduce; - } - - get powerConsume(): Value | null { - return this._powerConsume; - } - - get outsideTemperature(): Series | null { - return this._outsideTemperature; - } - - get outsideHumidityRelative(): Series | null { - return this._outsideHumidityRelative; - } - - get outsideHumidityAbsolute(): Series | null { - return this._outsideHumidityAbsolute; + getSeries(): Series[] { + return [ + this.energyPurchase, + this.energyDeliver, + this.energyProduce, + this.powerPurchase, + this.powerDeliver, + this.powerProduce, + this.outsideTemperature, + this.outsideHumidityRelative, + this.outsideHumidityAbsolute, + ].filter(s => s) as Series[]; } } diff --git a/src/main/angular/src/app/location/detail/location-detail.ts b/src/main/angular/src/app/location/detail/location-detail.ts index 57ddc6c..5844cbf 100644 --- a/src/main/angular/src/app/location/detail/location-detail.ts +++ b/src/main/angular/src/app/location/detail/location-detail.ts @@ -56,6 +56,8 @@ export class LocationDetail implements OnInit, OnDestroy { protected readonly Math = Math; + private locationId: number | null = null; + protected location: Location | null = null; private readonly subs: Subscription [] = []; @@ -85,19 +87,22 @@ export class LocationDetail implements OnInit, OnDestroy { this.router.navigate(["Location/" + this.configService.locationId]); return; } - setTimeout(() => this.locationService.id = id, 0); + this.locationId = id; + if (this.locationId) { + this.locationService.getById(this.locationId, this.onLocationChange); + } })); - this.subs.push(this.locationService.location$.subscribe(this.onLocationChange)); + this.subs.push(this.locationService.subscribe(this.onLocationChange)); } private readonly onLocationChange = (location: Location | null): void => { - this.location = location; - this.menuService.title = this.location?.name || ""; + if (this.locationId === location?.id) { + this.location = location; + this.menuService.setLocation(location); + } }; ngOnDestroy(): void { - this.location = null; - this.menuService.title = ""; this.subs.forEach(sub => sub.unsubscribe()); this.subs.length = 0; } diff --git a/src/main/angular/src/app/location/energy/charts/energy-charts.ts b/src/main/angular/src/app/location/energy/charts/energy-charts.ts index 2b9fb6b..97ec0f8 100644 --- a/src/main/angular/src/app/location/energy/charts/energy-charts.ts +++ b/src/main/angular/src/app/location/energy/charts/energy-charts.ts @@ -1,4 +1,4 @@ -import {Component, Inject, Input, LOCALE_ID, OnChanges, SimpleChanges, ViewChild} from '@angular/core'; +import {Component, Inject, Input, LOCALE_ID, OnChanges, ViewChild} from '@angular/core'; import {Interval} from '../../../series/Interval'; import {PointService} from '../../../point/point-service'; import {Location} from '../../Location'; @@ -111,6 +111,8 @@ export class EnergyCharts implements OnChanges { }, }; + private refreshTimeout: number | undefined; + constructor( readonly pointService: PointService, @Inject(LOCALE_ID) readonly locale: string, @@ -118,8 +120,10 @@ export class EnergyCharts implements OnChanges { // } - ngOnChanges(changes: SimpleChanges): void { - console.log("ngOnChanges", changes); + ngOnChanges(): void { + clearTimeout(this.refreshTimeout); + this.refreshTimeout = setTimeout(() => this.ngOnChanges(), 60 * 1000); + const series = [ this.location.energyPurchase, this.location.energyDeliver, diff --git a/src/main/angular/src/app/location/energy/location-energy.ts b/src/main/angular/src/app/location/energy/location-energy.ts index bb1a19a..83f9d38 100644 --- a/src/main/angular/src/app/location/energy/location-energy.ts +++ b/src/main/angular/src/app/location/energy/location-energy.ts @@ -93,6 +93,7 @@ export class LocationEnergy implements OnInit, OnChanges, OnDestroy { ngOnDestroy(): void { this.subs.forEach(sub => sub.unsubscribe()); + this.subs.length = 0; } protected readonly update = (fresh: Series): void => { @@ -109,12 +110,12 @@ export class LocationEnergy implements OnInit, OnChanges, OnDestroy { next(value); this.consume = this.purchase.plus(this.produce, true).minus(this.deliver, true); this.self = this.produce.minus(this.deliver, true); - this.purchasePercentConsume = this.purchase.percent(this.consume, "%", 0); - this.producePercentConsume = this.produce.percent(this.consume, "%", 0); - this.deliveryPercentConsume = this.deliver.percent(this.consume, "%", 0); - this.deliveryPercentProduce = this.deliver.percent(this.produce, "%", 0); - this.selfPercentConsume = this.self.percent(this.consume, "%", 0); - this.selfPercentProduce = this.self.percent(this.produce, "%", 0); + this.purchasePercentConsume = this.purchase.percent(this.consume); + this.producePercentConsume = this.produce.percent(this.consume); + this.deliveryPercentConsume = this.deliver.percent(this.consume); + this.deliveryPercentProduce = this.deliver.percent(this.produce); + this.selfPercentConsume = this.self.percent(this.consume); + this.selfPercentProduce = this.self.percent(this.produce); }; if (fresh !== null && fresh !== undefined) { if (fresh.id !== series?.id) { diff --git a/src/main/angular/src/app/location/location-service.ts b/src/main/angular/src/app/location/location-service.ts index b4f23ca..44cd61b 100644 --- a/src/main/angular/src/app/location/location-service.ts +++ b/src/main/angular/src/app/location/location-service.ts @@ -1,50 +1,17 @@ import {Injectable} from '@angular/core'; import {ApiService, CrudService, Next, WebsocketService} from '../common'; import {Location} from './Location' -import {SeriesService} from '../series/series-service'; -import {BehaviorSubject, Observable} from 'rxjs'; @Injectable({ providedIn: 'root' }) export class LocationService extends CrudService { - private readonly _location = new BehaviorSubject(null); - - private _id: number | null = null; - constructor( api: ApiService, ws: WebsocketService, - readonly seriesService: SeriesService, ) { super(api, ws, ['Location'], Location.fromJson); - this.seriesService.subscribe(series => this._location.value?.updateSeries(series)); - this.ws.onConnect(this.fetch); - this.ws.onDisconnect(() => this._location.next(null)); - } - - set id(id: number | null) { - if (this._id !== id) { - this._id = id; - this.fetch(); - } - } - - get id(): number | null { - return this._id; - } - - private readonly fetch = () => { - if (this._id === null) { - this._location.next(null); - } else { - this.getById(this._id, location => this._location.next(location)); - } - }; - - get location$(): Observable { - return this._location.asObservable(); } findAll(next: Next) { diff --git a/src/main/angular/src/app/location/power/location-power.html b/src/main/angular/src/app/location/power/location-power.html index d7e2ad1..4170907 100644 --- a/src/main/angular/src/app/location/power/location-power.html +++ b/src/main/angular/src/app/location/power/location-power.html @@ -11,11 +11,11 @@ Bezug
- {{ location.powerPurchase?.value?.toValueString(dateService.now) }} + {{ powerPurchase.toValueString(dateService.now) }}
@if (configService.energyPercent) {
- {{ location.powerPurchasePercentConsume.toValueString(dateService.now) }} + {{ powerPurchasePercentConsume.toValueString(dateService.now) }} Verbrauch
} @@ -26,11 +26,11 @@ Solar
- {{ location.powerProduce?.value?.toValueString(dateService.now) }} + {{ powerProduce.toValueString(dateService.now) }}
@if (configService.energyPercent) {
- {{ location.powerProducePercentConsume.toValueString(dateService.now) }} + {{ powerProducePercentConsume.toValueString(dateService.now) }} Verbrauch
} @@ -41,15 +41,15 @@ Selbst
- {{ location.powerSelf.toValueString(dateService.now) }} + {{ powerSelf.toValueString(dateService.now) }}
@if (configService.energyPercent) {
- {{ location.powerSelfPercentConsume.toValueString(dateService.now) }} + {{ powerSelfPercentConsume.toValueString(dateService.now) }} Verbrauch
- {{ location.powerSelfPercentProduce.toValueString(dateService.now) }} + {{ powerSelfPercentProduce.toValueString(dateService.now) }} Produktion
} @@ -60,7 +60,7 @@ Verbrauch
- {{ location.powerConsume?.toValueString(dateService.now) }} + {{ powerConsume.toValueString(dateService.now) }}
@@ -69,15 +69,15 @@ Einspeisung
- {{ location.powerDeliver?.value?.toValueString(dateService.now) }} + {{ powerDeliver.toValueString(dateService.now) }}
@if (configService.energyPercent) {
- {{ location.powerDeliveryPercentConsume.toValueString(dateService.now) }} + {{ powerDeliverPercentConsume.toValueString(dateService.now) }} Verbrauch
- {{ location.powerDeliveryPercentProduce.toValueString(dateService.now) }} + {{ powerDeliverPercentProduce.toValueString(dateService.now) }} Produktion
} diff --git a/src/main/angular/src/app/location/power/location-power.ts b/src/main/angular/src/app/location/power/location-power.ts index 555de35..13fc91e 100644 --- a/src/main/angular/src/app/location/power/location-power.ts +++ b/src/main/angular/src/app/location/power/location-power.ts @@ -1,7 +1,11 @@ -import {Component, Input} from '@angular/core'; +import {Component, Input, OnChanges, OnDestroy, OnInit} from '@angular/core'; import {Location} from '../Location'; import {DateService} from '../../date.service'; import {ConfigService} from '../../config.service'; +import {Value} from '../../series/Value'; +import {SeriesService} from '../../series/series-service'; +import {Subscription} from 'rxjs'; +import {Series} from '../../series/Series'; @Component({ selector: 'app-location-power', @@ -9,16 +13,86 @@ import {ConfigService} from '../../config.service'; templateUrl: './location-power.html', styleUrl: './location-power.less', }) -export class LocationPower { +export class LocationPower implements OnInit, OnChanges, OnDestroy { @Input() location!: Location; + private readonly subs: Subscription[] = []; + + protected energyPurchase: Value = Value.NULL; + + protected energyDeliver: Value = Value.NULL; + + protected energyProduce: Value = Value.NULL; + + protected powerPurchase: Value = Value.NULL; + + protected powerDeliver: Value = Value.NULL; + + protected powerProduce: Value = Value.NULL; + + protected outsideTemperature: Value = Value.NULL; + + protected outsideHumidityRelative: Value = Value.NULL; + + protected outsideHumidityAbsolute: Value = Value.NULL; + + protected powerConsume: Value = Value.NULL; + + protected powerSelf: Value = Value.NULL; + + protected powerPurchasePercentConsume: Value = Value.NULL; + + protected powerProducePercentConsume: Value = Value.NULL; + + protected powerDeliverPercentConsume: Value = Value.NULL; + + protected powerDeliverPercentProduce: Value = Value.NULL; + + protected powerSelfPercentConsume: Value = Value.NULL; + + protected powerSelfPercentProduce: Value = Value.NULL; + constructor( readonly dateService: DateService, readonly configService: ConfigService, + readonly seriesService: SeriesService, ) { // } + ngOnInit(): void { + this.subs.push(this.seriesService.subscribe(this.seriesUpdate)); + } + + ngOnChanges(): void { + this.location.getSeries().forEach(this.seriesUpdate); + } + + ngOnDestroy(): void { + this.subs.forEach(sub => sub.unsubscribe()); + this.subs.length = 0; + } + + private readonly seriesUpdate = (series: Series): void => { + if (series.id === this.location.energyPurchase?.id) this.energyPurchase = series.value; + if (series.id === this.location.energyDeliver?.id) this.energyDeliver = series.value; + if (series.id === this.location.energyProduce?.id) this.energyProduce = series.value; + if (series.id === this.location.powerPurchase?.id) this.powerPurchase = series.value; + if (series.id === this.location.powerDeliver?.id) this.powerDeliver = series.value; + if (series.id === this.location.powerProduce?.id) this.powerProduce = series.value; + if (series.id === this.location.outsideTemperature?.id) this.outsideTemperature = series.value; + if (series.id === this.location.outsideHumidityRelative?.id) this.outsideHumidityRelative = series.value; + if (series.id === this.location.outsideHumidityAbsolute?.id) this.outsideHumidityAbsolute = series.value; + this.powerSelf = Value.ZERO.plus(this.powerProduce).minus(this.powerDeliver); + this.powerConsume = Value.ZERO.plus(this.powerPurchase).plus(this.powerSelf); + this.powerPurchasePercentConsume = this.powerPurchase.percent(this.powerConsume); + this.powerProducePercentConsume = this.powerProduce.percent(this.powerConsume); + this.powerDeliverPercentConsume = this.powerDeliver.percent(this.powerConsume) + this.powerDeliverPercentProduce = this.powerDeliver.percent(this.powerProduce) + this.powerSelfPercentConsume = this.powerSelf.percent(this.powerConsume); + this.powerSelfPercentProduce = this.powerSelf.percent(this.powerProduce); + }; + } diff --git a/src/main/angular/src/app/menu-service.ts b/src/main/angular/src/app/menu-service.ts index 1107f68..b816036 100644 --- a/src/main/angular/src/app/menu-service.ts +++ b/src/main/angular/src/app/menu-service.ts @@ -1,18 +1,31 @@ import {Injectable} from '@angular/core'; +import {Location} from './location/Location'; @Injectable({ providedIn: 'root' }) export class MenuService { + private _locationId: number | null = null; + private _title: string = ""; + get locationId(): number | null { + return this._locationId; + } + get title(): string { return this._title; } - set title(value: string) { - setTimeout(() => this._title = value, 0); + setLocation(location: Location): void { + this._locationId = location.id; + this._title = location.name; + } + + setNonLocation(title: string) { + this._title = title; + this._locationId = null; } } diff --git a/src/main/angular/src/app/series/SeriesListResponse.ts b/src/main/angular/src/app/series/SeriesListResponse.ts new file mode 100644 index 0000000..ad3cd61 --- /dev/null +++ b/src/main/angular/src/app/series/SeriesListResponse.ts @@ -0,0 +1,22 @@ +import {Series} from "./Series"; +import {validateList} from "../common"; + +export class SeriesListResponse { + + constructor( + readonly series: Series[] + ) { + // + } + + static fromJson(json: any): SeriesListResponse { + return new SeriesListResponse( + validateList(json, Series.fromJson), + ); + } + + findSeries(series: Series | null): Series | null { + return series ? this.series.filter(s => s.id === series.id)[0] || null : null; + } + +} diff --git a/src/main/angular/src/app/series/Value.ts b/src/main/angular/src/app/series/Value.ts index 0aa736f..bb33e7b 100644 --- a/src/main/angular/src/app/series/Value.ts +++ b/src/main/angular/src/app/series/Value.ts @@ -29,7 +29,7 @@ export class Value { return `--- ${this.unit}` } const scale = Math.floor(Math.log10(this.value)); - if(isNaN(scale)) { + if (isNaN(scale)) { return '0'; } const rest = scale - this.precision + 1; @@ -39,14 +39,14 @@ export class Value { return formatNumber(this.value, "de-DE", `0.${-rest}-${-rest}`) + ' ' + this.unit; } - plus(other: Value | null | undefined, nullToZero: boolean): Value { + plus(other: Value | null | undefined, nullToZero: boolean = true): Value { if (!nullToZero && (other === null || other === undefined)) { return Value.NULL; } return new BiValue(this, other || Value.ZERO, (a, b) => a + b); } - minus(other: Value | null | undefined, nullToZero: boolean): Value { + minus(other: Value | null | undefined, nullToZero: boolean = true): Value { if (!nullToZero && (other === null || other === undefined)) { return Value.NULL; } @@ -86,11 +86,11 @@ export class Value { return ageSeconds > this.seconds * 2.1; } - percent(other: Value | null | undefined, unit: string | null = null, precision: number | null = null): Value { + percent(other: Value | null | undefined): Value { if (other === null || other === undefined) { return Value.NULL; } - return new BiValue(this, other, (a, b) => a / b * 100, unit, precision); + return new BiValue(this, other, (a, b) => a / b * 100, '%', 0); } } diff --git a/src/main/angular/src/app/settings/settings-component.ts b/src/main/angular/src/app/settings/settings-component.ts index 3e267d9..9acf542 100644 --- a/src/main/angular/src/app/settings/settings-component.ts +++ b/src/main/angular/src/app/settings/settings-component.ts @@ -1,6 +1,5 @@ import {Component, OnInit} from '@angular/core'; import {LocationSelect} from '../location/select/location-select'; -import {LocationService} from '../location/location-service'; import {Location} from '../location/Location' import {ConfigService} from '../config.service'; import {FormsModule} from '@angular/forms'; @@ -22,7 +21,6 @@ export class SettingsComponent implements OnInit { protected location: Location | null = null; constructor( - readonly locationService: LocationService, readonly configService: ConfigService, readonly menuService: MenuService, ) { @@ -30,7 +28,7 @@ export class SettingsComponent implements OnInit { } ngOnInit(): void { - this.menuService.title = "Einstellungen"; + this.menuService.setNonLocation("Einstellungen"); } }