diff --git a/src/main/java/de/ph87/data/series/Alignment.java b/src/main/java/de/ph87/data/series/Alignment.java index 6e1f2b3..db5b288 100644 --- a/src/main/java/de/ph87/data/series/Alignment.java +++ b/src/main/java/de/ph87/data/series/Alignment.java @@ -24,7 +24,7 @@ public enum Alignment { @NonNull public final BiFunction plus; - Alignment(final @NonNull Duration maxDuration, @NonNull final Function align, @NonNull final BiFunction plus) { + Alignment(@NonNull final Duration maxDuration, @NonNull final Function<@NonNull ZonedDateTime, @NonNull ZonedDateTime> align, @NonNull final BiFunction<@NonNull ZonedDateTime, @NonNull Long, @NonNull ZonedDateTime> plus) { this.maxDuration = maxDuration; this.align = align; this.plus = plus; diff --git a/src/main/java/de/ph87/data/series/Series.java b/src/main/java/de/ph87/data/series/Series.java index 6df67da..dc4e2b5 100644 --- a/src/main/java/de/ph87/data/series/Series.java +++ b/src/main/java/de/ph87/data/series/Series.java @@ -1,5 +1,6 @@ package de.ph87.data.series; +import de.ph87.data.value.Value; import de.ph87.data.value.*; import jakarta.persistence.*; import lombok.*; @@ -43,11 +44,36 @@ public class Series { @Column(nullable = false) private boolean graphAutoscale = false; - public Series(@NonNull final String name, @NonNull final Unit unit, @NonNull final SeriesType type) { + @Column(nullable = false) + private double min; + + @Column(nullable = false) + private double max; + + @Column(nullable = false) + private double avg; + + @Column(nullable = false) + private int count; + + public Series(@NonNull final String name, @NonNull final Unit unit, @NonNull final SeriesType type, @NonNull final Value value) { this.name = name; this.title = name; this.unit = unit; this.type = type; + + final double converted = value.as(unit).value; + this.min = converted; + this.max = converted; + this.avg = converted; + this.count = 1; + } + + public void update(@NonNull final Value value) { + final double converted = value.as(unit).value; + this.min = Math.min(this.min, converted); + this.max = Math.max(this.max, converted); + this.avg = (this.avg * this.count + converted) / ++this.count; } } diff --git a/src/main/java/de/ph87/data/series/SeriesInbound.java b/src/main/java/de/ph87/data/series/SeriesInbound.java new file mode 100644 index 0000000..fec538b --- /dev/null +++ b/src/main/java/de/ph87/data/series/SeriesInbound.java @@ -0,0 +1,26 @@ +package de.ph87.data.series; + +import de.ph87.data.value.Value; +import lombok.*; + +import java.time.*; + +@Data +public class SeriesInbound { + + @NonNull + public final String name; + + @NonNull + public final ZonedDateTime date; + + @NonNull + public final Value value; + + public SeriesInbound(@NonNull final String name, @NonNull final ZonedDateTime date, @NonNull final Value value) { + this.name = name; + this.date = date; + this.value = value; + } + +} diff --git a/src/main/java/de/ph87/data/series/SeriesService.java b/src/main/java/de/ph87/data/series/SeriesService.java index 2c607c4..5849846 100644 --- a/src/main/java/de/ph87/data/series/SeriesService.java +++ b/src/main/java/de/ph87/data/series/SeriesService.java @@ -1,7 +1,6 @@ package de.ph87.data.series; import de.ph87.data.*; -import de.ph87.data.value.*; import lombok.*; import lombok.extern.slf4j.*; import org.springframework.stereotype.*; @@ -31,35 +30,41 @@ public class SeriesService { publish(series, Action.DELETED); } + @NonNull private Series getById(final long id) { return seriesRepository.findById(id).orElseThrow(); } + @NonNull public SeriesDto getDtoById(final long id) { return toDto(getById(id)); } + @NonNull private SeriesDto publish(@NonNull final Series series, @NonNull final Action action) { final SeriesDto dto = toDto(series); log.info("Series {}: {}", action, series); return dto; } + @NonNull private SeriesDto toDto(@NonNull final Series series) { return new SeriesDto(series); } - public Series getOrCreate(@NonNull final String name, @NonNull final Unit unit, @NonNull final SeriesType type) { + @NonNull + public Series getOrCreate(@NonNull final SeriesInbound inbound, @NonNull final SeriesType type) { final Series series = seriesRepository - .findByName(name) + .findByName(inbound.getName()) .orElseGet(() -> { - final Series fresh = seriesRepository.save(new Series(name, unit, type)); + final Series fresh = seriesRepository.save(new Series(inbound.name, inbound.value.unit, type, inbound.value)); publish(fresh, Action.CREATED); return fresh; }); if (series.getType() != type) { log.warn("Existing Series type does not match requested type: requested={}, series={}", type, series); } + series.update(inbound.value); return series; } diff --git a/src/main/java/de/ph87/data/series/meter/Meter.java b/src/main/java/de/ph87/data/series/meter/Meter.java index c789903..deca7a4 100644 --- a/src/main/java/de/ph87/data/series/meter/Meter.java +++ b/src/main/java/de/ph87/data/series/meter/Meter.java @@ -1,6 +1,7 @@ package de.ph87.data.series.meter; import de.ph87.data.series.*; +import de.ph87.data.value.Value; import jakarta.persistence.*; import lombok.*; @@ -31,10 +32,26 @@ public class Meter { @Column(nullable = false) private String number; - public Meter(@NonNull final Series series, @NonNull final ZonedDateTime date, @NonNull final String number) { + @Column(nullable = false) + private double min; + + @Column(nullable = false) + private double max; + + public Meter(@NonNull final Series series, @NonNull final ZonedDateTime date, @NonNull final String number, @NonNull final Value value) { this.series = series; this.date = date; this.number = number; + + final double converted = value.as(series.getUnit()).value; + this.min = converted; + this.max = converted; + } + + public void update(@NonNull final Value value) { + final double converted = value.as(series.getUnit()).value; + this.min = Math.min(this.min, converted); + this.max = Math.max(this.max, converted); } } diff --git a/src/main/java/de/ph87/data/series/meter/MeterInbound.java b/src/main/java/de/ph87/data/series/meter/MeterInbound.java index 99d9c53..43edd0c 100644 --- a/src/main/java/de/ph87/data/series/meter/MeterInbound.java +++ b/src/main/java/de/ph87/data/series/meter/MeterInbound.java @@ -1,5 +1,6 @@ package de.ph87.data.series.meter; +import de.ph87.data.series.*; import de.ph87.data.value.Value; import lombok.*; @@ -7,24 +8,14 @@ import java.time.*; @Getter @ToString -public class MeterInbound { - - @NonNull - public final String seriesName; +public class MeterInbound extends SeriesInbound { @NonNull public final String meterNumber; - @NonNull - public final ZonedDateTime date; - - public final Value value; - - public MeterInbound(@NonNull final String seriesName, @NonNull final String meterNumber, @NonNull final ZonedDateTime date, final Value value) { - this.seriesName = seriesName; + public MeterInbound(@NonNull final String name, @NonNull final String meterNumber, @NonNull final ZonedDateTime date, final Value value) { + super(name, date, value); this.meterNumber = meterNumber; - this.date = date; - this.value = value; } } diff --git a/src/main/java/de/ph87/data/series/meter/MeterService.java b/src/main/java/de/ph87/data/series/meter/MeterService.java index 0b3891e..95409ba 100644 --- a/src/main/java/de/ph87/data/series/meter/MeterService.java +++ b/src/main/java/de/ph87/data/series/meter/MeterService.java @@ -40,34 +40,37 @@ public class MeterService { private final MeterYearRepository meterYearRepository; @EventListener(MeterInbound.class) - public void onEvent(@NonNull final MeterInbound event) { - final Meter meter = getOrCreate(event); + public void onEvent(@NonNull final MeterInbound inbound) { + final Meter meter = getOrCreate(inbound); - final MeterValue.Id five = new MeterValue.Id(meter, event.date, Alignment.FIVE); - meterFiveRepository.findById(five).ifPresentOrElse(existing -> existing.update(event.value), () -> meterFiveRepository.save(new MeterFive(five, event.value))); + final MeterValue.Id five = new MeterValue.Id(meter, inbound.date, Alignment.FIVE); + meterFiveRepository.findById(five).ifPresentOrElse(existing -> existing.update(inbound.value), () -> meterFiveRepository.save(new MeterFive(five, inbound.value))); - final MeterValue.Id hour = new MeterValue.Id(meter, event.date, Alignment.HOUR); - meterHourRepository.findById(hour).ifPresentOrElse(existing -> existing.update(event.value), () -> meterHourRepository.save(new MeterHour(hour, event.value))); + final MeterValue.Id hour = new MeterValue.Id(meter, inbound.date, Alignment.HOUR); + meterHourRepository.findById(hour).ifPresentOrElse(existing -> existing.update(inbound.value), () -> meterHourRepository.save(new MeterHour(hour, inbound.value))); - final MeterValue.Id day = new MeterValue.Id(meter, event.date, Alignment.DAY); - meterDayRepository.findById(day).ifPresentOrElse(existing -> existing.update(event.value), () -> meterDayRepository.save(new MeterDay(day, event.value))); + final MeterValue.Id day = new MeterValue.Id(meter, inbound.date, Alignment.DAY); + meterDayRepository.findById(day).ifPresentOrElse(existing -> existing.update(inbound.value), () -> meterDayRepository.save(new MeterDay(day, inbound.value))); - final MeterValue.Id week = new MeterValue.Id(meter, event.date, Alignment.WEEK); - meterWeekRepository.findById(week).ifPresentOrElse(existing -> existing.update(event.value), () -> meterWeekRepository.save(new MeterWeek(week, event.value))); + final MeterValue.Id week = new MeterValue.Id(meter, inbound.date, Alignment.WEEK); + meterWeekRepository.findById(week).ifPresentOrElse(existing -> existing.update(inbound.value), () -> meterWeekRepository.save(new MeterWeek(week, inbound.value))); - final MeterValue.Id month = new MeterValue.Id(meter, event.date, Alignment.MONTH); - meterMonthRepository.findById(month).ifPresentOrElse(existing -> existing.update(event.value), () -> meterMonthRepository.save(new MeterMonth(month, event.value))); + final MeterValue.Id month = new MeterValue.Id(meter, inbound.date, Alignment.MONTH); + meterMonthRepository.findById(month).ifPresentOrElse(existing -> existing.update(inbound.value), () -> meterMonthRepository.save(new MeterMonth(month, inbound.value))); - final MeterValue.Id year = new MeterValue.Id(meter, event.date, Alignment.YEAR); - meterYearRepository.findById(year).ifPresentOrElse(existing -> existing.update(event.value), () -> meterYearRepository.save(new MeterYear(year, event.value))); + final MeterValue.Id year = new MeterValue.Id(meter, inbound.date, Alignment.YEAR); + meterYearRepository.findById(year).ifPresentOrElse(existing -> existing.update(inbound.value), () -> meterYearRepository.save(new MeterYear(year, inbound.value))); } @NonNull - private Meter getOrCreate(@NonNull final MeterInbound event) { - final Series series = seriesService.getOrCreate(event.seriesName, event.value.unit, SeriesType.METER); + private Meter getOrCreate(@NonNull final MeterInbound inbound) { + final Series series = seriesService.getOrCreate(inbound, SeriesType.METER); return meterRepository.findFirstBySeriesOrderByDateDesc(series) - .filter(m -> m.getNumber().equals(event.meterNumber)) - .orElseGet(() -> meterRepository.save(new Meter(series, event.date, event.meterNumber))); + .filter(m -> m.getNumber().equals(inbound.meterNumber)) + .stream() + .peek(existing -> existing.update(inbound.value)) + .findFirst() + .orElseGet(() -> meterRepository.save(new Meter(series, inbound.date, inbound.meterNumber, inbound.value))); } @NonNull @@ -80,7 +83,7 @@ public class MeterService { case MONTH -> meterMonthRepository.findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqualOrderByIdDate(series.id, begin.date, end.date); case YEAR -> meterYearRepository.findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqualOrderByIdDate(series.id, begin.date, end.date); }; - final List points = graphPoints.stream().map(v -> new GraphPoint(v.getId().getDate(), v.getMax() - v.getMin())).collect(Collectors.toCollection(LinkedList::new)); + final List points = graphPoints.stream().map(meterValue -> new GraphPoint(meterValue.getId().getDate(), meterValue.getMax() - meterValue.getMin())).collect(Collectors.toCollection(LinkedList::new)); for (int i = 0; i < points.size() - 1; i++) { if (points.get(i).date.compareTo(points.get(i + 1).date) == 0) { final GraphPoint first = points.remove(i); diff --git a/src/main/java/de/ph87/data/series/varying/VaryingInbound.java b/src/main/java/de/ph87/data/series/varying/VaryingInbound.java index d0d1e12..d01ee5e 100644 --- a/src/main/java/de/ph87/data/series/varying/VaryingInbound.java +++ b/src/main/java/de/ph87/data/series/varying/VaryingInbound.java @@ -1,5 +1,6 @@ package de.ph87.data.series.varying; +import de.ph87.data.series.*; import de.ph87.data.value.Value; import lombok.*; @@ -7,20 +8,10 @@ import java.time.*; @Getter @ToString -public class VaryingInbound { +public class VaryingInbound extends SeriesInbound { - @NonNull - public final String name; - - @NonNull - public final ZonedDateTime date; - - public final Value value; - - public VaryingInbound(@NonNull final String name, @NonNull final ZonedDateTime date, final Value value) { - this.name = name; - this.date = date; - this.value = value; + public VaryingInbound(@NonNull final String name, @NonNull final ZonedDateTime date, @NonNull final Value value) { + super(name, date, value); } } diff --git a/src/main/java/de/ph87/data/series/varying/VaryingService.java b/src/main/java/de/ph87/data/series/varying/VaryingService.java index eae7d3f..ef5c268 100644 --- a/src/main/java/de/ph87/data/series/varying/VaryingService.java +++ b/src/main/java/de/ph87/data/series/varying/VaryingService.java @@ -26,6 +26,8 @@ public class VaryingService { private final VaryingFiveRepository varyingFiveRepository; + private final VaryingHourRepository varyingHourRepository; + private final VaryingDayRepository varyingDayRepository; private final VaryingWeekRepository varyingWeekRepository; @@ -34,29 +36,27 @@ public class VaryingService { private final VaryingYearRepository varyingYearRepository; - private final VaryingHourRepository varyingHourRepository; - @EventListener(VaryingInbound.class) - public void onEvent(@NonNull final VaryingInbound event) { - final Series series = seriesService.getOrCreate(event.name, event.value.unit, SeriesType.VARYING); + public void onEvent(@NonNull final VaryingInbound inbound) { + final Series series = seriesService.getOrCreate(inbound, SeriesType.VARYING); - final Varying.Id five = new Varying.Id(series, event.date, Alignment.FIVE); - varyingFiveRepository.findById(five).ifPresentOrElse(existing -> existing.update(event.value), () -> varyingFiveRepository.save(new VaryingFive(five, event.value))); + final Varying.Id five = new Varying.Id(series, inbound.date, Alignment.FIVE); + varyingFiveRepository.findById(five).ifPresentOrElse(existing -> existing.update(inbound.value), () -> varyingFiveRepository.save(new VaryingFive(five, inbound.value))); - final Varying.Id hour = new Varying.Id(series, event.date, Alignment.HOUR); - varyingHourRepository.findById(hour).ifPresentOrElse(existing -> existing.update(event.value), () -> varyingHourRepository.save(new VaryingHour(hour, event.value))); + final Varying.Id hour = new Varying.Id(series, inbound.date, Alignment.HOUR); + varyingHourRepository.findById(hour).ifPresentOrElse(existing -> existing.update(inbound.value), () -> varyingHourRepository.save(new VaryingHour(hour, inbound.value))); - final Varying.Id day = new Varying.Id(series, event.date, Alignment.DAY); - varyingDayRepository.findById(day).ifPresentOrElse(existing -> existing.update(event.value), () -> varyingDayRepository.save(new VaryingDay(day, event.value))); + final Varying.Id day = new Varying.Id(series, inbound.date, Alignment.DAY); + varyingDayRepository.findById(day).ifPresentOrElse(existing -> existing.update(inbound.value), () -> varyingDayRepository.save(new VaryingDay(day, inbound.value))); - final Varying.Id week = new Varying.Id(series, event.date, Alignment.WEEK); - varyingWeekRepository.findById(week).ifPresentOrElse(existing -> existing.update(event.value), () -> varyingWeekRepository.save(new VaryingWeek(week, event.value))); + final Varying.Id week = new Varying.Id(series, inbound.date, Alignment.WEEK); + varyingWeekRepository.findById(week).ifPresentOrElse(existing -> existing.update(inbound.value), () -> varyingWeekRepository.save(new VaryingWeek(week, inbound.value))); - final Varying.Id month = new Varying.Id(series, event.date, Alignment.MONTH); - varyingMonthRepository.findById(month).ifPresentOrElse(existing -> existing.update(event.value), () -> varyingMonthRepository.save(new VaryingMonth(month, event.value))); + final Varying.Id month = new Varying.Id(series, inbound.date, Alignment.MONTH); + varyingMonthRepository.findById(month).ifPresentOrElse(existing -> existing.update(inbound.value), () -> varyingMonthRepository.save(new VaryingMonth(month, inbound.value))); - final Varying.Id year = new Varying.Id(series, event.date, Alignment.YEAR); - varyingYearRepository.findById(year).ifPresentOrElse(existing -> existing.update(event.value), () -> varyingYearRepository.save(new VaryingYear(year, event.value))); + final Varying.Id year = new Varying.Id(series, inbound.date, Alignment.YEAR); + varyingYearRepository.findById(year).ifPresentOrElse(existing -> existing.update(inbound.value), () -> varyingYearRepository.save(new VaryingYear(year, inbound.value))); } @NonNull