GraphOperation
This commit is contained in:
parent
ab7f3de9a2
commit
d65628a7d6
@ -53,6 +53,14 @@ export function validateBoolean(json: any): boolean {
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function validateEnum<T extends Record<string, string>>(value: any, enumType: T): T[keyof T] {
|
||||||
|
const str = validateString(value);
|
||||||
|
if (Object.values(enumType).includes(str)) {
|
||||||
|
return str as T[keyof T];
|
||||||
|
}
|
||||||
|
throw new Error(`Invalid enum value: ${str}`);
|
||||||
|
}
|
||||||
|
|
||||||
export function validateList<T>(json: any, fromJson: FromJson<T>): T[] {
|
export function validateList<T>(json: any, fromJson: FromJson<T>): T[] {
|
||||||
return json.map(fromJson);
|
return json.map(fromJson);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,20 @@
|
|||||||
import {Series} from "../../../series/Series";
|
import {Series} from "../../../series/Series";
|
||||||
import {Group} from "../../Group";
|
import {Group} from "../../Group";
|
||||||
import {validateBoolean, validateNumber, validateString} from "../../../COMMON";
|
import {mapNotNull, validateBoolean, validateEnum, validateNumber, validateString} from "../../../COMMON";
|
||||||
import {Axis} from '../Axis';
|
import {Axis} from '../Axis';
|
||||||
import {SeriesType} from '../../../series/SeriesType';
|
import {SeriesType} from '../../../series/SeriesType';
|
||||||
import {GraphType} from './GraphType';
|
import {GraphType} from './GraphType';
|
||||||
|
|
||||||
|
export enum GraphOperation {
|
||||||
|
MINUS = 'MINUS',
|
||||||
|
PLUS = 'PLUS',
|
||||||
|
DIVIDE = 'DIVIDE',
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listGraphOperation() {
|
||||||
|
return Object.keys(GraphOperation);
|
||||||
|
}
|
||||||
|
|
||||||
export class Graph {
|
export class Graph {
|
||||||
|
|
||||||
readonly showMin: boolean;
|
readonly showMin: boolean;
|
||||||
@ -18,12 +28,15 @@ export class Graph {
|
|||||||
readonly id: number,
|
readonly id: number,
|
||||||
readonly version: number,
|
readonly version: number,
|
||||||
readonly series: Series,
|
readonly series: Series,
|
||||||
|
readonly factor: number,
|
||||||
|
readonly operation: GraphOperation,
|
||||||
|
readonly series2: Series | null,
|
||||||
|
readonly factor2: number,
|
||||||
readonly name: string,
|
readonly name: string,
|
||||||
readonly visible: boolean,
|
readonly visible: boolean,
|
||||||
readonly type: GraphType,
|
readonly type: GraphType,
|
||||||
readonly fill: string,
|
readonly fill: string,
|
||||||
readonly color: string,
|
readonly color: string,
|
||||||
readonly factor: number,
|
|
||||||
readonly group: Group,
|
readonly group: Group,
|
||||||
readonly stack: string,
|
readonly stack: string,
|
||||||
readonly min: boolean,
|
readonly min: boolean,
|
||||||
@ -41,12 +54,15 @@ export class Graph {
|
|||||||
validateNumber(json.id),
|
validateNumber(json.id),
|
||||||
validateNumber(json.version),
|
validateNumber(json.version),
|
||||||
Series.fromJson(json.series),
|
Series.fromJson(json.series),
|
||||||
|
validateNumber(json.factor),
|
||||||
|
validateEnum(json.operation, GraphOperation),
|
||||||
|
mapNotNull(json.series2, Series.fromJson),
|
||||||
|
validateNumber(json.factor2),
|
||||||
validateString(json.name),
|
validateString(json.name),
|
||||||
validateBoolean(json.visible),
|
validateBoolean(json.visible),
|
||||||
GraphType.fromJson(json.type),
|
GraphType.fromJson(json.type),
|
||||||
validateString(json.fill),
|
validateString(json.fill),
|
||||||
validateString(json.color),
|
validateString(json.color),
|
||||||
validateNumber(json.factor),
|
|
||||||
validateString(json.group) as Group,
|
validateString(json.group) as Group,
|
||||||
validateString(json.stack),
|
validateString(json.stack),
|
||||||
validateBoolean(json.min),
|
validateBoolean(json.min),
|
||||||
|
|||||||
@ -136,9 +136,9 @@
|
|||||||
</th>
|
</th>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Serie</th>
|
<th>Serie</th>
|
||||||
|
<th>Faktor</th>
|
||||||
<th>Typ</th>
|
<th>Typ</th>
|
||||||
<th>Farbe</th>
|
<th>Farbe</th>
|
||||||
<th>Faktor</th>
|
|
||||||
<th>Aggregat</th>
|
<th>Aggregat</th>
|
||||||
<th>Stack</th>
|
<th>Stack</th>
|
||||||
<th class="vertical">min</th>
|
<th class="vertical">min</th>
|
||||||
@ -163,6 +163,27 @@
|
|||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<app-number-nn [initial]="graph.factor" (onChange)="plotService.graphFactor(graph, $event)"></app-number-nn>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select [ngModel]="graph.operation" (ngModelChange)="plotService.graphOperation(graph, $event)">
|
||||||
|
@for (operation of listGraphOperation(); track operation) {
|
||||||
|
<option [ngValue]="operation">{{ operation }}</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select [ngModel]="graph.series2?.id" (ngModelChange)="plotService.graphSeries2(graph, $event)">
|
||||||
|
<option [ngValue]="null">-</option>
|
||||||
|
@for (s of seriesService.list; track s.name) {
|
||||||
|
<option [ngValue]="s.id">{{ s.name }} [{{ s.valueString }}]</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<app-number-nn [initial]="graph.factor2" (onChange)="plotService.graphFactor2(graph, $event)"></app-number-nn>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<select [ngModel]="graph.type" (ngModelChange)="plotService.graphType(graph, $event)">
|
<select [ngModel]="graph.type" (ngModelChange)="plotService.graphType(graph, $event)">
|
||||||
@for (type of GraphType.values; track type.jsonName) {
|
@for (type of GraphType.values; track type.jsonName) {
|
||||||
@ -173,9 +194,6 @@
|
|||||||
<td>
|
<td>
|
||||||
<app-text [initial]="graph.color" (onChange)="plotService.graphColor(graph, $event)"></app-text>
|
<app-text [initial]="graph.color" (onChange)="plotService.graphColor(graph, $event)"></app-text>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
|
||||||
<app-number-nn [initial]="graph.factor" (onChange)="plotService.graphFactor(graph, $event)"></app-number-nn>
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
<select [ngModel]="graph.group" (ngModelChange)="plotService.graphGroup(graph, $event)">
|
<select [ngModel]="graph.group" (ngModelChange)="plotService.graphGroup(graph, $event)">
|
||||||
@for (group of groups(); track group) {
|
@for (group of groups(); track group) {
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import {Location} from '@angular/common';
|
|||||||
import {PlotComponent} from '../plot/plot.component';
|
import {PlotComponent} from '../plot/plot.component';
|
||||||
import {ActivatedRoute} from '@angular/router';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
import {GraphType} from '../axis/graph/GraphType';
|
import {GraphType} from '../axis/graph/GraphType';
|
||||||
|
import {GraphOperation, listGraphOperation} from '../axis/graph/Graph';
|
||||||
|
|
||||||
Chart.register(
|
Chart.register(
|
||||||
CategoryScale,
|
CategoryScale,
|
||||||
@ -35,13 +36,6 @@ Chart.register(
|
|||||||
Filler,
|
Filler,
|
||||||
);
|
);
|
||||||
|
|
||||||
export function unitInBrackets(unit: string): string {
|
|
||||||
if (!unit) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return ` [${unit}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-plot-editor',
|
selector: 'app-plot-editor',
|
||||||
imports: [
|
imports: [
|
||||||
@ -58,6 +52,12 @@ export function unitInBrackets(unit: string): string {
|
|||||||
})
|
})
|
||||||
export class PlotEditor implements OnInit, OnDestroy {
|
export class PlotEditor implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
protected readonly GraphType = GraphType;
|
||||||
|
|
||||||
|
protected readonly GraphOperation = GraphOperation;
|
||||||
|
|
||||||
|
protected readonly listGraphOperation = listGraphOperation;
|
||||||
|
|
||||||
protected readonly SeriesType = SeriesType;
|
protected readonly SeriesType = SeriesType;
|
||||||
|
|
||||||
protected readonly Interval = Interval;
|
protected readonly Interval = Interval;
|
||||||
@ -142,5 +142,4 @@ export class PlotEditor implements OnInit, OnDestroy {
|
|||||||
return Object.keys(Group);
|
return Object.keys(Group);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly GraphType = GraphType;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import {Injectable} from '@angular/core';
|
|||||||
import {ApiService, EntityListService, Next} from '../COMMON';
|
import {ApiService, EntityListService, Next} from '../COMMON';
|
||||||
import {Plot} from './Plot';
|
import {Plot} from './Plot';
|
||||||
import {Axis} from './axis/Axis';
|
import {Axis} from './axis/Axis';
|
||||||
import {Graph} from './axis/graph/Graph';
|
import {Graph, GraphOperation} from './axis/graph/Graph';
|
||||||
import {Group} from './Group';
|
import {Group} from './Group';
|
||||||
import {Interval} from '../series/Interval';
|
import {Interval} from '../series/Interval';
|
||||||
import {GraphType} from './axis/graph/GraphType';
|
import {GraphType} from './axis/graph/GraphType';
|
||||||
@ -118,14 +118,26 @@ export class PlotService extends EntityListService<Plot> {
|
|||||||
this.postSingle(['Graph', graph.id, 'series'], value, next);
|
this.postSingle(['Graph', graph.id, 'series'], value, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
graphColor(graph: Graph, value: string, next?: Next<Plot>): void {
|
|
||||||
this.postSingle(['Graph', graph.id, 'color'], value, next);
|
|
||||||
}
|
|
||||||
|
|
||||||
graphFactor(graph: Graph, value: number, next?: Next<Plot>): void {
|
graphFactor(graph: Graph, value: number, next?: Next<Plot>): void {
|
||||||
this.postSingle(['Graph', graph.id, 'factor'], value, next);
|
this.postSingle(['Graph', graph.id, 'factor'], value, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
graphOperation(graph: Graph, value: GraphOperation, next?: Next<Plot>): void {
|
||||||
|
this.postSingle(['Graph', graph.id, 'operation'], value, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
graphSeries2(graph: Graph, value: number, next?: Next<Plot>): void {
|
||||||
|
this.postSingle(['Graph', graph.id, 'series2'], value, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
graphFactor2(graph: Graph, value: number, next?: Next<Plot>): void {
|
||||||
|
this.postSingle(['Graph', graph.id, 'factor2'], value, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
graphColor(graph: Graph, value: string, next?: Next<Plot>): void {
|
||||||
|
this.postSingle(['Graph', graph.id, 'color'], value, next);
|
||||||
|
}
|
||||||
|
|
||||||
graphGroup(graph: Graph, value: Group, next?: Next<Plot>): void {
|
graphGroup(graph: Graph, value: Group, next?: Next<Plot>): void {
|
||||||
this.postSingle(['Graph', graph.id, 'group'], value, next);
|
this.postSingle(['Graph', graph.id, 'group'], value, next);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -216,24 +216,21 @@ export class PlotComponent implements AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
private points(graph: Graph, min: Dataset, mid: Dataset, max: Dataset): void {
|
private points(graph: Graph, min: Dataset, mid: Dataset, max: Dataset): void {
|
||||||
this.seriesService.oneSeriesPoints(
|
this.seriesService.oneSeriesPoints(
|
||||||
graph.series,
|
graph,
|
||||||
graph.axis.plot.interval,
|
|
||||||
graph.axis.plot.offset,
|
|
||||||
graph.axis.plot.duration,
|
|
||||||
points => {
|
points => {
|
||||||
if (graph.series.type === SeriesType.BOOL) {
|
if (graph.series.type === SeriesType.BOOL) {
|
||||||
mid.data = toBool(points, graph.factor);
|
mid.data = toBool(points);
|
||||||
} else if (graph.series.type === SeriesType.DELTA) {
|
} else if (graph.series.type === SeriesType.DELTA) {
|
||||||
mid.data = toDelta(points, graph.factor);
|
mid.data = toDelta(points);
|
||||||
} else if (graph.series.type === SeriesType.VARYING) {
|
} else if (graph.series.type === SeriesType.VARYING) {
|
||||||
if (min) {
|
if (min) {
|
||||||
min.data = toMin(points, graph.factor);
|
min.data = toMin(points);
|
||||||
}
|
}
|
||||||
if (max) {
|
if (max) {
|
||||||
max.data = toMax(points, graph.factor);
|
max.data = toMax(points);
|
||||||
}
|
}
|
||||||
if (mid) {
|
if (mid) {
|
||||||
mid.data = toAvg(points, graph.factor);
|
mid.data = toAvg(points);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.chart.update();
|
this.chart.update();
|
||||||
@ -264,14 +261,13 @@ export class PlotComponent implements AfterViewInit, OnDestroy {
|
|||||||
private updatePoint(graph: Graph, dataset: Dataset, date: Date, y: number): void {
|
private updatePoint(graph: Graph, dataset: Dataset, date: Date, y: number): void {
|
||||||
const x = date.getTime();
|
const x = date.getTime();
|
||||||
const point = dataset.data.filter((p: PointElement) => p.x === x)[0];
|
const point = dataset.data.filter((p: PointElement) => p.x === x)[0];
|
||||||
const yMultiplied = y * graph.factor;
|
|
||||||
if (point) {
|
if (point) {
|
||||||
if (point.y !== yMultiplied) {
|
if (point.y !== y) {
|
||||||
point.y = yMultiplied;
|
point.y = y * graph.factor; // TODO this is wrong. we need to take GraphOperation into account
|
||||||
this.chart.update();
|
this.chart.update();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dataset.data.push({x: x, y: yMultiplied}); // TODO check if this is a LIVE/SCROLLING plot (right end of plot is 'now')
|
dataset.data.push({x: x, y: y}); // TODO check if this is a LIVE/SCROLLING plot (right end of plot is 'now')
|
||||||
this.chart.update();
|
this.chart.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,10 +9,7 @@ export class Point {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PointMapper = (p: number[][], factor: number) => Point[];
|
export function toBool(points: number[][]): Point[] {
|
||||||
|
|
||||||
// noinspection JSUnusedLocalSymbols
|
|
||||||
export function toBool(points: number[][], factor: number): Point[] {
|
|
||||||
const result = [];
|
const result = [];
|
||||||
let postPone: Point | null = null;
|
let postPone: Point | null = null;
|
||||||
for (const p of points) {
|
for (const p of points) {
|
||||||
@ -44,45 +41,45 @@ export function toBool(points: number[][], factor: number): Point[] {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toDelta(points: number[][], factor: number): Point[] {
|
export function toDelta(points: number[][]): Point[] {
|
||||||
const result = [];
|
const result = [];
|
||||||
for (const p of points) {
|
for (const p of points) {
|
||||||
result.push({
|
result.push({
|
||||||
x: p[0] * 1000,
|
x: p[0] * 1000,
|
||||||
y: (p[1]) * factor,
|
y: (p[1]),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toMin(points: number[][], factor: number): Point[] {
|
export function toMin(points: number[][]): Point[] {
|
||||||
const result = [];
|
const result = [];
|
||||||
for (const p of points) {
|
for (const p of points) {
|
||||||
result.push({
|
result.push({
|
||||||
x: p[0] * 1000,
|
x: p[0] * 1000,
|
||||||
y: p[1] * factor,
|
y: p[1],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toMax(points: number[][], factor: number): Point[] {
|
export function toMax(points: number[][]): Point[] {
|
||||||
const result = [];
|
const result = [];
|
||||||
for (const p of points) {
|
for (const p of points) {
|
||||||
result.push({
|
result.push({
|
||||||
x: p[0] * 1000,
|
x: p[0] * 1000,
|
||||||
y: p[2] * factor,
|
y: p[2],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toAvg(points: number[][], factor: number): Point[] {
|
export function toAvg(points: number[][]): Point[] {
|
||||||
const result = [];
|
const result = [];
|
||||||
for (const p of points) {
|
for (const p of points) {
|
||||||
result.push({
|
result.push({
|
||||||
x: p[0] * 1000,
|
x: p[0] * 1000,
|
||||||
y: p[3] * factor,
|
y: p[3],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import {Series} from './Series';
|
|||||||
import {Interval} from './Interval';
|
import {Interval} from './Interval';
|
||||||
import {AllSeriesPointResponse} from './AllSeriesPointResponse';
|
import {AllSeriesPointResponse} from './AllSeriesPointResponse';
|
||||||
import {AllSeriesPointRequest} from './AllSeriesPointRequest';
|
import {AllSeriesPointRequest} from './AllSeriesPointRequest';
|
||||||
|
import {Graph} from "../plot/axis/graph/Graph";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@ -16,12 +17,16 @@ export class SeriesService extends EntityListService<Series> {
|
|||||||
super(api, ['Series'], Series.fromJson, Series.equals, Series.compareName);
|
super(api, ['Series'], Series.fromJson, Series.equals, Series.compareName);
|
||||||
}
|
}
|
||||||
|
|
||||||
oneSeriesPoints(series: Series, interval: Interval, offset: number, duration: number, next: Next<number[][]>): void {
|
oneSeriesPoints(graph: Graph, next: Next<number[][]>): void {
|
||||||
const request = {
|
const request = {
|
||||||
id: series.id,
|
id: graph.series.id,
|
||||||
interval: interval.name,
|
factor: graph.factor,
|
||||||
offset: offset,
|
operation: graph.operation,
|
||||||
duration: duration,
|
id2: graph.series2?.id,
|
||||||
|
factor2: graph.factor2,
|
||||||
|
interval: graph.axis.plot.interval.name,
|
||||||
|
offset: graph.axis.plot.offset,
|
||||||
|
duration: graph.axis.plot.duration,
|
||||||
};
|
};
|
||||||
this.api.postList([...this.path, 'oneSeriesPoints'], request, (outer: any[]) => outer.map(validateNumber), next);
|
this.api.postList([...this.path, 'oneSeriesPoints'], request, (outer: any[]) => outer.map(validateNumber), next);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,7 +43,6 @@ public class DemoService {
|
|||||||
@EventListener(ApplicationReadyEvent.class)
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
public void init() {
|
public void init() {
|
||||||
topics();
|
topics();
|
||||||
// plots();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void topics() {
|
private void topics() {
|
||||||
@ -168,6 +167,7 @@ public class DemoService {
|
|||||||
zuhauseEnergie();
|
zuhauseEnergie();
|
||||||
zuhauseTemperatur();
|
zuhauseTemperatur();
|
||||||
eltern();
|
eltern();
|
||||||
|
leistungVergleich();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void zuhauseEnergie() {
|
private void zuhauseEnergie() {
|
||||||
@ -181,30 +181,33 @@ public class DemoService {
|
|||||||
energy.setName("Energie");
|
energy.setName("Energie");
|
||||||
energy.setUnit("kWh");
|
energy.setUnit("kWh");
|
||||||
|
|
||||||
final Series electricityEnergyPurchase = seriesRepository.findByName("electricity/energy/purchase").orElseThrow();
|
final String stack = "a";
|
||||||
final Graph electricityEnergyPurchaseGraph = graphRepository.save(new Graph(energy, electricityEnergyPurchase));
|
|
||||||
electricityEnergyPurchaseGraph.setType(GraphType.BAR);
|
|
||||||
electricityEnergyPurchaseGraph.setStack("a");
|
|
||||||
electricityEnergyPurchaseGraph.setName("Bezug");
|
|
||||||
electricityEnergyPurchaseGraph.setColor("#FF8800");
|
|
||||||
energy.addGraph(electricityEnergyPurchaseGraph);
|
|
||||||
|
|
||||||
final Series electricityEnergyDelivery = seriesRepository.findByName("electricity/energy/delivery").orElseThrow();
|
final Series electricityEnergyDelivery = seriesRepository.findByName("electricity/energy/delivery").orElseThrow();
|
||||||
final Graph electricityEnergyDeliveryGraph = graphRepository.save(new Graph(energy, electricityEnergyDelivery));
|
final Graph electricityEnergyDeliveryGraph = graphRepository.save(new Graph(energy, electricityEnergyDelivery));
|
||||||
electricityEnergyDeliveryGraph.setType(GraphType.BAR);
|
electricityEnergyDeliveryGraph.setType(GraphType.BAR);
|
||||||
electricityEnergyDeliveryGraph.setStack("a");
|
electricityEnergyDeliveryGraph.setStack(stack);
|
||||||
electricityEnergyDeliveryGraph.setName("Überschuss");
|
electricityEnergyDeliveryGraph.setName("Zuhause Überschuss");
|
||||||
electricityEnergyDeliveryGraph.setColor("#FF00FF");
|
electricityEnergyDeliveryGraph.setColor("#FF00FF");
|
||||||
electricityEnergyDeliveryGraph.setFactor(-1);
|
electricityEnergyDeliveryGraph.setFactor(-1);
|
||||||
energy.addGraph(electricityEnergyDeliveryGraph);
|
energy.addGraph(electricityEnergyDeliveryGraph);
|
||||||
|
|
||||||
final Series electricityEnergyProduce = seriesRepository.findByName("electricity/energy/produce").orElseThrow();
|
final Series electricityEnergyProduce = seriesRepository.findByName("electricity/energy/produce").orElseThrow();
|
||||||
final Graph electricityEnergyProduceGraph = graphRepository.save(new Graph(energy, electricityEnergyProduce));
|
final Graph electricityEnergyProduceGraph = graphRepository.save(new Graph(energy, electricityEnergyProduce));
|
||||||
|
electricityEnergyProduceGraph.setSeries2(electricityEnergyDelivery);
|
||||||
|
electricityEnergyProduceGraph.setName("Zuhause Eigenverbrauch");
|
||||||
electricityEnergyProduceGraph.setType(GraphType.BAR);
|
electricityEnergyProduceGraph.setType(GraphType.BAR);
|
||||||
electricityEnergyProduceGraph.setStack("a");
|
electricityEnergyProduceGraph.setStack(stack);
|
||||||
electricityEnergyProduceGraph.setName("Produktion");
|
electricityEnergyProduceGraph.setColor("#008800");
|
||||||
electricityEnergyProduceGraph.setColor("#0000FF");
|
|
||||||
energy.addGraph(electricityEnergyProduceGraph);
|
energy.addGraph(electricityEnergyProduceGraph);
|
||||||
|
|
||||||
|
final Series electricityEnergyPurchase = seriesRepository.findByName("electricity/energy/purchase").orElseThrow();
|
||||||
|
final Graph electricityEnergyPurchaseGraph = graphRepository.save(new Graph(energy, electricityEnergyPurchase));
|
||||||
|
electricityEnergyPurchaseGraph.setType(GraphType.BAR);
|
||||||
|
electricityEnergyPurchaseGraph.setStack(stack);
|
||||||
|
electricityEnergyPurchaseGraph.setName("Zuhause Bezug");
|
||||||
|
electricityEnergyPurchaseGraph.setColor("#FF8800");
|
||||||
|
energy.addGraph(electricityEnergyPurchaseGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void zuhauseTemperatur() {
|
private void zuhauseTemperatur() {
|
||||||
@ -236,6 +239,24 @@ public class DemoService {
|
|||||||
gardenTemperatureGraph.setAvg(false);
|
gardenTemperatureGraph.setAvg(false);
|
||||||
gardenTemperatureGraph.setMax(true);
|
gardenTemperatureGraph.setMax(true);
|
||||||
temperature.addGraph(gardenTemperatureGraph);
|
temperature.addGraph(gardenTemperatureGraph);
|
||||||
|
|
||||||
|
final Series bufferTemperature = seriesRepository.findByName("heating/buffer/inside/temperature").orElseThrow();
|
||||||
|
final Graph bufferTemperatureGraph = graphRepository.save(new Graph(temperature, bufferTemperature));
|
||||||
|
bufferTemperatureGraph.setName("Puffer");
|
||||||
|
bufferTemperatureGraph.setColor("#FF00FF");
|
||||||
|
bufferTemperatureGraph.setMin(true);
|
||||||
|
bufferTemperatureGraph.setAvg(false);
|
||||||
|
bufferTemperatureGraph.setMax(true);
|
||||||
|
temperature.addGraph(bufferTemperatureGraph);
|
||||||
|
|
||||||
|
final Series circuitTemperature = seriesRepository.findByName("heating/circuit/supply/temperature").orElseThrow();
|
||||||
|
final Graph circuitTemperatureGraph = graphRepository.save(new Graph(temperature, circuitTemperature));
|
||||||
|
circuitTemperatureGraph.setName("Heizkreis");
|
||||||
|
circuitTemperatureGraph.setColor("#FF0000");
|
||||||
|
circuitTemperatureGraph.setMin(true);
|
||||||
|
circuitTemperatureGraph.setAvg(false);
|
||||||
|
circuitTemperatureGraph.setMax(true);
|
||||||
|
temperature.addGraph(circuitTemperatureGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void eltern() {
|
private void eltern() {
|
||||||
@ -249,14 +270,6 @@ public class DemoService {
|
|||||||
energy.setName("Energie");
|
energy.setName("Energie");
|
||||||
energy.setUnit("kWh");
|
energy.setUnit("kWh");
|
||||||
|
|
||||||
final Series electricityEnergyPurchase = seriesRepository.findByName("eltern/electricity/energy/purchase").orElseThrow();
|
|
||||||
final Graph electricityEnergyPurchaseGraph = graphRepository.save(new Graph(energy, electricityEnergyPurchase));
|
|
||||||
electricityEnergyPurchaseGraph.setName("Bezug");
|
|
||||||
electricityEnergyPurchaseGraph.setType(GraphType.BAR);
|
|
||||||
electricityEnergyPurchaseGraph.setStack("a");
|
|
||||||
electricityEnergyPurchaseGraph.setColor("#FF8800");
|
|
||||||
energy.addGraph(electricityEnergyPurchaseGraph);
|
|
||||||
|
|
||||||
final Series electricityEnergyDelivery = seriesRepository.findByName("eltern/electricity/energy/delivery").orElseThrow();
|
final Series electricityEnergyDelivery = seriesRepository.findByName("eltern/electricity/energy/delivery").orElseThrow();
|
||||||
final Graph electricityEnergyDeliveryGraph = graphRepository.save(new Graph(energy, electricityEnergyDelivery));
|
final Graph electricityEnergyDeliveryGraph = graphRepository.save(new Graph(energy, electricityEnergyDelivery));
|
||||||
electricityEnergyDeliveryGraph.setName("Überschuss");
|
electricityEnergyDeliveryGraph.setName("Überschuss");
|
||||||
@ -268,11 +281,46 @@ public class DemoService {
|
|||||||
|
|
||||||
final Series electricityEnergyProduce = seriesRepository.findByName("eltern/electricity/energy/produce").orElseThrow();
|
final Series electricityEnergyProduce = seriesRepository.findByName("eltern/electricity/energy/produce").orElseThrow();
|
||||||
final Graph electricityEnergyProduceGraph = graphRepository.save(new Graph(energy, electricityEnergyProduce));
|
final Graph electricityEnergyProduceGraph = graphRepository.save(new Graph(energy, electricityEnergyProduce));
|
||||||
electricityEnergyProduceGraph.setName("Produktion");
|
electricityEnergyProduceGraph.setSeries2(electricityEnergyDelivery);
|
||||||
|
electricityEnergyProduceGraph.setName("Eigenverbrauch");
|
||||||
electricityEnergyProduceGraph.setType(GraphType.BAR);
|
electricityEnergyProduceGraph.setType(GraphType.BAR);
|
||||||
electricityEnergyProduceGraph.setStack("a");
|
electricityEnergyProduceGraph.setStack("a");
|
||||||
electricityEnergyProduceGraph.setColor("#0000FF");
|
electricityEnergyProduceGraph.setColor("#008800");
|
||||||
energy.addGraph(electricityEnergyProduceGraph);
|
energy.addGraph(electricityEnergyProduceGraph);
|
||||||
|
|
||||||
|
final Series electricityEnergyPurchase = seriesRepository.findByName("eltern/electricity/energy/purchase").orElseThrow();
|
||||||
|
final Graph electricityEnergyPurchaseGraph = graphRepository.save(new Graph(energy, electricityEnergyPurchase));
|
||||||
|
electricityEnergyPurchaseGraph.setName("Bezug");
|
||||||
|
electricityEnergyPurchaseGraph.setType(GraphType.BAR);
|
||||||
|
electricityEnergyPurchaseGraph.setStack("a");
|
||||||
|
electricityEnergyPurchaseGraph.setColor("#FF8800");
|
||||||
|
energy.addGraph(electricityEnergyPurchaseGraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void leistungVergleich() {
|
||||||
|
final Plot plot = plotRepository.save(new Plot(plotRepository.count()));
|
||||||
|
plot.setName("Leistung Vergleich");
|
||||||
|
|
||||||
|
final Axis power = axisRepository.save(new Axis(plot));
|
||||||
|
plot.addAxis(power);
|
||||||
|
plot.setDashboard(true);
|
||||||
|
power.setRight(true);
|
||||||
|
power.setName("Leistung");
|
||||||
|
power.setUnit("W");
|
||||||
|
|
||||||
|
final Series electricityPowerProduce = seriesRepository.findByName("electricity/power/produce").orElseThrow();
|
||||||
|
final Graph electricityPowerProduceGraph = graphRepository.save(new Graph(power, electricityPowerProduce));
|
||||||
|
electricityPowerProduceGraph.setName("Zuhause");
|
||||||
|
electricityPowerProduceGraph.setType(GraphType.BAR);
|
||||||
|
electricityPowerProduceGraph.setColor("#008800");
|
||||||
|
power.addGraph(electricityPowerProduceGraph);
|
||||||
|
|
||||||
|
final Series elternElectricityPowerProduce = seriesRepository.findByName("eltern/electricity/power/produce").orElseThrow();
|
||||||
|
final Graph elternElectricityPowerProduceGraph = graphRepository.save(new Graph(power, elternElectricityPowerProduce));
|
||||||
|
elternElectricityPowerProduceGraph.setName("Eltern");
|
||||||
|
elternElectricityPowerProduceGraph.setType(GraphType.BAR);
|
||||||
|
elternElectricityPowerProduceGraph.setColor("#0088FF");
|
||||||
|
power.addGraph(elternElectricityPowerProduceGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package de.ph87.data;
|
|||||||
import jakarta.annotation.Nullable;
|
import jakarta.annotation.Nullable;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
|
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class Helpers {
|
public class Helpers {
|
||||||
@ -15,6 +16,14 @@ public class Helpers {
|
|||||||
return mapper.apply(t);
|
return mapper.apply(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static <T, U, R> R map(@Nullable final T t, @NonNull final U u, @NonNull final BiFunction<T, U, R> mapper) {
|
||||||
|
if (t == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return mapper.apply(t, u);
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static <T> T or(@Nullable final T t, @NonNull final T r) {
|
public static <T> T or(@Nullable final T t, @NonNull final T r) {
|
||||||
if (t == null) {
|
if (t == null) {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package de.ph87.data.plot.axis.graph;
|
|||||||
|
|
||||||
import de.ph87.data.plot.axis.Axis;
|
import de.ph87.data.plot.axis.Axis;
|
||||||
import de.ph87.data.series.Series;
|
import de.ph87.data.series.Series;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.EnumType;
|
import jakarta.persistence.EnumType;
|
||||||
@ -41,6 +42,24 @@ public class Graph {
|
|||||||
@ManyToOne(optional = false)
|
@ManyToOne(optional = false)
|
||||||
private Series series;
|
private Series series;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Column(nullable = false)
|
||||||
|
private double factor = 1;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Column(nullable = false)
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private GraphOperation operation = GraphOperation.MINUS;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Nullable
|
||||||
|
@ManyToOne
|
||||||
|
private Series series2;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Column(nullable = false)
|
||||||
|
private double factor2 = 1;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
@NonNull
|
@NonNull
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
@ -66,10 +85,6 @@ public class Graph {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String color = "#FF0000";
|
private String color = "#FF0000";
|
||||||
|
|
||||||
@Setter
|
|
||||||
@Column(nullable = false)
|
|
||||||
private double factor = 1;
|
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
@NonNull
|
@NonNull
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
|
|||||||
@ -56,16 +56,31 @@ public class GraphController {
|
|||||||
return graphService.set(id, graph -> graph.setSeries(seriesRepository.findById(value).orElseThrow()));
|
return graphService.set(id, graph -> graph.setSeries(seriesRepository.findById(value).orElseThrow()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("{id}/color")
|
|
||||||
public PlotDto color(@PathVariable final long id, @RequestBody @NonNull final String value) {
|
|
||||||
return graphService.set(id, graph -> graph.setColor(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("{id}/factor")
|
@PostMapping("{id}/factor")
|
||||||
public PlotDto factor(@PathVariable final long id, @RequestBody final double value) {
|
public PlotDto factor(@PathVariable final long id, @RequestBody final double value) {
|
||||||
return graphService.set(id, graph -> graph.setFactor(value));
|
return graphService.set(id, graph -> graph.setFactor(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("{id}/operation")
|
||||||
|
public PlotDto operation(@PathVariable final long id, @RequestBody @NonNull final String value) {
|
||||||
|
return graphService.set(id, graph -> graph.setOperation(GraphOperation.valueOf(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("{id}/series2")
|
||||||
|
public PlotDto series2(@PathVariable final long id, @RequestBody(required = false) @Nullable final Long value) {
|
||||||
|
return graphService.set(id, graph -> graph.setSeries2(value == null ? null : seriesRepository.findById(value).orElseThrow()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("{id}/factor2")
|
||||||
|
public PlotDto factor2(@PathVariable final long id, @RequestBody final double value) {
|
||||||
|
return graphService.set(id, graph -> graph.setFactor2(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("{id}/color")
|
||||||
|
public PlotDto color(@PathVariable final long id, @RequestBody @NonNull final String value) {
|
||||||
|
return graphService.set(id, graph -> graph.setColor(value));
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("{id}/group")
|
@PostMapping("{id}/group")
|
||||||
public PlotDto group(@PathVariable final long id, @RequestBody @NonNull final String value) {
|
public PlotDto group(@PathVariable final long id, @RequestBody @NonNull final String value) {
|
||||||
return graphService.set(id, graph -> graph.setGroup(Group.valueOf(value)));
|
return graphService.set(id, graph -> graph.setGroup(Group.valueOf(value)));
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
package de.ph87.data.plot.axis.graph;
|
||||||
|
|
||||||
|
public class GraphDivisionByZero extends Exception {
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,9 +1,12 @@
|
|||||||
package de.ph87.data.plot.axis.graph;
|
package de.ph87.data.plot.axis.graph;
|
||||||
|
|
||||||
import de.ph87.data.series.SeriesDto;
|
import de.ph87.data.series.SeriesDto;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
|
|
||||||
|
import static de.ph87.data.Helpers.map;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class GraphDto {
|
public class GraphDto {
|
||||||
|
|
||||||
@ -14,6 +17,15 @@ public class GraphDto {
|
|||||||
@NonNull
|
@NonNull
|
||||||
public final SeriesDto series;
|
public final SeriesDto series;
|
||||||
|
|
||||||
|
public final double factor;
|
||||||
|
|
||||||
|
public final GraphOperation operation;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public final SeriesDto series2;
|
||||||
|
|
||||||
|
public final double factor2;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
@ -26,8 +38,6 @@ public class GraphDto {
|
|||||||
@NonNull
|
@NonNull
|
||||||
public final String color;
|
public final String color;
|
||||||
|
|
||||||
public final double factor;
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public final Group group;
|
public final Group group;
|
||||||
|
|
||||||
@ -44,12 +54,15 @@ public class GraphDto {
|
|||||||
this.id = graph.getId();
|
this.id = graph.getId();
|
||||||
this.version = graph.getVersion();
|
this.version = graph.getVersion();
|
||||||
this.series = new SeriesDto(graph.getSeries(), false);
|
this.series = new SeriesDto(graph.getSeries(), false);
|
||||||
|
this.factor = graph.getFactor();
|
||||||
|
this.operation = graph.getOperation();
|
||||||
|
this.series2 = map(graph.getSeries2(), false, SeriesDto::new);
|
||||||
|
this.factor2 = graph.getFactor2();
|
||||||
this.name = graph.getName();
|
this.name = graph.getName();
|
||||||
this.visible = graph.isVisible();
|
this.visible = graph.isVisible();
|
||||||
this.type = graph.getType();
|
this.type = graph.getType();
|
||||||
this.fill = graph.getFill();
|
this.fill = graph.getFill();
|
||||||
this.color = graph.getColor();
|
this.color = graph.getColor();
|
||||||
this.factor = graph.getFactor();
|
|
||||||
this.group = graph.getGroup();
|
this.group = graph.getGroup();
|
||||||
this.stack = graph.getStack();
|
this.stack = graph.getStack();
|
||||||
this.min = graph.isMin();
|
this.min = graph.isMin();
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
package de.ph87.data.plot.axis.graph;
|
||||||
|
|
||||||
|
import lombok.NonNull;
|
||||||
|
|
||||||
|
public enum GraphOperation {
|
||||||
|
MINUS((s0, s1) -> s0 - s1),
|
||||||
|
PLUS(Double::sum),
|
||||||
|
DIVIDE((s0, s1) -> {
|
||||||
|
if (s1 == 0) {
|
||||||
|
throw new GraphDivisionByZero();
|
||||||
|
}
|
||||||
|
return s0 / s1;
|
||||||
|
}),
|
||||||
|
;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final GraphOperationFunction<Double, Double, Double> function;
|
||||||
|
|
||||||
|
GraphOperation(@NonNull final GraphOperationFunction<Double, Double, Double> function) {
|
||||||
|
this.function = function;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double apply(final double a, final double b) throws GraphDivisionByZero {
|
||||||
|
return function.apply(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package de.ph87.data.plot.axis.graph;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface GraphOperationFunction<A, B, R> {
|
||||||
|
|
||||||
|
R apply(A a, B b) throws GraphDivisionByZero;
|
||||||
|
|
||||||
|
}
|
||||||
@ -22,7 +22,7 @@ public class AllSeriesPointResponse {
|
|||||||
public final SeriesDto series;
|
public final SeriesDto series;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public final SeriesPoint point;
|
public final SeriesPoint<?> point;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,9 @@ package de.ph87.data.series;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import de.ph87.data.plot.axis.graph.GraphOperation;
|
||||||
import de.ph87.data.series.data.Interval;
|
import de.ph87.data.series.data.Interval;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
|
|
||||||
@ -13,6 +15,15 @@ public class OneSeriesPointsRequest implements ISeriesPointRequest {
|
|||||||
|
|
||||||
public final long id;
|
public final long id;
|
||||||
|
|
||||||
|
public final double factor;
|
||||||
|
|
||||||
|
public final GraphOperation operation;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public final Long id2;
|
||||||
|
|
||||||
|
public final double factor2;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public final Interval interval;
|
public final Interval interval;
|
||||||
|
|
||||||
@ -30,11 +41,19 @@ public class OneSeriesPointsRequest implements ISeriesPointRequest {
|
|||||||
|
|
||||||
public OneSeriesPointsRequest(
|
public OneSeriesPointsRequest(
|
||||||
@JsonProperty("id") final long id,
|
@JsonProperty("id") final long id,
|
||||||
|
@JsonProperty("factor") final double factor,
|
||||||
|
@JsonProperty("operation") final GraphOperation operation,
|
||||||
|
@JsonProperty("id2") @Nullable final Long id2,
|
||||||
|
@JsonProperty("factor2") final double factor2,
|
||||||
@JsonProperty("interval") final Interval interval,
|
@JsonProperty("interval") final Interval interval,
|
||||||
@JsonProperty("offset") final long offset,
|
@JsonProperty("offset") final long offset,
|
||||||
@JsonProperty("duration") final long duration
|
@JsonProperty("duration") final long duration
|
||||||
) {
|
) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
this.factor = factor;
|
||||||
|
this.operation = operation;
|
||||||
|
this.id2 = id2;
|
||||||
|
this.factor2 = factor2;
|
||||||
this.interval = interval;
|
this.interval = interval;
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
|
|||||||
@ -9,6 +9,6 @@ import java.util.List;
|
|||||||
@JsonSerialize(using = OneSeriesPointsResponseSerializer.class)
|
@JsonSerialize(using = OneSeriesPointsResponseSerializer.class)
|
||||||
public class OneSeriesPointsResponse {
|
public class OneSeriesPointsResponse {
|
||||||
|
|
||||||
public final List<? extends SeriesPoint> points;
|
public final List<? extends SeriesPoint<?>> points;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,67 @@
|
|||||||
package de.ph87.data.series;
|
package de.ph87.data.series;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonGenerator;
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import de.ph87.data.plot.axis.graph.GraphDivisionByZero;
|
||||||
|
import de.ph87.data.plot.axis.graph.GraphOperation;
|
||||||
|
import lombok.NonNull;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface SeriesPoint {
|
public interface SeriesPoint<T extends SeriesPoint<T>> {
|
||||||
|
|
||||||
|
ZonedDateTime getDate();
|
||||||
|
|
||||||
|
double getValue();
|
||||||
|
|
||||||
void toJson(final JsonGenerator jsonGenerator) throws IOException;
|
void toJson(final JsonGenerator jsonGenerator) throws IOException;
|
||||||
|
|
||||||
|
T times(final double factor);
|
||||||
|
|
||||||
|
T combine(@NonNull final SeriesPoint<?> other, @NonNull final GraphOperation operation) throws GraphDivisionByZero;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
static List<SeriesPoint<?>> combine(@NonNull final List<? extends SeriesPoint<?>> points1, @NonNull final List<? extends SeriesPoint<?>> points2, @NonNull final GraphOperation operation) {
|
||||||
|
if (points1.isEmpty() || points2.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
final List<SeriesPoint<?>> as = new ArrayList<>(points1);
|
||||||
|
final List<SeriesPoint<?>> bs = new ArrayList<>(points2);
|
||||||
|
SeriesPoint<?> a = as.removeFirst();
|
||||||
|
SeriesPoint<?> b = bs.removeFirst();
|
||||||
|
final List<SeriesPoint<?>> result = new ArrayList<>(Math.min(as.size(), bs.size()));
|
||||||
|
while (true) {
|
||||||
|
final int diff = a.getDate().compareTo(b.getDate());
|
||||||
|
if (diff == 0) {
|
||||||
|
try {
|
||||||
|
result.add(a.combine(b, operation));
|
||||||
|
} catch (GraphDivisionByZero e) {
|
||||||
|
// just not add
|
||||||
|
}
|
||||||
|
a = null;
|
||||||
|
b = null;
|
||||||
|
} else if (diff < 0) {
|
||||||
|
a = null;
|
||||||
|
} else {
|
||||||
|
b = null;
|
||||||
|
}
|
||||||
|
if (a == null) {
|
||||||
|
if (as.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
a = as.removeFirst();
|
||||||
|
}
|
||||||
|
if (b == null) {
|
||||||
|
if (bs.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
b = bs.removeFirst();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package de.ph87.data.series;
|
|||||||
import de.ph87.data.series.data.bool.BoolService;
|
import de.ph87.data.series.data.bool.BoolService;
|
||||||
import de.ph87.data.series.data.delta.DeltaService;
|
import de.ph87.data.series.data.delta.DeltaService;
|
||||||
import de.ph87.data.series.data.varying.VaryingService;
|
import de.ph87.data.series.data.varying.VaryingService;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -27,8 +28,14 @@ public class SeriesService {
|
|||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public OneSeriesPointsResponse oneSeriesPoints(@NonNull final OneSeriesPointsRequest request) {
|
public OneSeriesPointsResponse oneSeriesPoints(@NonNull final OneSeriesPointsRequest request) {
|
||||||
final Series series = seriesRepository.findById(request.id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
final Series series1 = seriesRepository.findById(request.id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||||
return new OneSeriesPointsResponse(getSeriesPoints(series, request));
|
final List<? extends SeriesPoint<?>> points1 = getSeriesPoints(series1, request, request.factor);
|
||||||
|
if (request.id2 != null) {
|
||||||
|
final Series series2 = seriesRepository.findById(request.id2).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||||
|
final List<? extends SeriesPoint<?>> points2 = getSeriesPoints(series2, request, request.factor2);
|
||||||
|
return new OneSeriesPointsResponse(SeriesPoint.combine(points1, points2, request.operation));
|
||||||
|
}
|
||||||
|
return new OneSeriesPointsResponse(points1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@ -39,19 +46,23 @@ public class SeriesService {
|
|||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private AllSeriesPointResponse.Entry map(@NonNull final Series series, @NonNull final ISeriesPointRequest request) {
|
private AllSeriesPointResponse.Entry map(@NonNull final Series series, @NonNull final ISeriesPointRequest request) {
|
||||||
final List<? extends SeriesPoint> points = getSeriesPoints(series, request);
|
final List<? extends SeriesPoint<?>> points = getSeriesPoints(series, request, null);
|
||||||
final SeriesDto seriesDto = new SeriesDto(series, false);
|
final SeriesDto seriesDto = new SeriesDto(series, false);
|
||||||
final SeriesPoint point = points.isEmpty() ? null : points.getFirst();
|
final SeriesPoint<?> point = points.isEmpty() ? null : points.getFirst();
|
||||||
return new AllSeriesPointResponse.Entry(seriesDto, point);
|
return new AllSeriesPointResponse.Entry(seriesDto, point);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private List<? extends SeriesPoint> getSeriesPoints(@NonNull final Series series, @NonNull final ISeriesPointRequest request) {
|
private List<? extends SeriesPoint<?>> getSeriesPoints(@NonNull final Series series, @NonNull final ISeriesPointRequest request, @Nullable final Double factor) {
|
||||||
return switch (series.getType()) {
|
final List<? extends SeriesPoint<?>> points = switch (series.getType()) {
|
||||||
case BOOL -> boolService.points(series, request);
|
case BOOL -> boolService.points(series, request);
|
||||||
case DELTA -> deltaService.points(series, request);
|
case DELTA -> deltaService.points(series, request);
|
||||||
case VARYING -> varyingService.points(series, request);
|
case VARYING -> varyingService.points(series, request);
|
||||||
};
|
};
|
||||||
|
if (factor == null || factor == 1) {
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
return points.stream().map(p -> p.times(factor)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,17 @@
|
|||||||
package de.ph87.data.series.data.bool;
|
package de.ph87.data.series.data.bool;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonGenerator;
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import de.ph87.data.plot.axis.graph.GraphDivisionByZero;
|
||||||
|
import de.ph87.data.plot.axis.graph.GraphOperation;
|
||||||
import de.ph87.data.series.SeriesPoint;
|
import de.ph87.data.series.SeriesPoint;
|
||||||
|
import lombok.Data;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
|
|
||||||
@SuppressWarnings("unused") // used by repository query
|
@Data
|
||||||
public class BoolPoint implements SeriesPoint {
|
public class BoolPoint implements SeriesPoint<BoolPoint> {
|
||||||
|
|
||||||
public final ZonedDateTime begin;
|
public final ZonedDateTime begin;
|
||||||
|
|
||||||
@ -25,6 +28,13 @@ public class BoolPoint implements SeriesPoint {
|
|||||||
this.terminated = bool.isTerminated();
|
this.terminated = bool.isTerminated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BoolPoint(final ZonedDateTime begin, final ZonedDateTime end, final boolean state, final boolean terminated) {
|
||||||
|
this.begin = begin;
|
||||||
|
this.end = end;
|
||||||
|
this.state = state;
|
||||||
|
this.terminated = terminated;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toJson(final JsonGenerator jsonGenerator) throws IOException {
|
public void toJson(final JsonGenerator jsonGenerator) throws IOException {
|
||||||
jsonGenerator.writeNumber(begin.toEpochSecond());
|
jsonGenerator.writeNumber(begin.toEpochSecond());
|
||||||
@ -33,4 +43,24 @@ public class BoolPoint implements SeriesPoint {
|
|||||||
jsonGenerator.writeNumber(terminated ? 1 : 0);
|
jsonGenerator.writeNumber(terminated ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BoolPoint times(final double factor) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZonedDateTime getDate() {
|
||||||
|
return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getValue() {
|
||||||
|
return state ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BoolPoint combine(@NonNull final SeriesPoint<?> other, @NonNull final GraphOperation operation) throws GraphDivisionByZero {
|
||||||
|
return new BoolPoint(begin, end, operation.apply(getValue(), other.getValue()) > 0, terminated);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,17 @@
|
|||||||
package de.ph87.data.series.data.delta;
|
package de.ph87.data.series.data.delta;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonGenerator;
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import de.ph87.data.plot.axis.graph.GraphDivisionByZero;
|
||||||
|
import de.ph87.data.plot.axis.graph.GraphOperation;
|
||||||
import de.ph87.data.series.SeriesPoint;
|
import de.ph87.data.series.SeriesPoint;
|
||||||
|
import lombok.Data;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
|
|
||||||
@SuppressWarnings("unused") // used by repository query
|
@Data
|
||||||
public class DeltaPoint implements SeriesPoint {
|
public class DeltaPoint implements SeriesPoint<DeltaPoint> {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public final ZonedDateTime date;
|
public final ZonedDateTime date;
|
||||||
@ -26,4 +29,19 @@ public class DeltaPoint implements SeriesPoint {
|
|||||||
jsonGenerator.writeNumber(delta);
|
jsonGenerator.writeNumber(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DeltaPoint times(final double factor) {
|
||||||
|
return new DeltaPoint(date, delta * factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getValue() {
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DeltaPoint combine(@NonNull final SeriesPoint<?> other, @NonNull final GraphOperation operation) throws GraphDivisionByZero {
|
||||||
|
return new DeltaPoint(date, operation.apply(getValue(), other.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import java.util.List;
|
|||||||
public interface DeltaRepo<T extends Delta> extends CrudRepository<T, DeltaId> {
|
public interface DeltaRepo<T extends Delta> extends CrudRepository<T, DeltaId> {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Query("select new de.ph87.data.series.data.delta.DeltaPoint(e.id.date, sum(e.last - e.first)) from #{#entityName} e where e.id.meter.series = :series and e.id.date >= :first and e.id.date < :after group by e.id.date")
|
@Query("select new de.ph87.data.series.data.delta.DeltaPoint(e.id.date, sum((e.last - e.first))) from #{#entityName} e where e.id.meter.series = :series and e.id.date >= :first and e.id.date < :after group by e.id.date")
|
||||||
List<DeltaPoint> points(@NonNull Series series, @NonNull ZonedDateTime first, @NonNull ZonedDateTime after);
|
List<DeltaPoint> points(@NonNull Series series, @NonNull ZonedDateTime first, @NonNull ZonedDateTime after);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,17 @@
|
|||||||
package de.ph87.data.series.data.varying;
|
package de.ph87.data.series.data.varying;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonGenerator;
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import de.ph87.data.plot.axis.graph.GraphDivisionByZero;
|
||||||
|
import de.ph87.data.plot.axis.graph.GraphOperation;
|
||||||
import de.ph87.data.series.SeriesPoint;
|
import de.ph87.data.series.SeriesPoint;
|
||||||
|
import lombok.Data;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
|
|
||||||
@SuppressWarnings("unused") // used by repository query
|
@Data
|
||||||
public class VaryingPoint implements SeriesPoint {
|
public class VaryingPoint implements SeriesPoint<VaryingPoint> {
|
||||||
|
|
||||||
public final ZonedDateTime date;
|
public final ZonedDateTime date;
|
||||||
|
|
||||||
@ -25,6 +28,13 @@ public class VaryingPoint implements SeriesPoint {
|
|||||||
this.avg = varying.getAvg();
|
this.avg = varying.getAvg();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private VaryingPoint(final ZonedDateTime date, final double min, final double avg, final double max) {
|
||||||
|
this.date = date;
|
||||||
|
this.min = min;
|
||||||
|
this.avg = avg;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toJson(final JsonGenerator jsonGenerator) throws IOException {
|
public void toJson(final JsonGenerator jsonGenerator) throws IOException {
|
||||||
jsonGenerator.writeNumber(date.toEpochSecond());
|
jsonGenerator.writeNumber(date.toEpochSecond());
|
||||||
@ -33,4 +43,22 @@ public class VaryingPoint implements SeriesPoint {
|
|||||||
jsonGenerator.writeNumber(avg);
|
jsonGenerator.writeNumber(avg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VaryingPoint times(final double factor) {
|
||||||
|
return new VaryingPoint(date, min * factor, avg * factor, max * factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getValue() {
|
||||||
|
return avg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VaryingPoint combine(@NonNull final SeriesPoint<?> other, @NonNull final GraphOperation operation) throws GraphDivisionByZero {
|
||||||
|
if (other instanceof final VaryingPoint otherVarying) {
|
||||||
|
return new VaryingPoint(date, operation.apply(min, otherVarying.min), operation.apply(avg, otherVarying.avg), operation.apply(max, otherVarying.max));
|
||||||
|
}
|
||||||
|
return new VaryingPoint(date, operation.apply(min, other.getValue()), operation.apply(avg, other.getValue()), operation.apply(max, other.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user