From ded17239b6495d9ab2bb31ed833ca7cc9c87f7d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Mon, 25 Nov 2024 15:53:05 +0100 Subject: [PATCH] Automation + Expression WIP --- .../src/app/api/Automation/Automation.ts | 24 +++ .../src/app/api/Expression/Expression.ts | 35 ++++ .../app/api/Expression/ExpressionLiteral.ts | 14 ++ .../Expression/ExpressionLiteralBoolean.ts | 25 +++ .../api/Expression/ExpressionLiteralNumber.ts | 25 +++ .../src/app/api/Expression/ExpressionNary.ts | 29 +++ .../api/Expression/ExpressionNaryOperator.ts | 15 ++ .../app/api/Expression/ExpressionProperty.ts | 24 +++ .../src/app/api/Expression/ExpressionUnary.ts | 27 +++ .../api/Expression/ExpressionUnaryOperator.ts | 21 +++ .../expression-literal-boolean.service.ts | 22 +++ .../expression-literal-number.service.ts | 22 +++ .../expression-literal-boolean.component.html | 1 + .../expression-literal-boolean.component.less | 0 .../expression-literal-boolean.component.ts | 26 +++ .../expression-literal-number.component.html | 1 + .../expression-literal-number.component.less | 0 .../expression-literal-number.component.ts | 26 +++ .../expression/expression.component.html | 8 + .../expression/expression.component.less | 0 .../shared/expression/expression.component.ts | 49 +++++ .../de/ph87/home/automation/Automation.java | 48 +++++ .../home/automation/AutomationController.java | 22 +++ .../ph87/home/automation/AutomationDto.java | 31 ++++ .../home/automation/AutomationRepository.java | 7 + .../home/automation/AutomationService.java | 36 ++++ .../java/de/ph87/home/demo/DemoService.java | 11 ++ .../home/expression/AbstractExpression.java | 168 ++++++++++++++++++ .../AbstractExpressionController.java | 19 ++ .../expression/AbstractExpressionDto.java | 27 +++ .../AbstractExpressionRepository.java | 9 + .../expression/AbstractExpressionService.java | 48 +++++ .../java/de/ph87/home/expression/Runtime.java | 26 +++ .../expression/ExpressionController.java | 20 +++ .../expression/ExpressionMapper.java | 60 +++++++ .../expression/ExpressionRepository.java | 8 + .../expression/ExpressionService.java | 60 +++++++ .../expression/list/ExpressionList.java | 42 +++++ .../expression/list/ExpressionListDto.java | 29 +++ .../list/ExpressionListOperator.java | 39 ++++ .../list/ExpressionListRepository.java | 7 + .../list/ExpressionListService.java | 28 +++ .../expression/literal/ExpressionLiteral.java | 32 ++++ .../literal/ExpressionLiteralController.java | 23 +++ .../literal/ExpressionLiteralService.java | 20 +++ .../bool/ExpressionLiteralBoolean.java | 20 +++ .../ExpressionLiteralBooleanController.java | 16 ++ .../bool/ExpressionLiteralBooleanDto.java | 18 ++ .../ExpressionLiteralBooleanRepository.java | 9 + .../bool/ExpressionLiteralBooleanService.java | 23 +++ .../number/ExpressionLiteralDouble.java | 20 +++ .../ExpressionLiteralDoubleController.java | 16 ++ .../number/ExpressionLiteralDoubleDto.java | 18 ++ .../ExpressionLiteralDoubleRepository.java | 7 + .../ExpressionLiteralDoubleService.java | 23 +++ .../property/ExpressionProperty.java | 28 +++ .../property/ExpressionPropertyDto.java | 29 +++ .../ExpressionPropertyRepository.java | 7 + .../property/ExpressionPropertyService.java | 29 +++ .../expression/unary/ExpressionUnary.java | 31 ++++ .../expression/unary/ExpressionUnaryDto.java | 28 +++ .../unary/ExpressionUnaryOperator.java | 42 +++++ .../unary/ExpressionUnaryRepository.java | 7 + .../unary/ExpressionUnaryService.java | 28 +++ .../ph87/home/property/PropertyService.java | 7 +- 65 files changed, 1619 insertions(+), 1 deletion(-) create mode 100644 src/main/angular/src/app/api/Automation/Automation.ts create mode 100644 src/main/angular/src/app/api/Expression/Expression.ts create mode 100644 src/main/angular/src/app/api/Expression/ExpressionLiteral.ts create mode 100644 src/main/angular/src/app/api/Expression/ExpressionLiteralBoolean.ts create mode 100644 src/main/angular/src/app/api/Expression/ExpressionLiteralNumber.ts create mode 100644 src/main/angular/src/app/api/Expression/ExpressionNary.ts create mode 100644 src/main/angular/src/app/api/Expression/ExpressionNaryOperator.ts create mode 100644 src/main/angular/src/app/api/Expression/ExpressionProperty.ts create mode 100644 src/main/angular/src/app/api/Expression/ExpressionUnary.ts create mode 100644 src/main/angular/src/app/api/Expression/ExpressionUnaryOperator.ts create mode 100644 src/main/angular/src/app/api/Expression/expression-literal-boolean.service.ts create mode 100644 src/main/angular/src/app/api/Expression/expression-literal-number.service.ts create mode 100644 src/main/angular/src/app/shared/expression/expression-literal-boolean/expression-literal-boolean.component.html create mode 100644 src/main/angular/src/app/shared/expression/expression-literal-boolean/expression-literal-boolean.component.less create mode 100644 src/main/angular/src/app/shared/expression/expression-literal-boolean/expression-literal-boolean.component.ts create mode 100644 src/main/angular/src/app/shared/expression/expression-literal-number/expression-literal-number.component.html create mode 100644 src/main/angular/src/app/shared/expression/expression-literal-number/expression-literal-number.component.less create mode 100644 src/main/angular/src/app/shared/expression/expression-literal-number/expression-literal-number.component.ts create mode 100644 src/main/angular/src/app/shared/expression/expression.component.html create mode 100644 src/main/angular/src/app/shared/expression/expression.component.less create mode 100644 src/main/angular/src/app/shared/expression/expression.component.ts create mode 100644 src/main/java/de/ph87/home/automation/Automation.java create mode 100644 src/main/java/de/ph87/home/automation/AutomationController.java create mode 100644 src/main/java/de/ph87/home/automation/AutomationDto.java create mode 100644 src/main/java/de/ph87/home/automation/AutomationRepository.java create mode 100644 src/main/java/de/ph87/home/automation/AutomationService.java create mode 100644 src/main/java/de/ph87/home/expression/AbstractExpression.java create mode 100644 src/main/java/de/ph87/home/expression/AbstractExpressionController.java create mode 100644 src/main/java/de/ph87/home/expression/AbstractExpressionDto.java create mode 100644 src/main/java/de/ph87/home/expression/AbstractExpressionRepository.java create mode 100644 src/main/java/de/ph87/home/expression/AbstractExpressionService.java create mode 100644 src/main/java/de/ph87/home/expression/Runtime.java create mode 100644 src/main/java/de/ph87/home/expression/expression/ExpressionController.java create mode 100644 src/main/java/de/ph87/home/expression/expression/ExpressionMapper.java create mode 100644 src/main/java/de/ph87/home/expression/expression/ExpressionRepository.java create mode 100644 src/main/java/de/ph87/home/expression/expression/ExpressionService.java create mode 100644 src/main/java/de/ph87/home/expression/expression/list/ExpressionList.java create mode 100644 src/main/java/de/ph87/home/expression/expression/list/ExpressionListDto.java create mode 100644 src/main/java/de/ph87/home/expression/expression/list/ExpressionListOperator.java create mode 100644 src/main/java/de/ph87/home/expression/expression/list/ExpressionListRepository.java create mode 100644 src/main/java/de/ph87/home/expression/expression/list/ExpressionListService.java create mode 100644 src/main/java/de/ph87/home/expression/expression/literal/ExpressionLiteral.java create mode 100644 src/main/java/de/ph87/home/expression/expression/literal/ExpressionLiteralController.java create mode 100644 src/main/java/de/ph87/home/expression/expression/literal/ExpressionLiteralService.java create mode 100644 src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBoolean.java create mode 100644 src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanController.java create mode 100644 src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanDto.java create mode 100644 src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanRepository.java create mode 100644 src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanService.java create mode 100644 src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDouble.java create mode 100644 src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleController.java create mode 100644 src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleDto.java create mode 100644 src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleRepository.java create mode 100644 src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleService.java create mode 100644 src/main/java/de/ph87/home/expression/expression/property/ExpressionProperty.java create mode 100644 src/main/java/de/ph87/home/expression/expression/property/ExpressionPropertyDto.java create mode 100644 src/main/java/de/ph87/home/expression/expression/property/ExpressionPropertyRepository.java create mode 100644 src/main/java/de/ph87/home/expression/expression/property/ExpressionPropertyService.java create mode 100644 src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnary.java create mode 100644 src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryDto.java create mode 100644 src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryOperator.java create mode 100644 src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryRepository.java create mode 100644 src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryService.java diff --git a/src/main/angular/src/app/api/Automation/Automation.ts b/src/main/angular/src/app/api/Automation/Automation.ts new file mode 100644 index 0000000..74a611b --- /dev/null +++ b/src/main/angular/src/app/api/Automation/Automation.ts @@ -0,0 +1,24 @@ +import {Expression, expressionFromJson} from '../Expression/Expression'; +import {validateBoolean, validateString} from '../common/validators'; + +export class Automation { + + constructor( + readonly uuid: string, + readonly name: string, + readonly enabled: boolean, + readonly condition: Expression, + ) { + // + } + + static fromJson(json: any): Automation { + return new Automation( + validateString(json.uuid), + validateString(json.name), + validateBoolean(json.enabled), + expressionFromJson(json.condition), + ); + } + +} diff --git a/src/main/angular/src/app/api/Expression/Expression.ts b/src/main/angular/src/app/api/Expression/Expression.ts new file mode 100644 index 0000000..751b40c --- /dev/null +++ b/src/main/angular/src/app/api/Expression/Expression.ts @@ -0,0 +1,35 @@ +import {validateString} from '../common/validators'; +import {ExpressionLiteralBoolean} from './ExpressionLiteralBoolean'; +import {ExpressionLiteralNumber} from './ExpressionLiteralNumber'; +import {ExpressionProperty} from './ExpressionProperty'; +import {ExpressionUnary} from './ExpressionUnary'; +import {ExpressionNary} from './ExpressionNary'; + +export abstract class Expression { + + protected constructor( + readonly _type_: string, + readonly uuid: string, + readonly name: string, + ) { + // + } + +} + +export function expressionFromJson(json: any): Expression { + const _type_ = validateString(json._type_); + switch (_type_) { + case 'ExpressionLiteralBoolean': + return ExpressionLiteralBoolean.fromJson(json); + case 'ExpressionLiteralNumber': + return ExpressionLiteralNumber.fromJson(json); + case 'ExpressionProperty': + return ExpressionProperty.fromJson(json); + case 'ExpressionUnary': + return ExpressionUnary.fromJson(json); + case 'ExpressionNary': + return ExpressionNary.fromJson(json); + } + throw Error("expression type not implemented: " + _type_); +} diff --git a/src/main/angular/src/app/api/Expression/ExpressionLiteral.ts b/src/main/angular/src/app/api/Expression/ExpressionLiteral.ts new file mode 100644 index 0000000..5efbfb4 --- /dev/null +++ b/src/main/angular/src/app/api/Expression/ExpressionLiteral.ts @@ -0,0 +1,14 @@ +import {Expression} from "./Expression"; + +export abstract class ExpressionLiteral extends Expression { + + protected constructor( + _type_: string, + uuid: string, + name: string, + readonly literal: T | null, + ) { + super(_type_, uuid, name); + } + +} diff --git a/src/main/angular/src/app/api/Expression/ExpressionLiteralBoolean.ts b/src/main/angular/src/app/api/Expression/ExpressionLiteralBoolean.ts new file mode 100644 index 0000000..9f3926f --- /dev/null +++ b/src/main/angular/src/app/api/Expression/ExpressionLiteralBoolean.ts @@ -0,0 +1,25 @@ +import {validateBooleanOrNull, validateString} from "../common/validators"; + +import {ExpressionLiteral} from './ExpressionLiteral'; + +export class ExpressionLiteralBoolean extends ExpressionLiteral { + + constructor( + _type_: string, + uuid: string, + name: string, + readonly value: boolean | null, + ) { + super(_type_, uuid, name, value); + } + + static fromJson(json: any): ExpressionLiteralBoolean { + return new ExpressionLiteralBoolean( + validateString(json._type_), + validateString(json.uuid), + validateString(json.name), + validateBooleanOrNull(json.value), + ); + } + +} diff --git a/src/main/angular/src/app/api/Expression/ExpressionLiteralNumber.ts b/src/main/angular/src/app/api/Expression/ExpressionLiteralNumber.ts new file mode 100644 index 0000000..17ea549 --- /dev/null +++ b/src/main/angular/src/app/api/Expression/ExpressionLiteralNumber.ts @@ -0,0 +1,25 @@ +import {validateNumberOrNull, validateString} from "../common/validators"; + +import {ExpressionLiteral} from './ExpressionLiteral'; + +export class ExpressionLiteralNumber extends ExpressionLiteral { + + constructor( + _type_: string, + uuid: string, + name: string, + readonly value: number | null, + ) { + super(_type_, uuid, name, value); + } + + static fromJson(json: any): ExpressionLiteralNumber { + return new ExpressionLiteralNumber( + validateString(json._type_), + validateString(json.uuid), + validateString(json.name), + validateNumberOrNull(json.value), + ); + } + +} diff --git a/src/main/angular/src/app/api/Expression/ExpressionNary.ts b/src/main/angular/src/app/api/Expression/ExpressionNary.ts new file mode 100644 index 0000000..01020f9 --- /dev/null +++ b/src/main/angular/src/app/api/Expression/ExpressionNary.ts @@ -0,0 +1,29 @@ +import {orNull, validateString} from "../common/validators"; +import {Expression, expressionFromJson} from './Expression'; +import {ExpressionNaryOperator} from './ExpressionNaryOperator'; + +export class ExpressionNary extends Expression { + + constructor( + _type_: string, + uuid: string, + name: string, + readonly operator: ExpressionNaryOperator, + readonly expression0: Expression | null, + readonly expression1: Expression | null, + ) { + super(_type_, uuid, name); + } + + static fromJson(json: any): ExpressionNary { + return new ExpressionNary( + validateString(json._type_), + validateString(json.uuid), + validateString(json.name), + validateString(json.operator) as ExpressionNaryOperator, + orNull(json.expression0, j => expressionFromJson(j)), + orNull(json.expression1, j => expressionFromJson(j)), + ); + } + +} diff --git a/src/main/angular/src/app/api/Expression/ExpressionNaryOperator.ts b/src/main/angular/src/app/api/Expression/ExpressionNaryOperator.ts new file mode 100644 index 0000000..1d09570 --- /dev/null +++ b/src/main/angular/src/app/api/Expression/ExpressionNaryOperator.ts @@ -0,0 +1,15 @@ +export enum ExpressionNaryOperator { + OR = 'OR', + AND = 'AND', + XOR = 'XOR', + ADD = 'ADD', + SUB = 'SUB', + MUL = 'MUL', + DIV = 'DIV', + MOD = 'MOD', + POW = 'POW', + HYPOT = 'HYPOT', + ATAN2 = 'ATAN2', + MIN = 'MIN', + MAX = 'MAX', +} diff --git a/src/main/angular/src/app/api/Expression/ExpressionProperty.ts b/src/main/angular/src/app/api/Expression/ExpressionProperty.ts new file mode 100644 index 0000000..270ccbe --- /dev/null +++ b/src/main/angular/src/app/api/Expression/ExpressionProperty.ts @@ -0,0 +1,24 @@ +import {validateString} from "../common/validators"; +import {Expression} from './Expression'; + +export class ExpressionProperty extends Expression { + + constructor( + _type_: string, + uuid: string, + name: string, + readonly propertyId: string, + ) { + super(_type_, uuid, name); + } + + static fromJson(json: any): ExpressionProperty { + return new ExpressionProperty( + validateString(json._type_), + validateString(json.uuid), + validateString(json.name), + validateString(json.propertyId), + ); + } + +} diff --git a/src/main/angular/src/app/api/Expression/ExpressionUnary.ts b/src/main/angular/src/app/api/Expression/ExpressionUnary.ts new file mode 100644 index 0000000..488ea34 --- /dev/null +++ b/src/main/angular/src/app/api/Expression/ExpressionUnary.ts @@ -0,0 +1,27 @@ +import {orNull, validateString} from "../common/validators"; +import {Expression, expressionFromJson} from './Expression'; +import {ExpressionUnaryOperator} from './ExpressionUnaryOperator'; + +export class ExpressionUnary extends Expression { + + constructor( + _type_: string, + uuid: string, + name: string, + readonly operator: ExpressionUnaryOperator, + readonly expression: Expression | null, + ) { + super(_type_, uuid, name); + } + + static fromJson(json: any): ExpressionUnary { + return new ExpressionUnary( + validateString(json._type_), + validateString(json.uuid), + validateString(json.name), + validateString(json.operator) as ExpressionUnaryOperator, + orNull(json.expression, j => expressionFromJson(j)), + ); + } + +} diff --git a/src/main/angular/src/app/api/Expression/ExpressionUnaryOperator.ts b/src/main/angular/src/app/api/Expression/ExpressionUnaryOperator.ts new file mode 100644 index 0000000..9347f45 --- /dev/null +++ b/src/main/angular/src/app/api/Expression/ExpressionUnaryOperator.ts @@ -0,0 +1,21 @@ +export enum ExpressionUnaryOperator { + NOT = 'NOT', + NEG = 'NEG', + POS = 'POS', + FLOOR = 'FLOOR', + CEIL = 'CEIL', + ROUND = 'ROUND', + SQRT = 'SQRT', + ABS = 'ABS', + COS = 'COS', + SIN = 'SIN', + TAN = 'TAN', + ACOS = 'ACOS', + ASIN = 'ASIN', + ATAN = 'ATAN', + COSH = 'COSH', + SINH = 'SINH', + TANH = 'TANH', + EXP = 'EXP', + EXPM1 = 'EXPM1', +} diff --git a/src/main/angular/src/app/api/Expression/expression-literal-boolean.service.ts b/src/main/angular/src/app/api/Expression/expression-literal-boolean.service.ts new file mode 100644 index 0000000..b935c1c --- /dev/null +++ b/src/main/angular/src/app/api/Expression/expression-literal-boolean.service.ts @@ -0,0 +1,22 @@ +import {Injectable} from '@angular/core'; +import {ExpressionLiteralBoolean} from './ExpressionLiteralBoolean'; +import {ApiService} from '../common/api.service'; +import {CrudService} from '../common/CrudService'; +import {Next} from '../common/types'; + +@Injectable({ + providedIn: 'root' +}) +export class ExpressionLiteralBooleanService extends CrudService { + + constructor( + apiService: ApiService, + ) { + super(apiService, ['ExpressionLiteralBoolean'], ExpressionLiteralBoolean.fromJson); + } + + setValue(expression: ExpressionLiteralBoolean, value: boolean, next?: Next) { + this.postSingle([expression.uuid, 'setValue'], value, next); + } + +} diff --git a/src/main/angular/src/app/api/Expression/expression-literal-number.service.ts b/src/main/angular/src/app/api/Expression/expression-literal-number.service.ts new file mode 100644 index 0000000..9a06b51 --- /dev/null +++ b/src/main/angular/src/app/api/Expression/expression-literal-number.service.ts @@ -0,0 +1,22 @@ +import {Injectable} from '@angular/core'; +import {ExpressionLiteralNumber} from './ExpressionLiteralNumber'; +import {ApiService} from '../common/api.service'; +import {CrudService} from '../common/CrudService'; +import {Next} from '../common/types'; + +@Injectable({ + providedIn: 'root' +}) +export class ExpressionLiteralNumberService extends CrudService { + + constructor( + apiService: ApiService, + ) { + super(apiService, ['ExpressionLiteralNumber'], ExpressionLiteralNumber.fromJson); + } + + setValue(expression: ExpressionLiteralNumber, value: number, next?: Next) { + this.postSingle([expression.uuid, 'setValue'], value, next); + } + +} diff --git a/src/main/angular/src/app/shared/expression/expression-literal-boolean/expression-literal-boolean.component.html b/src/main/angular/src/app/shared/expression/expression-literal-boolean/expression-literal-boolean.component.html new file mode 100644 index 0000000..899d17e --- /dev/null +++ b/src/main/angular/src/app/shared/expression/expression-literal-boolean/expression-literal-boolean.component.html @@ -0,0 +1 @@ + diff --git a/src/main/angular/src/app/shared/expression/expression-literal-boolean/expression-literal-boolean.component.less b/src/main/angular/src/app/shared/expression/expression-literal-boolean/expression-literal-boolean.component.less new file mode 100644 index 0000000..e69de29 diff --git a/src/main/angular/src/app/shared/expression/expression-literal-boolean/expression-literal-boolean.component.ts b/src/main/angular/src/app/shared/expression/expression-literal-boolean/expression-literal-boolean.component.ts new file mode 100644 index 0000000..c5f0bbc --- /dev/null +++ b/src/main/angular/src/app/shared/expression/expression-literal-boolean/expression-literal-boolean.component.ts @@ -0,0 +1,26 @@ +import {Component, Input} from '@angular/core'; +import {FormsModule} from '@angular/forms'; +import {ExpressionLiteralBoolean} from '../../../api/Expression/ExpressionLiteralBoolean'; +import {ExpressionLiteralBooleanService} from '../../../api/Expression/expression-literal-boolean.service'; + +@Component({ + selector: 'app-expression-literal-boolean', + standalone: true, + imports: [ + FormsModule + ], + templateUrl: './expression-literal-boolean.component.html', + styleUrl: './expression-literal-boolean.component.less' +}) +export class ExpressionLiteralBooleanComponent { + + @Input() + expression!: ExpressionLiteralBoolean; + + constructor( + readonly expressionService: ExpressionLiteralBooleanService, + ) { + // + } + +} diff --git a/src/main/angular/src/app/shared/expression/expression-literal-number/expression-literal-number.component.html b/src/main/angular/src/app/shared/expression/expression-literal-number/expression-literal-number.component.html new file mode 100644 index 0000000..2f50156 --- /dev/null +++ b/src/main/angular/src/app/shared/expression/expression-literal-number/expression-literal-number.component.html @@ -0,0 +1 @@ + diff --git a/src/main/angular/src/app/shared/expression/expression-literal-number/expression-literal-number.component.less b/src/main/angular/src/app/shared/expression/expression-literal-number/expression-literal-number.component.less new file mode 100644 index 0000000..e69de29 diff --git a/src/main/angular/src/app/shared/expression/expression-literal-number/expression-literal-number.component.ts b/src/main/angular/src/app/shared/expression/expression-literal-number/expression-literal-number.component.ts new file mode 100644 index 0000000..a34c3c2 --- /dev/null +++ b/src/main/angular/src/app/shared/expression/expression-literal-number/expression-literal-number.component.ts @@ -0,0 +1,26 @@ +import {Component, Input} from '@angular/core'; +import {FormsModule} from '@angular/forms'; +import {ExpressionLiteralNumber} from '../../../api/Expression/ExpressionLiteralNumber'; +import {ExpressionLiteralNumberService} from '../../../api/Expression/expression-literal-number.service'; + +@Component({ + selector: 'app-expression-literal-number', + standalone: true, + imports: [ + FormsModule + ], + templateUrl: './expression-literal-number.component.html', + styleUrl: './expression-literal-number.component.less' +}) +export class ExpressionLiteralNumberComponent { + + @Input() + expression!: ExpressionLiteralNumber; + + constructor( + readonly expressionService: ExpressionLiteralNumberService, + ) { + // + } + +} diff --git a/src/main/angular/src/app/shared/expression/expression.component.html b/src/main/angular/src/app/shared/expression/expression.component.html new file mode 100644 index 0000000..90e846f --- /dev/null +++ b/src/main/angular/src/app/shared/expression/expression.component.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/angular/src/app/shared/expression/expression.component.less b/src/main/angular/src/app/shared/expression/expression.component.less new file mode 100644 index 0000000..e69de29 diff --git a/src/main/angular/src/app/shared/expression/expression.component.ts b/src/main/angular/src/app/shared/expression/expression.component.ts new file mode 100644 index 0000000..ce91259 --- /dev/null +++ b/src/main/angular/src/app/shared/expression/expression.component.ts @@ -0,0 +1,49 @@ +import {Component, Input} from '@angular/core'; +import {Expression} from '../../api/Expression/Expression'; +import {ExpressionLiteralBoolean} from '../../api/Expression/ExpressionLiteralBoolean'; +import {NgSwitch, NgSwitchCase} from '@angular/common'; +import {ExpressionLiteralNumber} from '../../api/Expression/ExpressionLiteralNumber'; +import {ExpressionProperty} from '../../api/Expression/ExpressionProperty'; +import {ExpressionUnary} from '../../api/Expression/ExpressionUnary'; +import {ExpressionNary} from '../../api/Expression/ExpressionNary'; +import {ExpressionLiteralBooleanComponent} from './expression-literal-boolean/expression-literal-boolean.component'; +import {ExpressionLiteralNumberComponent} from './expression-literal-number/expression-literal-number.component'; + +@Component({ + selector: 'app-expression', + standalone: true, + imports: [ + NgSwitch, + NgSwitchCase, + ExpressionLiteralBooleanComponent, + ExpressionLiteralNumberComponent + ], + templateUrl: './expression.component.html', + styleUrl: './expression.component.less' +}) +export class ExpressionComponent { + + @Input() + expression!: Expression; + + asExpressionLiteralBoolean(): ExpressionLiteralBoolean { + return this.expression as ExpressionLiteralBoolean; + } + + asExpressionLiteralNumber(): ExpressionLiteralNumber { + return this.expression as ExpressionLiteralNumber; + } + + asExpressionProperty(): ExpressionProperty { + return this.expression as ExpressionProperty; + } + + asExpressionUnary(): ExpressionUnary { + return this.expression as ExpressionUnary; + } + + asExpressionNary(): ExpressionNary { + return this.expression as ExpressionNary; + } + +} diff --git a/src/main/java/de/ph87/home/automation/Automation.java b/src/main/java/de/ph87/home/automation/Automation.java new file mode 100644 index 0000000..461babb --- /dev/null +++ b/src/main/java/de/ph87/home/automation/Automation.java @@ -0,0 +1,48 @@ +package de.ph87.home.automation; + +import de.ph87.home.expression.AbstractExpression; +import de.ph87.home.expression.Runtime; +import jakarta.annotation.Nullable; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; + +import java.util.UUID; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class Automation { + + @Id + @NonNull + private String uuid = UUID.randomUUID().toString(); + + @NonNull + @Column(nullable = false) + private String name; + + @Column(nullable = false) + private boolean enabled = false; + + @Nullable + @ManyToOne + private AbstractExpression condition; + + public Automation(@NonNull final String name, final boolean enabled, @NonNull final AbstractExpression condition) { + this.name = name; + this.enabled = enabled; + this.condition = condition; + } + + public boolean evaluateCondition(@NonNull final Runtime runtime) { + return AbstractExpression.mapOrElseNotNull(condition, c -> c.evaluate(runtime), AbstractExpression::convertToBoolean, false); + } + +} diff --git a/src/main/java/de/ph87/home/automation/AutomationController.java b/src/main/java/de/ph87/home/automation/AutomationController.java new file mode 100644 index 0000000..d2f8339 --- /dev/null +++ b/src/main/java/de/ph87/home/automation/AutomationController.java @@ -0,0 +1,22 @@ +package de.ph87.home.automation; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("Automation") +public class AutomationController { + + private final AutomationService automationService; + + @GetMapping("list") + public List list() { + return automationService.listDto(); + } + +} diff --git a/src/main/java/de/ph87/home/automation/AutomationDto.java b/src/main/java/de/ph87/home/automation/AutomationDto.java new file mode 100644 index 0000000..1078160 --- /dev/null +++ b/src/main/java/de/ph87/home/automation/AutomationDto.java @@ -0,0 +1,31 @@ +package de.ph87.home.automation; + +import de.ph87.home.expression.AbstractExpressionDto; +import jakarta.annotation.Nullable; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; + +@Getter +@ToString +public class AutomationDto { + + @NonNull + private final String uuid; + + @NonNull + private final String name; + + private final boolean enabled; + + @Nullable + private final AbstractExpressionDto condition; + + public AutomationDto(@NonNull final Automation automation, @Nullable final AbstractExpressionDto condition) { + this.uuid = automation.getUuid(); + this.name = automation.getName(); + this.enabled = automation.isEnabled(); + this.condition = condition; + } + +} diff --git a/src/main/java/de/ph87/home/automation/AutomationRepository.java b/src/main/java/de/ph87/home/automation/AutomationRepository.java new file mode 100644 index 0000000..dc9f3e9 --- /dev/null +++ b/src/main/java/de/ph87/home/automation/AutomationRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.automation; + +import org.springframework.data.repository.ListCrudRepository; + +public interface AutomationRepository extends ListCrudRepository { + +} diff --git a/src/main/java/de/ph87/home/automation/AutomationService.java b/src/main/java/de/ph87/home/automation/AutomationService.java new file mode 100644 index 0000000..d2dadad --- /dev/null +++ b/src/main/java/de/ph87/home/automation/AutomationService.java @@ -0,0 +1,36 @@ +package de.ph87.home.automation; + +import de.ph87.home.expression.AbstractExpressionDto; +import de.ph87.home.expression.expression.ExpressionService; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +import static de.ph87.home.expression.AbstractExpression.mapOrElse; + +@Slf4j +@Service +@Transactional +@RequiredArgsConstructor +public class AutomationService { + + private final AutomationRepository automationRepository; + + private final ExpressionService expressionService; + + @NonNull + public List listDto() { + return automationRepository.findAll().stream().map(this::toDto).toList(); + } + + @NonNull + private AutomationDto toDto(@NonNull final Automation automation) { + final AbstractExpressionDto condition = mapOrElse(automation.getCondition(), expressionService::toDto, null); + return new AutomationDto(automation, condition); + } + +} diff --git a/src/main/java/de/ph87/home/demo/DemoService.java b/src/main/java/de/ph87/home/demo/DemoService.java index 92fd85f..8e7384a 100644 --- a/src/main/java/de/ph87/home/demo/DemoService.java +++ b/src/main/java/de/ph87/home/demo/DemoService.java @@ -1,6 +1,10 @@ package de.ph87.home.demo; +import de.ph87.home.automation.Automation; +import de.ph87.home.automation.AutomationRepository; import de.ph87.home.device.DeviceService; +import de.ph87.home.expression.expression.ExpressionRepository; +import de.ph87.home.expression.expression.literal.bool.ExpressionLiteralBoolean; import de.ph87.home.knx.property.KnxPropertyService; import de.ph87.home.knx.property.KnxPropertyType; import de.ph87.home.shutter.ShutterService; @@ -24,6 +28,10 @@ public class DemoService { private final ShutterService shutterService; + private final ExpressionRepository expressionRepository; + + private final AutomationRepository automationRepository; + @EventListener(ApplicationStartedEvent.class) public void startup() { knxPropertyService.create("eg_ambiente", KnxPropertyType.BOOLEAN, adr(849), adr(848)); @@ -58,6 +66,9 @@ public class DemoService { knxPropertyService.create("kueche_tuer", KnxPropertyType.DOUBLE, adr(2324), adr(2324)); shutterService.create("Küche Tür", "kueche_tuer", "kueche_tuer"); + + final ExpressionLiteralBoolean exp = expressionRepository.save(new ExpressionLiteralBoolean(true)); + automationRepository.save(new Automation("test", true, exp)); } private static GroupAddress adr(final int rawGroupAddress) { diff --git a/src/main/java/de/ph87/home/expression/AbstractExpression.java b/src/main/java/de/ph87/home/expression/AbstractExpression.java new file mode 100644 index 0000000..ad7b044 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/AbstractExpression.java @@ -0,0 +1,168 @@ +package de.ph87.home.expression; + +import jakarta.annotation.Nullable; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; + +import java.util.UUID; +import java.util.function.BiFunction; +import java.util.function.Function; + +@Getter +@ToString +@NoArgsConstructor +@Entity(name = "expression") +@DiscriminatorColumn(name = "_type_") +@Inheritance(strategy = InheritanceType.JOINED) +public abstract class AbstractExpression { + + @Id + @NonNull + private String uuid = UUID.randomUUID().toString(); + + @NonNull + @Column(nullable = false) + private String name = ""; + + public abstract Object evaluate(@NonNull final Runtime runtime); + + @NonNull + public static Object applyFallback(final Object v0, final Object v1, @NonNull final Function convert, @NonNull final T fallback, @NonNull final BiFunction function) { + final T c0 = orElseNotNull(convert.apply(v0), fallback); + final T c1 = orElseNotNull(convert.apply(v1), fallback); + return function.apply(c0, c1); + } + + @Nullable + public static Object applyNullToNull(final Object value, @NonNull final Function convert, @NonNull final Function function) { + final T converted = convert.apply(value); + if (converted == null) { + return null; + } + return function.apply(converted); + } + + @Nullable + public static Object applyNullToNull(final Object v0, final Object v1, @NonNull final Function convert, @NonNull final BiFunction function) { + final T c0 = convert.apply(v0); + final T c1 = convert.apply(v1); + if (c0 == null || c1 == null) { + return null; + } + return function.apply(c0, c1); + } + + @Nullable + public static Object evalOrNull(@NonNull final Runtime runtime, @Nullable final AbstractExpression expression) { + if (expression == null) { + return null; + } + return expression.evaluate(runtime); + } + + @Nullable + public static Boolean convertToBoolean(@Nullable final Object value) { + if (value == null) { + return null; + } + if (value instanceof Boolean) { + return (boolean) value; + } + final Double number = convertToNumber(value); + if (number == null) { + return null; + } + return number > 0; + } + + @Nullable + public static Double convertToNumber(@Nullable final Object value) { + if (value == null) { + return null; + } + if (value instanceof Number) { + return (double) value; + } + try { + return Double.parseDouble((String) value); + } catch (NumberFormatException e) { + return null; + } + } + + @NonNull + public static T orElseNotNull(@Nullable final T t, @NonNull final T orElse) { + if (t == null) { + return orElse; + } + return t; + } + + @NonNull + public static R mapOrElseNotNull(@Nullable final T t, @NonNull final Function tr, @NonNull final R orElse) { + if (t == null) { + return orElse; + } + final R r = tr.apply(t); + if (r == null) { + return orElse; + } + return r; + } + + @NonNull + public static R mapOrElseNotNull(@Nullable final T t, @NonNull final Function tu, @NonNull final Function ur, @NonNull final R orElse) { + if (t == null) { + return orElse; + } + final U u = tu.apply(t); + if (u == null) { + return orElse; + } + final R r = ur.apply(u); + if (r == null) { + return orElse; + } + return r; + } + + @Nullable + public static T orElse(@Nullable final T t, @Nullable final T orElse) { + if (t == null) { + return orElse; + } + return t; + } + + @Nullable + public static R mapOrElse(@Nullable final T t, @NonNull final Function tr, @Nullable final R orElse) { + if (t == null) { + return orElse; + } + final R r = tr.apply(t); + if (r == null) { + return orElse; + } + return r; + } + + @Nullable + public static R mapOrElse(@Nullable final T t, @NonNull final Function tu, @NonNull final Function ur, @Nullable final R orElse) { + if (t == null) { + return orElse; + } + final U u = tu.apply(t); + if (u == null) { + return orElse; + } + final R r = ur.apply(u); + if (r == null) { + return orElse; + } + return r; + } + +} diff --git a/src/main/java/de/ph87/home/expression/AbstractExpressionController.java b/src/main/java/de/ph87/home/expression/AbstractExpressionController.java new file mode 100644 index 0000000..1b5e119 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/AbstractExpressionController.java @@ -0,0 +1,19 @@ +package de.ph87.home.expression; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +@Slf4j +@RequiredArgsConstructor +public abstract class AbstractExpressionController { + + protected final AbstractExpressionService expressionService; + + @GetMapping("{uuid}") + public D byUuid(@PathVariable("uuid") String uuid) { + return expressionService.getDtoByUuid(uuid); + } + +} diff --git a/src/main/java/de/ph87/home/expression/AbstractExpressionDto.java b/src/main/java/de/ph87/home/expression/AbstractExpressionDto.java new file mode 100644 index 0000000..9212e61 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/AbstractExpressionDto.java @@ -0,0 +1,27 @@ +package de.ph87.home.expression; + +import jakarta.annotation.Nullable; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; + +@Getter +@ToString +public abstract class AbstractExpressionDto { + + @NonNull + private final String uuid; + + @NonNull + private final String name; + + @Nullable + private final Object value; + + protected AbstractExpressionDto(@NonNull final AbstractExpression expression, @Nullable final Object value) { + this.uuid = expression.getUuid(); + this.name = expression.getName(); + this.value = value; + } + +} diff --git a/src/main/java/de/ph87/home/expression/AbstractExpressionRepository.java b/src/main/java/de/ph87/home/expression/AbstractExpressionRepository.java new file mode 100644 index 0000000..197e169 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/AbstractExpressionRepository.java @@ -0,0 +1,9 @@ +package de.ph87.home.expression; + +import org.springframework.data.repository.ListCrudRepository; +import org.springframework.data.repository.NoRepositoryBean; + +@NoRepositoryBean +public interface AbstractExpressionRepository extends ListCrudRepository { + +} diff --git a/src/main/java/de/ph87/home/expression/AbstractExpressionService.java b/src/main/java/de/ph87/home/expression/AbstractExpressionService.java new file mode 100644 index 0000000..c85df53 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/AbstractExpressionService.java @@ -0,0 +1,48 @@ +package de.ph87.home.expression; + +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.function.Consumer; + +@Slf4j +@Service +@Transactional +@RequiredArgsConstructor +public abstract class AbstractExpressionService { + + protected final AbstractExpressionRepository repository; + + protected final ApplicationEventPublisher applicationEventPublisher; + + @NonNull + public D set(@NonNull final String uuid, @NonNull final Consumer setter) { + final E expression = getByUuid(uuid); + setter.accept(expression); + return publish(expression); + } + + @NonNull + private E getByUuid(@NonNull final String uuid) { + return repository.findById(uuid).orElseThrow(); + } + + @NonNull + public D getDtoByUuid(@NonNull final String uuid) { + return toDto(getByUuid(uuid)); + } + + @NonNull + private D publish(@NonNull final E expression) { + final D dto = toDto(expression); + applicationEventPublisher.publishEvent(dto); + return dto; + } + + public abstract D toDto(final E expression); + +} diff --git a/src/main/java/de/ph87/home/expression/Runtime.java b/src/main/java/de/ph87/home/expression/Runtime.java new file mode 100644 index 0000000..829bb87 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/Runtime.java @@ -0,0 +1,26 @@ +package de.ph87.home.expression; + +import de.ph87.home.property.PropertyService; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; + +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.Map; + +@Getter +@ToString +public class Runtime { + + public final ZonedDateTime now = ZonedDateTime.now(); + + public final Map write = new HashMap<>(); + + public final PropertyService propertyService; + + public Runtime(@NonNull final PropertyService propertyService) { + this.propertyService = propertyService; + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/ExpressionController.java b/src/main/java/de/ph87/home/expression/expression/ExpressionController.java new file mode 100644 index 0000000..2ed7c47 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/ExpressionController.java @@ -0,0 +1,20 @@ +package de.ph87.home.expression.expression; + +import de.ph87.home.expression.AbstractExpression; +import de.ph87.home.expression.AbstractExpressionController; +import de.ph87.home.expression.AbstractExpressionDto; +import de.ph87.home.expression.AbstractExpressionService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@RestController +@RequestMapping("Expression") +public class ExpressionController extends AbstractExpressionController { + + public ExpressionController(final AbstractExpressionService expressionService) { + super(expressionService); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/ExpressionMapper.java b/src/main/java/de/ph87/home/expression/expression/ExpressionMapper.java new file mode 100644 index 0000000..a1ed2a6 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/ExpressionMapper.java @@ -0,0 +1,60 @@ +package de.ph87.home.expression.expression; + +import de.ph87.home.expression.AbstractExpression; +import de.ph87.home.expression.AbstractExpressionDto; +import de.ph87.home.expression.AbstractExpressionRepository; +import de.ph87.home.expression.AbstractExpressionService; +import de.ph87.home.expression.expression.literal.bool.ExpressionLiteralBoolean; +import de.ph87.home.expression.expression.literal.bool.ExpressionLiteralBooleanService; +import de.ph87.home.expression.expression.literal.number.ExpressionLiteralDouble; +import de.ph87.home.expression.expression.literal.number.ExpressionLiteralDoubleService; +import de.ph87.home.expression.expression.list.ExpressionList; +import de.ph87.home.expression.expression.list.ExpressionListService; +import de.ph87.home.expression.expression.property.ExpressionProperty; +import de.ph87.home.expression.expression.property.ExpressionPropertyService; +import de.ph87.home.expression.expression.unary.ExpressionUnary; +import de.ph87.home.expression.expression.unary.ExpressionUnaryService; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@Transactional +public class ExpressionMapper extends AbstractExpressionService { + + private final ExpressionLiteralBooleanService expressionLiteralBooleanService; + + private final ExpressionLiteralDoubleService expressionLiteralDoubleService; + + private final ExpressionUnaryService expressionUnaryService; + + private final ExpressionListService expressionNaryService; + + private final ExpressionPropertyService expressionPropertyService; + + public ExpressionMapper(final AbstractExpressionRepository repository, final ApplicationEventPublisher applicationEventPublisher, final ExpressionLiteralBooleanService expressionLiteralBooleanService, final ExpressionLiteralDoubleService expressionLiteralDoubleService, final ExpressionUnaryService expressionUnaryService, final ExpressionListService expressionNaryService, final ExpressionPropertyService expressionPropertyService) { + super(repository, applicationEventPublisher); + this.expressionLiteralBooleanService = expressionLiteralBooleanService; + this.expressionLiteralDoubleService = expressionLiteralDoubleService; + this.expressionUnaryService = expressionUnaryService; + this.expressionNaryService = expressionNaryService; + this.expressionPropertyService = expressionPropertyService; + } + + @NonNull + @Override + public AbstractExpressionDto toDto(@NonNull final AbstractExpression expression) { + return switch (expression) { + case final ExpressionLiteralBoolean expressionLiteralBoolean -> expressionLiteralBooleanService.toDto(expressionLiteralBoolean); + case final ExpressionLiteralDouble expressionLiteralDouble -> expressionLiteralDoubleService.toDto(expressionLiteralDouble); + case final ExpressionProperty expressionProperty -> expressionPropertyService.toDto(expressionProperty); + case final ExpressionUnary expressionUnary -> expressionUnaryService.toDto(expressionUnary); + case final ExpressionList expressionNary -> expressionNaryService.toDto(expressionNary); + default -> throw new RuntimeException("Expression type not implemented: " + expression.getClass().getSimpleName()); + }; + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/ExpressionRepository.java b/src/main/java/de/ph87/home/expression/expression/ExpressionRepository.java new file mode 100644 index 0000000..d1671bc --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/ExpressionRepository.java @@ -0,0 +1,8 @@ +package de.ph87.home.expression.expression; + +import de.ph87.home.expression.AbstractExpression; +import de.ph87.home.expression.AbstractExpressionRepository; + +public interface ExpressionRepository extends AbstractExpressionRepository { + +} diff --git a/src/main/java/de/ph87/home/expression/expression/ExpressionService.java b/src/main/java/de/ph87/home/expression/expression/ExpressionService.java new file mode 100644 index 0000000..7472a39 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/ExpressionService.java @@ -0,0 +1,60 @@ +package de.ph87.home.expression.expression; + +import de.ph87.home.expression.AbstractExpression; +import de.ph87.home.expression.AbstractExpressionDto; +import de.ph87.home.expression.AbstractExpressionRepository; +import de.ph87.home.expression.AbstractExpressionService; +import de.ph87.home.expression.expression.literal.bool.ExpressionLiteralBoolean; +import de.ph87.home.expression.expression.literal.bool.ExpressionLiteralBooleanService; +import de.ph87.home.expression.expression.literal.number.ExpressionLiteralDouble; +import de.ph87.home.expression.expression.literal.number.ExpressionLiteralDoubleService; +import de.ph87.home.expression.expression.list.ExpressionList; +import de.ph87.home.expression.expression.list.ExpressionListService; +import de.ph87.home.expression.expression.property.ExpressionProperty; +import de.ph87.home.expression.expression.property.ExpressionPropertyService; +import de.ph87.home.expression.expression.unary.ExpressionUnary; +import de.ph87.home.expression.expression.unary.ExpressionUnaryService; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@Transactional +public class ExpressionService extends AbstractExpressionService { + + private final ExpressionLiteralBooleanService expressionLiteralBooleanService; + + private final ExpressionLiteralDoubleService expressionLiteralDoubleService; + + private final ExpressionUnaryService expressionUnaryService; + + private final ExpressionListService expressionNaryService; + + private final ExpressionPropertyService expressionPropertyService; + + public ExpressionService(final AbstractExpressionRepository repository, final ApplicationEventPublisher applicationEventPublisher, final ExpressionLiteralBooleanService expressionLiteralBooleanService, final ExpressionLiteralDoubleService expressionLiteralDoubleService, final ExpressionUnaryService expressionUnaryService, final ExpressionListService expressionNaryService, final ExpressionPropertyService expressionPropertyService) { + super(repository, applicationEventPublisher); + this.expressionLiteralBooleanService = expressionLiteralBooleanService; + this.expressionLiteralDoubleService = expressionLiteralDoubleService; + this.expressionUnaryService = expressionUnaryService; + this.expressionNaryService = expressionNaryService; + this.expressionPropertyService = expressionPropertyService; + } + + @NonNull + @Override + public AbstractExpressionDto toDto(@NonNull final AbstractExpression expression) { + return switch (expression) { + case final ExpressionLiteralBoolean expressionLiteralBoolean -> expressionLiteralBooleanService.toDto(expressionLiteralBoolean); + case final ExpressionLiteralDouble expressionLiteralDouble -> expressionLiteralDoubleService.toDto(expressionLiteralDouble); + case final ExpressionProperty expressionProperty -> expressionPropertyService.toDto(expressionProperty); + case final ExpressionUnary expressionUnary -> expressionUnaryService.toDto(expressionUnary); + case final ExpressionList expressionNary -> expressionNaryService.toDto(expressionNary); + default -> throw new RuntimeException("Expression type not implemented: " + expression.getClass().getSimpleName()); + }; + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/list/ExpressionList.java b/src/main/java/de/ph87/home/expression/expression/list/ExpressionList.java new file mode 100644 index 0000000..c02a175 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/list/ExpressionList.java @@ -0,0 +1,42 @@ +package de.ph87.home.expression.expression.list; + +import de.ph87.home.expression.AbstractExpression; +import de.ph87.home.expression.Runtime; +import jakarta.persistence.*; +import lombok.*; + +import java.util.ArrayList; +import java.util.List; + +@Setter +@Getter +@Entity +@NoArgsConstructor +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class ExpressionList extends AbstractExpression { + + @NonNull + @Enumerated(EnumType.STRING) + private ExpressionListOperator operator = ExpressionListOperator.OR; + + @NonNull + @ManyToMany + @OrderColumn + private List list = new ArrayList<>(); + + @Override + public Object evaluate(@NonNull final Runtime runtime) { + if (list.size() < 2) { + return null; + } + final List valueList = list.stream().map(e -> evalOrNull(runtime, e)).toList(); + Object vLast = valueList.removeFirst(); + while (!valueList.isEmpty()) { + final Object vNext = evalOrNull(runtime, list.removeFirst()); + vLast = operator.apply(vLast, vNext); + } + return vLast; + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/list/ExpressionListDto.java b/src/main/java/de/ph87/home/expression/expression/list/ExpressionListDto.java new file mode 100644 index 0000000..5fc1697 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/list/ExpressionListDto.java @@ -0,0 +1,29 @@ +package de.ph87.home.expression.expression.list; + +import de.ph87.home.expression.AbstractExpression; +import de.ph87.home.expression.AbstractExpressionDto; +import jakarta.annotation.Nullable; +import lombok.*; + +import java.util.List; +import java.util.stream.Collectors; + +@Setter +@Getter +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class ExpressionListDto extends AbstractExpressionDto { + + @NonNull + private final ExpressionListOperator operator; + + @NonNull + private final List expressionUuidList; + + public ExpressionListDto(@NonNull final ExpressionList expression, @Nullable final Object value) { + super(expression, value); + this.operator = expression.getOperator(); + this.expressionUuidList = expression.getList().stream().map(AbstractExpression::getUuid).collect(Collectors.toList()); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/list/ExpressionListOperator.java b/src/main/java/de/ph87/home/expression/expression/list/ExpressionListOperator.java new file mode 100644 index 0000000..bbca6a8 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/list/ExpressionListOperator.java @@ -0,0 +1,39 @@ +package de.ph87.home.expression.expression.list; + +import de.ph87.home.expression.AbstractExpression; +import jakarta.annotation.Nullable; +import lombok.NonNull; + +import java.util.function.BiFunction; + +import static de.ph87.home.expression.AbstractExpression.applyFallback; +import static de.ph87.home.expression.AbstractExpression.applyNullToNull; + +public enum ExpressionListOperator { + OR((v0, v1) -> applyFallback(v0, v1, AbstractExpression::convertToBoolean, false, (c0, c1) -> c0 || c1)), + AND((v0, v1) -> applyNullToNull(v0, v1, AbstractExpression::convertToBoolean, (c0, c1) -> c0 && c1)), + XOR((v0, v1) -> applyNullToNull(v0, v1, AbstractExpression::convertToBoolean, (c0, c1) -> c0 ^ c1)), + ADD((v0, v1) -> applyNullToNull(v0, v1, AbstractExpression::convertToNumber, Double::sum)), + SUB((v0, v1) -> applyNullToNull(v0, v1, AbstractExpression::convertToNumber, (c0, c1) -> c0 - c1)), + MUL((v0, v1) -> applyNullToNull(v0, v1, AbstractExpression::convertToNumber, (c0, c1) -> c0 * c1)), + DIV((v0, v1) -> applyNullToNull(v0, v1, AbstractExpression::convertToNumber, (c0, c1) -> c0 / c1)), + MOD((v0, v1) -> applyNullToNull(v0, v1, AbstractExpression::convertToNumber, (c0, c1) -> c0 % c1)), + POW((v0, v1) -> applyNullToNull(v0, v1, AbstractExpression::convertToNumber, Math::pow)), + HYPOT((v0, v1) -> applyNullToNull(v0, v1, AbstractExpression::convertToNumber, Math::hypot)), + ATAN2((v0, v1) -> applyNullToNull(v0, v1, AbstractExpression::convertToNumber, Math::atan2)), + MIN((v0, v1) -> applyNullToNull(v0, v1, AbstractExpression::convertToNumber, Math::min)), + MAX((v0, v1) -> applyNullToNull(v0, v1, AbstractExpression::convertToNumber, Math::max)), + ; + + private final BiFunction function; + + ExpressionListOperator(@NonNull final BiFunction function) { + this.function = function; + } + + @Nullable + public Object apply(@Nullable final Object v0, @Nullable final Object v1) { + return function.apply(v0, v1); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/list/ExpressionListRepository.java b/src/main/java/de/ph87/home/expression/expression/list/ExpressionListRepository.java new file mode 100644 index 0000000..25fa065 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/list/ExpressionListRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.expression.expression.list; + +import de.ph87.home.expression.AbstractExpressionRepository; + +public interface ExpressionListRepository extends AbstractExpressionRepository { + +} diff --git a/src/main/java/de/ph87/home/expression/expression/list/ExpressionListService.java b/src/main/java/de/ph87/home/expression/expression/list/ExpressionListService.java new file mode 100644 index 0000000..1f82501 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/list/ExpressionListService.java @@ -0,0 +1,28 @@ +package de.ph87.home.expression.expression.list; + +import de.ph87.home.expression.AbstractExpressionService; +import de.ph87.home.expression.Runtime; +import de.ph87.home.property.PropertyService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@Transactional +public class ExpressionListService extends AbstractExpressionService { + + private final PropertyService propertyService; + + public ExpressionListService(final ExpressionListRepository repository, final ApplicationEventPublisher applicationEventPublisher, final PropertyService propertyService) { + super(repository, applicationEventPublisher); + this.propertyService = propertyService; + } + + @Override + public ExpressionListDto toDto(final ExpressionList expression) { + return new ExpressionListDto(expression, expression.evaluate(new Runtime(propertyService))); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/literal/ExpressionLiteral.java b/src/main/java/de/ph87/home/expression/expression/literal/ExpressionLiteral.java new file mode 100644 index 0000000..a8ff617 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/literal/ExpressionLiteral.java @@ -0,0 +1,32 @@ +package de.ph87.home.expression.expression.literal; + +import de.ph87.home.expression.AbstractExpression; +import de.ph87.home.expression.Runtime; +import jakarta.annotation.Nullable; +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; +import lombok.*; + +@Setter +@Getter +@MappedSuperclass +@NoArgsConstructor +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public abstract class ExpressionLiteral extends AbstractExpression { + + @Nullable + @Column(name = "`value`") + private T value = null; + + public ExpressionLiteral(final T value) { + this.value = value; + } + + @Nullable + @Override + public Object evaluate(@NonNull final Runtime runtime) { + return value; + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/literal/ExpressionLiteralController.java b/src/main/java/de/ph87/home/expression/expression/literal/ExpressionLiteralController.java new file mode 100644 index 0000000..69e16c7 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/literal/ExpressionLiteralController.java @@ -0,0 +1,23 @@ +package de.ph87.home.expression.expression.literal; + +import de.ph87.home.expression.AbstractExpressionController; +import de.ph87.home.expression.AbstractExpressionDto; +import jakarta.annotation.Nullable; +import lombok.NonNull; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +public abstract class ExpressionLiteralController, D extends AbstractExpressionDto> extends AbstractExpressionController { + + public ExpressionLiteralController(final ExpressionLiteralService expressionService) { + super(expressionService); + } + + @NonNull + @PostMapping("{uuid}/setValue") + public D setValue(@PathVariable final String uuid, @RequestBody(required = false) @Nullable final T value) { + return expressionService.set(uuid, e -> e.setValue(value)); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/literal/ExpressionLiteralService.java b/src/main/java/de/ph87/home/expression/expression/literal/ExpressionLiteralService.java new file mode 100644 index 0000000..992c668 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/literal/ExpressionLiteralService.java @@ -0,0 +1,20 @@ +package de.ph87.home.expression.expression.literal; + +import de.ph87.home.expression.AbstractExpressionDto; +import de.ph87.home.expression.AbstractExpressionRepository; +import de.ph87.home.expression.AbstractExpressionService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@Transactional +public abstract class ExpressionLiteralService, D extends AbstractExpressionDto> extends AbstractExpressionService { + + public ExpressionLiteralService(final AbstractExpressionRepository repository, final ApplicationEventPublisher applicationEventPublisher) { + super(repository, applicationEventPublisher); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBoolean.java b/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBoolean.java new file mode 100644 index 0000000..00320a4 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBoolean.java @@ -0,0 +1,20 @@ +package de.ph87.home.expression.expression.literal.bool; + +import de.ph87.home.expression.expression.literal.ExpressionLiteral; +import jakarta.annotation.Nullable; +import jakarta.persistence.Entity; +import lombok.*; + +@Setter +@Getter +@Entity +@NoArgsConstructor +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class ExpressionLiteralBoolean extends ExpressionLiteral { + + public ExpressionLiteralBoolean(@Nullable final Boolean value) { + super(value); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanController.java b/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanController.java new file mode 100644 index 0000000..bf375cb --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanController.java @@ -0,0 +1,16 @@ +package de.ph87.home.expression.expression.literal.bool; + +import de.ph87.home.expression.expression.literal.ExpressionLiteralController; +import de.ph87.home.expression.expression.literal.ExpressionLiteralService; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("ExpressionLiteralBoolean") +public class ExpressionLiteralBooleanController extends ExpressionLiteralController { + + public ExpressionLiteralBooleanController(final ExpressionLiteralService expressionLiteralService) { + super(expressionLiteralService); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanDto.java b/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanDto.java new file mode 100644 index 0000000..200cb46 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanDto.java @@ -0,0 +1,18 @@ +package de.ph87.home.expression.expression.literal.bool; + +import de.ph87.home.expression.AbstractExpressionDto; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; + +@Getter +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class ExpressionLiteralBooleanDto extends AbstractExpressionDto { + + protected ExpressionLiteralBooleanDto(@NonNull final ExpressionLiteralBoolean expression) { + super(expression, expression.getValue()); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanRepository.java b/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanRepository.java new file mode 100644 index 0000000..e68b693 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanRepository.java @@ -0,0 +1,9 @@ +package de.ph87.home.expression.expression.literal.bool; + +import de.ph87.home.expression.AbstractExpressionRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ExpressionLiteralBooleanRepository extends AbstractExpressionRepository { + +} diff --git a/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanService.java b/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanService.java new file mode 100644 index 0000000..29e482d --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/literal/bool/ExpressionLiteralBooleanService.java @@ -0,0 +1,23 @@ +package de.ph87.home.expression.expression.literal.bool; + +import de.ph87.home.expression.expression.literal.ExpressionLiteralService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@Transactional +public class ExpressionLiteralBooleanService extends ExpressionLiteralService { + + public ExpressionLiteralBooleanService(final ExpressionLiteralBooleanRepository repository, final ApplicationEventPublisher applicationEventPublisher) { + super(repository, applicationEventPublisher); + } + + @Override + public ExpressionLiteralBooleanDto toDto(final ExpressionLiteralBoolean expression) { + return new ExpressionLiteralBooleanDto(expression); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDouble.java b/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDouble.java new file mode 100644 index 0000000..35b16a7 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDouble.java @@ -0,0 +1,20 @@ +package de.ph87.home.expression.expression.literal.number; + +import de.ph87.home.expression.expression.literal.ExpressionLiteral; +import jakarta.annotation.Nullable; +import jakarta.persistence.Entity; +import lombok.*; + +@Setter +@Getter +@Entity +@NoArgsConstructor +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class ExpressionLiteralDouble extends ExpressionLiteral { + + public ExpressionLiteralDouble(@Nullable final Double value) { + super(value); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleController.java b/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleController.java new file mode 100644 index 0000000..468d5b3 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleController.java @@ -0,0 +1,16 @@ +package de.ph87.home.expression.expression.literal.number; + +import de.ph87.home.expression.expression.literal.ExpressionLiteralController; +import de.ph87.home.expression.expression.literal.ExpressionLiteralService; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("ExpressionLiteralDouble") +public class ExpressionLiteralDoubleController extends ExpressionLiteralController { + + public ExpressionLiteralDoubleController(final ExpressionLiteralService expressionLiteralService) { + super(expressionLiteralService); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleDto.java b/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleDto.java new file mode 100644 index 0000000..e635a79 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleDto.java @@ -0,0 +1,18 @@ +package de.ph87.home.expression.expression.literal.number; + +import de.ph87.home.expression.AbstractExpressionDto; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; + +@Getter +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class ExpressionLiteralDoubleDto extends AbstractExpressionDto { + + protected ExpressionLiteralDoubleDto(@NonNull final ExpressionLiteralDouble expression) { + super(expression, expression.getValue()); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleRepository.java b/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleRepository.java new file mode 100644 index 0000000..4fe8849 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.expression.expression.literal.number; + +import de.ph87.home.expression.AbstractExpressionRepository; + +public interface ExpressionLiteralDoubleRepository extends AbstractExpressionRepository { + +} diff --git a/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleService.java b/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleService.java new file mode 100644 index 0000000..7a1e0db --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/literal/number/ExpressionLiteralDoubleService.java @@ -0,0 +1,23 @@ +package de.ph87.home.expression.expression.literal.number; + +import de.ph87.home.expression.expression.literal.ExpressionLiteralService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@Transactional +public class ExpressionLiteralDoubleService extends ExpressionLiteralService { + + public ExpressionLiteralDoubleService(final ExpressionLiteralDoubleRepository repository, final ApplicationEventPublisher applicationEventPublisher) { + super(repository, applicationEventPublisher); + } + + @Override + public ExpressionLiteralDoubleDto toDto(final ExpressionLiteralDouble expression) { + return new ExpressionLiteralDoubleDto(expression); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/property/ExpressionProperty.java b/src/main/java/de/ph87/home/expression/expression/property/ExpressionProperty.java new file mode 100644 index 0000000..f4b7740 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/property/ExpressionProperty.java @@ -0,0 +1,28 @@ +package de.ph87.home.expression.expression.property; + +import de.ph87.home.expression.AbstractExpression; +import de.ph87.home.expression.Runtime; +import de.ph87.home.property.Property; +import de.ph87.home.property.State; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import lombok.*; + +@Setter +@Getter +@Entity +@NoArgsConstructor +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class ExpressionProperty extends AbstractExpression { + + @NonNull + @Column(nullable = false) + private String propertyId; + + @Override + public Object evaluate(@NonNull final Runtime runtime) { + return runtime.propertyService.findById(propertyId).map(Property::getState).map(State::getValue).orElse(null); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/property/ExpressionPropertyDto.java b/src/main/java/de/ph87/home/expression/expression/property/ExpressionPropertyDto.java new file mode 100644 index 0000000..a05b8e1 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/property/ExpressionPropertyDto.java @@ -0,0 +1,29 @@ +package de.ph87.home.expression.expression.property; + +import de.ph87.home.expression.AbstractExpressionDto; +import de.ph87.home.property.IProperty; +import de.ph87.home.property.PropertyDto; +import jakarta.annotation.Nullable; +import lombok.*; + +import static de.ph87.home.expression.AbstractExpression.mapOrElse; + +@Setter +@Getter +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class ExpressionPropertyDto extends AbstractExpressionDto { + + @NonNull + private final String propertyId; + + @Nullable + private final PropertyDto property; + + public ExpressionPropertyDto(@NonNull final ExpressionProperty expressionProperty, @Nullable final PropertyDto property) { + super(expressionProperty, mapOrElse(property, IProperty::getStateValue, null)); + this.propertyId = expressionProperty.getPropertyId(); + this.property = property; + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/property/ExpressionPropertyRepository.java b/src/main/java/de/ph87/home/expression/expression/property/ExpressionPropertyRepository.java new file mode 100644 index 0000000..0f2b7b4 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/property/ExpressionPropertyRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.expression.expression.property; + +import de.ph87.home.expression.AbstractExpressionRepository; + +public interface ExpressionPropertyRepository extends AbstractExpressionRepository { + +} diff --git a/src/main/java/de/ph87/home/expression/expression/property/ExpressionPropertyService.java b/src/main/java/de/ph87/home/expression/expression/property/ExpressionPropertyService.java new file mode 100644 index 0000000..2275478 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/property/ExpressionPropertyService.java @@ -0,0 +1,29 @@ +package de.ph87.home.expression.expression.property; + +import de.ph87.home.expression.AbstractExpressionService; +import de.ph87.home.property.PropertyDto; +import de.ph87.home.property.PropertyService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@Transactional +public class ExpressionPropertyService extends AbstractExpressionService { + + private final PropertyService propertyService; + + public ExpressionPropertyService(final ExpressionPropertyRepository repository, final ApplicationEventPublisher applicationEventPublisher, final PropertyService propertyService) { + super(repository, applicationEventPublisher); + this.propertyService = propertyService; + } + + @Override + public ExpressionPropertyDto toDto(final ExpressionProperty expressionProperty) { + final PropertyDto property = propertyService.dtoByIdOrNull(expressionProperty.getPropertyId()); + return new ExpressionPropertyDto(expressionProperty, property); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnary.java b/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnary.java new file mode 100644 index 0000000..e8dedc9 --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnary.java @@ -0,0 +1,31 @@ +package de.ph87.home.expression.expression.unary; + +import de.ph87.home.expression.AbstractExpression; +import de.ph87.home.expression.Runtime; +import jakarta.annotation.Nullable; +import jakarta.persistence.*; +import lombok.*; + +@Setter +@Getter +@Entity +@NoArgsConstructor +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class ExpressionUnary extends AbstractExpression { + + @NonNull + @Enumerated(EnumType.STRING) + private ExpressionUnaryOperator operator = ExpressionUnaryOperator.NOT; + + @Nullable + @ManyToOne(fetch = FetchType.LAZY) + private AbstractExpression expression; + + @Override + public Object evaluate(@NonNull final Runtime runtime) { + final Object value = evalOrNull(runtime, expression); + return operator.apply(value); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryDto.java b/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryDto.java new file mode 100644 index 0000000..97becef --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryDto.java @@ -0,0 +1,28 @@ +package de.ph87.home.expression.expression.unary; + +import de.ph87.home.expression.AbstractExpression; +import de.ph87.home.expression.AbstractExpressionDto; +import jakarta.annotation.Nullable; +import lombok.*; + +import static de.ph87.home.expression.AbstractExpression.mapOrElse; + +@Setter +@Getter +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class ExpressionUnaryDto extends AbstractExpressionDto { + + @NonNull + private final ExpressionUnaryOperator operator; + + @Nullable + private final String expressionUuid; + + public ExpressionUnaryDto(@NonNull final ExpressionUnary expressionUnary, @Nullable final Object value) { + super(expressionUnary, value); + this.operator = expressionUnary.getOperator(); + this.expressionUuid = mapOrElse(expressionUnary.getExpression(), AbstractExpression::getUuid, null); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryOperator.java b/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryOperator.java new file mode 100644 index 0000000..925941b --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryOperator.java @@ -0,0 +1,42 @@ +package de.ph87.home.expression.expression.unary; + +import de.ph87.home.expression.AbstractExpression; +import jakarta.annotation.Nullable; +import lombok.NonNull; + +import java.util.function.Function; + +public enum ExpressionUnaryOperator { + NOT(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToBoolean, c -> !c)), + NEG(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, c -> -c)), + POS(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, c -> +c)), + FLOOR(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::floor)), + CEIL(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::ceil)), + ROUND(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::round)), + SQRT(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::sqrt)), + ABS(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::abs)), + COS(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::cos)), + SIN(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::sin)), + TAN(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::tan)), + ACOS(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::acos)), + ASIN(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::asin)), + ATAN(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::atan)), + COSH(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::cosh)), + SINH(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::sinh)), + TANH(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::tanh)), + EXP(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::exp)), + EXPM1(v -> AbstractExpression.applyNullToNull(v, AbstractExpression::convertToNumber, Math::expm1)), + ; + + private final Function function; + + ExpressionUnaryOperator(@NonNull final Function function) { + this.function = function; + } + + @Nullable + public Object apply(@Nullable final Object value) { + return function.apply(value); + } + +} diff --git a/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryRepository.java b/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryRepository.java new file mode 100644 index 0000000..1dc4abf --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.expression.expression.unary; + +import de.ph87.home.expression.AbstractExpressionRepository; + +public interface ExpressionUnaryRepository extends AbstractExpressionRepository { + +} diff --git a/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryService.java b/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryService.java new file mode 100644 index 0000000..9081a5c --- /dev/null +++ b/src/main/java/de/ph87/home/expression/expression/unary/ExpressionUnaryService.java @@ -0,0 +1,28 @@ +package de.ph87.home.expression.expression.unary; + +import de.ph87.home.expression.AbstractExpressionService; +import de.ph87.home.expression.Runtime; +import de.ph87.home.property.PropertyService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@Transactional +public class ExpressionUnaryService extends AbstractExpressionService { + + private final PropertyService propertyService; + + public ExpressionUnaryService(final ExpressionUnaryRepository repository, final ApplicationEventPublisher applicationEventPublisher, final PropertyService propertyService) { + super(repository, applicationEventPublisher); + this.propertyService = propertyService; + } + + @Override + public ExpressionUnaryDto toDto(final ExpressionUnary expression) { + return new ExpressionUnaryDto(expression, expression.evaluate(new Runtime(propertyService))); + } + +} diff --git a/src/main/java/de/ph87/home/property/PropertyService.java b/src/main/java/de/ph87/home/property/PropertyService.java index 79e0f06..353a381 100644 --- a/src/main/java/de/ph87/home/property/PropertyService.java +++ b/src/main/java/de/ph87/home/property/PropertyService.java @@ -89,7 +89,7 @@ public class PropertyService { } @NonNull - private Optional> findById(final String id) { + public Optional> findById(final String id) { return propertyList.stream().filter(p -> p.getId().equals(id)).findFirst(); } @@ -120,4 +120,9 @@ public class PropertyService { } } + @Nullable + public PropertyDto dtoByIdOrNull(@NonNull final String propertyId) { + return findById(propertyId).map(this::toDto).orElse(null); + } + }