diff --git a/src/main/java/de/ph87/data/message/handler/EspHomeHandler.java b/src/main/java/de/ph87/data/message/handler/EspHomeHandler.java index 5166071..33b9c10 100644 --- a/src/main/java/de/ph87/data/message/handler/EspHomeHandler.java +++ b/src/main/java/de/ph87/data/message/handler/EspHomeHandler.java @@ -4,11 +4,12 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.*; import de.ph87.data.message.*; -import de.ph87.data.series.entry.*; +import de.ph87.data.series.varying.*; import de.ph87.data.value.Value; import de.ph87.data.value.*; import lombok.*; import lombok.extern.slf4j.*; +import org.springframework.context.*; import org.springframework.stereotype.*; import java.time.*; @@ -25,7 +26,7 @@ public class EspHomeHandler implements IMessageHandler { private final ObjectMapper objectMapper; - private final EntryService entryService; + private final ApplicationEventPublisher applicationEventPublisher; @Override public void handle(@NonNull final Message message) throws Exception { @@ -58,7 +59,7 @@ public class EspHomeHandler implements IMessageHandler { return; } final Value value = new Value(inbound.value, unitFromPayload); - entryService.receive(name, inbound.date, value.as(targetUnit)); + applicationEventPublisher.publishEvent(new VaryingInbound(name, inbound.date, value.as(targetUnit))); } private String propertyReplace(final String property) { diff --git a/src/main/java/de/ph87/data/message/handler/HeizungHandler.java b/src/main/java/de/ph87/data/message/handler/HeizungHandler.java index 739fd54..c3f7073 100644 --- a/src/main/java/de/ph87/data/message/handler/HeizungHandler.java +++ b/src/main/java/de/ph87/data/message/handler/HeizungHandler.java @@ -2,11 +2,12 @@ package de.ph87.data.message.handler; import com.fasterxml.jackson.databind.*; import de.ph87.data.message.*; -import de.ph87.data.series.entry.*; +import de.ph87.data.series.varying.*; import de.ph87.data.value.Value; import de.ph87.data.value.*; import lombok.*; import lombok.extern.slf4j.*; +import org.springframework.context.*; import org.springframework.stereotype.*; import java.time.*; @@ -21,7 +22,7 @@ public class HeizungHandler implements IMessageHandler { private final ObjectMapper objectMapper; - private final EntryService entryService; + private final ApplicationEventPublisher applicationEventPublisher; @Override public void handle(@NonNull final Message message) throws Exception { @@ -31,7 +32,7 @@ public class HeizungHandler implements IMessageHandler { } final String property = matcher.group("property"); final Inbound inbound = objectMapper.readValue(message.payload, Inbound.class); - entryService.receive(property, inbound.date, inbound.value); + applicationEventPublisher.publishEvent(new VaryingInbound(property, inbound.date, inbound.value)); } @Getter diff --git a/src/main/java/de/ph87/data/message/handler/OpenDTUHandler.java b/src/main/java/de/ph87/data/message/handler/OpenDTUHandler.java index d96b128..f8eb379 100644 --- a/src/main/java/de/ph87/data/message/handler/OpenDTUHandler.java +++ b/src/main/java/de/ph87/data/message/handler/OpenDTUHandler.java @@ -2,12 +2,13 @@ package de.ph87.data.message.handler; import com.fasterxml.jackson.databind.*; import de.ph87.data.message.*; -import de.ph87.data.meter.*; -import de.ph87.data.series.entry.*; +import de.ph87.data.series.meter.*; +import de.ph87.data.series.varying.*; import de.ph87.data.value.Value; import de.ph87.data.value.*; import lombok.*; import lombok.extern.slf4j.*; +import org.springframework.context.*; import org.springframework.stereotype.*; import java.time.*; @@ -21,18 +22,16 @@ public class OpenDTUHandler implements IMessageHandler { private final ObjectMapper objectMapper; - private final EntryService entryService; - - private final MeterService meterService; + private final ApplicationEventPublisher applicationEventPublisher; @Override - public void handle(final @NonNull Message message) throws Exception { + public void handle(@NonNull final Message message) throws Exception { if (!"openDTU/pv/patrix/json".equals(message.topic)) { return; } final Inbound inbound = objectMapper.readValue(message.payload, Inbound.class); - entryService.receive("power/produced", inbound.date, inbound.power.as(Unit.POWER_W)); - meterService.receive("energy/produced", METER_NUMBER, inbound.date, inbound.energy); + applicationEventPublisher.publishEvent(new VaryingInbound("power/produced", inbound.date, inbound.power.as(Unit.POWER_W))); + applicationEventPublisher.publishEvent(new MeterInbound("energy/produced", METER_NUMBER, inbound.date, inbound.energy)); } @Getter diff --git a/src/main/java/de/ph87/data/message/handler/SimpleJsonHandler.java b/src/main/java/de/ph87/data/message/handler/SimpleJsonHandler.java index 90393c5..f712cd5 100644 --- a/src/main/java/de/ph87/data/message/handler/SimpleJsonHandler.java +++ b/src/main/java/de/ph87/data/message/handler/SimpleJsonHandler.java @@ -2,11 +2,12 @@ package de.ph87.data.message.handler; import com.fasterxml.jackson.databind.*; import de.ph87.data.message.*; -import de.ph87.data.series.entry.*; +import de.ph87.data.series.varying.*; import de.ph87.data.value.Value; import de.ph87.data.value.*; import lombok.*; import lombok.extern.slf4j.*; +import org.springframework.context.*; import org.springframework.stereotype.*; import java.time.*; @@ -18,7 +19,7 @@ public class SimpleJsonHandler implements IMessageHandler { private final ObjectMapper objectMapper; - private final EntryService entryService; + private final ApplicationEventPublisher applicationEventPublisher; @Override public void handle(@NonNull final Message message) throws Exception { @@ -26,7 +27,7 @@ public class SimpleJsonHandler implements IMessageHandler { return; } final Inbound inbound = objectMapper.readValue(message.payload, Inbound.class); - entryService.receive(inbound.name, inbound.date, inbound.value); + applicationEventPublisher.publishEvent(new VaryingInbound(inbound.name, inbound.date, inbound.value)); } @Getter diff --git a/src/main/java/de/ph87/data/message/handler/SmartMeterHandler.java b/src/main/java/de/ph87/data/message/handler/SmartMeterHandler.java index 642a242..6406ac2 100644 --- a/src/main/java/de/ph87/data/message/handler/SmartMeterHandler.java +++ b/src/main/java/de/ph87/data/message/handler/SmartMeterHandler.java @@ -2,12 +2,13 @@ package de.ph87.data.message.handler; import com.fasterxml.jackson.databind.*; import de.ph87.data.message.*; -import de.ph87.data.meter.*; -import de.ph87.data.series.entry.*; +import de.ph87.data.series.meter.*; +import de.ph87.data.series.varying.*; import de.ph87.data.value.Value; import de.ph87.data.value.*; import lombok.*; import lombok.extern.slf4j.*; +import org.springframework.context.*; import org.springframework.stereotype.*; import java.time.*; @@ -21,9 +22,7 @@ public class SmartMeterHandler implements IMessageHandler { private final ObjectMapper objectMapper; - private final EntryService entryService; - - private final MeterService meterService; + private final ApplicationEventPublisher applicationEventPublisher; @Override public void handle(@NonNull final Message message) throws Exception { @@ -31,9 +30,9 @@ public class SmartMeterHandler implements IMessageHandler { return; } final Inbound inbound = objectMapper.readValue(message.payload, Inbound.class); - entryService.receive("power/balance", inbound.date, inbound.powerBalance.as(Unit.POWER_W)); - meterService.receive("energy/purchased", METER_NUMBER, inbound.date, inbound.energyPurchased); - meterService.receive("energy/delivered", METER_NUMBER, inbound.date, inbound.energyDelivered); + applicationEventPublisher.publishEvent(new VaryingInbound("power/balance", inbound.date, inbound.powerBalance.as(Unit.POWER_W))); + applicationEventPublisher.publishEvent(new MeterInbound("energy/purchased", METER_NUMBER, inbound.date, inbound.energyPurchased)); + applicationEventPublisher.publishEvent(new MeterInbound("energy/delivered", METER_NUMBER, inbound.date, inbound.energyDelivered)); } @Getter diff --git a/src/main/java/de/ph87/data/meter/MeterService.java b/src/main/java/de/ph87/data/meter/MeterService.java deleted file mode 100644 index 0f38185..0000000 --- a/src/main/java/de/ph87/data/meter/MeterService.java +++ /dev/null @@ -1,93 +0,0 @@ -package de.ph87.data.meter; - -import de.ph87.data.meter.day.*; -import de.ph87.data.meter.five.*; -import de.ph87.data.meter.month.*; -import de.ph87.data.meter.week.*; -import de.ph87.data.meter.year.*; -import de.ph87.data.series.*; -import de.ph87.data.value.Value; -import lombok.*; -import lombok.extern.slf4j.*; -import org.springframework.stereotype.*; -import org.springframework.transaction.annotation.*; - -import java.time.*; -import java.time.temporal.*; - -@Slf4j -@Service -@Transactional -@RequiredArgsConstructor -public class MeterService { - - private final SeriesService seriesService; - - private final MeterRepository meterRepository; - - private final MeterDayRepository meterDayRepository; - - private final MeterWeekRepository meterWeekRepository; - - private final MeterMonthRepository meterMonthRepository; - - private final MeterYearRepository meterYearRepository; - - private final MeterFiveRepository meterFiveRepository; - - public void receive(@NonNull final String seriesName, @NonNull final String meterNumber, @NonNull final ZonedDateTime date, @NonNull final Value value) { - final Series series = seriesService.getOrCreate(seriesName, value.unit); - final Meter meter = meterRepository.findFirstBySeriesOrderByDateDesc(series) - .filter(m -> m.getNumber().equals(meterNumber)) - .orElseGet(() -> meterRepository.save(new Meter(series, date, meterNumber))); - - final MeterFive.Id five = new MeterFive.Id(meter, date.truncatedTo(ChronoUnit.MINUTES).minusMinutes(date.getMinute() % 5)); - meterFiveRepository.findById(five).ifPresentOrElse( - existing -> existing.update(value), - () -> meterFiveRepository.save(new MeterFive(five, value)) - ); - - final MeterDay.Id day = new MeterDay.Id(meter, date.toLocalDate()); - meterDayRepository.findById(day).ifPresentOrElse( - existing -> existing.update(value), - () -> meterDayRepository.save(new MeterDay(day, value)) - ); - - final MeterWeek.Id week = new MeterWeek.Id(meter, toWeek(date)); - meterWeekRepository.findById(week).ifPresentOrElse( - existing -> existing.update(value), - () -> meterWeekRepository.save(new MeterWeek(week, value)) - ); - - final MeterMonth.Id month = new MeterMonth.Id(meter, toMonth(date)); - meterMonthRepository.findById(month).ifPresentOrElse( - existing -> existing.update(value), - () -> meterMonthRepository.save(new MeterMonth(month, value)) - ); - - final MeterYear.Id year = new MeterYear.Id(meter, toYear(date)); - meterYearRepository.findById(year).ifPresentOrElse( - existing -> existing.update(value), - () -> meterYearRepository.save(new MeterYear(year, value)) - ); - } - - @NonNull - private static LocalDate toWeek(@NonNull final ZonedDateTime date) { - final LocalDate t = date.toLocalDate(); - return t.minusDays(t.getDayOfWeek().getValue() - 1); - } - - @NonNull - private static LocalDate toMonth(@NonNull final ZonedDateTime date) { - final LocalDate t = date.toLocalDate(); - return t.minusDays(t.getDayOfMonth() - 1); - } - - @NonNull - private static LocalDate toYear(@NonNull final ZonedDateTime date) { - final LocalDate t = date.toLocalDate(); - return t.minusDays(t.getDayOfYear() - 1); - } - -} diff --git a/src/main/java/de/ph87/data/meter/day/MeterDay.java b/src/main/java/de/ph87/data/meter/day/MeterDay.java deleted file mode 100644 index e1a61e4..0000000 --- a/src/main/java/de/ph87/data/meter/day/MeterDay.java +++ /dev/null @@ -1,61 +0,0 @@ -package de.ph87.data.meter.day; - -import de.ph87.data.meter.*; -import de.ph87.data.value.Value; -import jakarta.persistence.*; -import lombok.*; - -import java.time.*; - -@Entity -@Getter -@ToString -@NoArgsConstructor -public class MeterDay { - - @EmbeddedId - private Id id; - - @Column(nullable = false) - private double min; - - @Column(nullable = false) - private double max; - - public MeterDay(@NonNull final Id id, @NonNull final Value value) { - this.id = id; - this.min = value.as(id.getMeter().getSeries().getUnit()).value; - this.max = this.min; - } - - public void update(@NonNull final Value value) { - final double v = value.as(id.getMeter().getSeries().getUnit()).value; - if (v < this.max) { - throw new RuntimeException(); - } - this.max = v; - } - - @Getter - @ToString - @Embeddable - @EqualsAndHashCode - @NoArgsConstructor - public static class Id { - - @NonNull - @ManyToOne - private Meter meter; - - @NonNull - @Column(nullable = false) - private LocalDate date; - - public Id(@NonNull final Meter meter, @NonNull final LocalDate date) { - this.meter = meter; - this.date = date; - } - - } - -} diff --git a/src/main/java/de/ph87/data/meter/day/MeterDayRepository.java b/src/main/java/de/ph87/data/meter/day/MeterDayRepository.java deleted file mode 100644 index 290d42c..0000000 --- a/src/main/java/de/ph87/data/meter/day/MeterDayRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package de.ph87.data.meter.day; - -import org.springframework.data.repository.*; - -public interface MeterDayRepository extends ListCrudRepository { - -} diff --git a/src/main/java/de/ph87/data/meter/five/MeterFiveRepository.java b/src/main/java/de/ph87/data/meter/five/MeterFiveRepository.java deleted file mode 100644 index f49ca96..0000000 --- a/src/main/java/de/ph87/data/meter/five/MeterFiveRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package de.ph87.data.meter.five; - -import org.springframework.data.repository.*; - -public interface MeterFiveRepository extends ListCrudRepository { - -} diff --git a/src/main/java/de/ph87/data/meter/month/MeterMonth.java b/src/main/java/de/ph87/data/meter/month/MeterMonth.java deleted file mode 100644 index 24428ac..0000000 --- a/src/main/java/de/ph87/data/meter/month/MeterMonth.java +++ /dev/null @@ -1,61 +0,0 @@ -package de.ph87.data.meter.month; - -import de.ph87.data.meter.*; -import de.ph87.data.value.Value; -import jakarta.persistence.*; -import lombok.*; - -import java.time.*; - -@Entity -@Getter -@ToString -@NoArgsConstructor -public class MeterMonth { - - @EmbeddedId - private Id id; - - @Column(nullable = false) - private double min; - - @Column(nullable = false) - private double max; - - public MeterMonth(@NonNull final Id id, @NonNull final Value value) { - this.id = id; - this.min = value.as(id.getMeter().getSeries().getUnit()).value; - this.max = this.min; - } - - public void update(@NonNull final Value value) { - final double v = value.as(id.getMeter().getSeries().getUnit()).value; - if (v < this.max) { - throw new RuntimeException(); - } - this.max = v; - } - - @Getter - @ToString - @Embeddable - @EqualsAndHashCode - @NoArgsConstructor - public static class Id { - - @NonNull - @ManyToOne - private Meter meter; - - @NonNull - @Column(nullable = false) - private LocalDate date; - - public Id(@NonNull final Meter meter, @NonNull final LocalDate date) { - this.meter = meter; - this.date = date; - } - - } - -} diff --git a/src/main/java/de/ph87/data/meter/month/MeterMonthRepository.java b/src/main/java/de/ph87/data/meter/month/MeterMonthRepository.java deleted file mode 100644 index 8f6033d..0000000 --- a/src/main/java/de/ph87/data/meter/month/MeterMonthRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package de.ph87.data.meter.month; - -import org.springframework.data.repository.*; - -public interface MeterMonthRepository extends ListCrudRepository { - -} diff --git a/src/main/java/de/ph87/data/meter/week/MeterWeek.java b/src/main/java/de/ph87/data/meter/week/MeterWeek.java deleted file mode 100644 index 7b4c4d5..0000000 --- a/src/main/java/de/ph87/data/meter/week/MeterWeek.java +++ /dev/null @@ -1,61 +0,0 @@ -package de.ph87.data.meter.week; - -import de.ph87.data.meter.*; -import de.ph87.data.value.Value; -import jakarta.persistence.*; -import lombok.*; - -import java.time.*; - -@Entity -@Getter -@ToString -@NoArgsConstructor -public class MeterWeek { - - @EmbeddedId - private Id id; - - @Column(nullable = false) - private double min; - - @Column(nullable = false) - private double max; - - public MeterWeek(@NonNull final Id id, @NonNull final Value value) { - this.id = id; - this.min = value.as(id.getMeter().getSeries().getUnit()).value; - this.max = this.min; - } - - public void update(@NonNull final Value value) { - final double v = value.as(id.getMeter().getSeries().getUnit()).value; - if (v < this.max) { - throw new RuntimeException(); - } - this.max = v; - } - - @Getter - @ToString - @Embeddable - @EqualsAndHashCode - @NoArgsConstructor - public static class Id { - - @NonNull - @ManyToOne - private Meter meter; - - @NonNull - @Column(nullable = false) - private LocalDate date; - - public Id(@NonNull final Meter meter, @NonNull final LocalDate date) { - this.meter = meter; - this.date = date; - } - - } - -} diff --git a/src/main/java/de/ph87/data/meter/week/MeterWeekRepository.java b/src/main/java/de/ph87/data/meter/week/MeterWeekRepository.java deleted file mode 100644 index 267d514..0000000 --- a/src/main/java/de/ph87/data/meter/week/MeterWeekRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package de.ph87.data.meter.week; - -import org.springframework.data.repository.*; - -public interface MeterWeekRepository extends ListCrudRepository { - -} diff --git a/src/main/java/de/ph87/data/meter/year/MeterYear.java b/src/main/java/de/ph87/data/meter/year/MeterYear.java deleted file mode 100644 index 8aaa0a7..0000000 --- a/src/main/java/de/ph87/data/meter/year/MeterYear.java +++ /dev/null @@ -1,61 +0,0 @@ -package de.ph87.data.meter.year; - -import de.ph87.data.meter.*; -import de.ph87.data.value.Value; -import jakarta.persistence.*; -import lombok.*; - -import java.time.*; - -@Entity -@Getter -@ToString -@NoArgsConstructor -public class MeterYear { - - @EmbeddedId - private Id id; - - @Column(nullable = false) - private double min; - - @Column(nullable = false) - private double max; - - public MeterYear(@NonNull final Id id, @NonNull final Value value) { - this.id = id; - this.min = value.as(id.getMeter().getSeries().getUnit()).value; - this.max = this.min; - } - - public void update(@NonNull final Value value) { - final double v = value.as(id.getMeter().getSeries().getUnit()).value; - if (v < this.max) { - throw new RuntimeException(); - } - this.max = v; - } - - @Getter - @ToString - @Embeddable - @EqualsAndHashCode - @NoArgsConstructor - public static class Id { - - @NonNull - @ManyToOne - private Meter meter; - - @NonNull - @Column(nullable = false) - private LocalDate date; - - public Id(@NonNull final Meter meter, @NonNull final LocalDate date) { - this.meter = meter; - this.date = date; - } - - } - -} diff --git a/src/main/java/de/ph87/data/meter/year/MeterYearRepository.java b/src/main/java/de/ph87/data/meter/year/MeterYearRepository.java deleted file mode 100644 index e01d074..0000000 --- a/src/main/java/de/ph87/data/meter/year/MeterYearRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package de.ph87.data.meter.year; - -import org.springframework.data.repository.*; - -public interface MeterYearRepository extends ListCrudRepository { - -} diff --git a/src/main/java/de/ph87/data/series/Aligned.java b/src/main/java/de/ph87/data/series/Aligned.java new file mode 100644 index 0000000..6af060c --- /dev/null +++ b/src/main/java/de/ph87/data/series/Aligned.java @@ -0,0 +1,26 @@ +package de.ph87.data.series; + +import lombok.*; + +import java.time.*; + +@Data +public class Aligned { + + @NonNull + public final Alignment interval; + + @NonNull + public final ZonedDateTime date; + + public Aligned(@NonNull final Alignment interval, @NonNull final ZonedDateTime date) { + this.interval = interval; + this.date = interval.align.apply(date); + } + + @NonNull + public Aligned minus(final long offset) { + return new Aligned(interval, interval.plus.apply(date, -offset)); + } + +} diff --git a/src/main/java/de/ph87/data/series/Alignment.java b/src/main/java/de/ph87/data/series/Alignment.java new file mode 100644 index 0000000..3df0d25 --- /dev/null +++ b/src/main/java/de/ph87/data/series/Alignment.java @@ -0,0 +1,34 @@ +package de.ph87.data.series; + +import lombok.*; + +import java.time.*; +import java.time.temporal.*; +import java.util.function.*; + +public enum Alignment { + FIVE(t -> t.truncatedTo(ChronoUnit.MINUTES).minusMinutes(t.getMinute() % 5), (t, a) -> t.plusMinutes(5 * a)), + HOUR(t -> t.truncatedTo(ChronoUnit.HOURS).minusMinutes(t.getMinute() % 5), ZonedDateTime::plusHours), + DAY(t -> t.truncatedTo(ChronoUnit.DAYS), ZonedDateTime::plusDays), + WEEK(t -> t.truncatedTo(ChronoUnit.DAYS).minusWeeks(t.getDayOfWeek().getValue() - 1), ZonedDateTime::plusWeeks), + MONTH(t -> t.truncatedTo(ChronoUnit.DAYS).minusMonths(t.getDayOfMonth() - 1), ZonedDateTime::plusMonths), + YEAR(t -> t.truncatedTo(ChronoUnit.DAYS).minusYears(t.getDayOfYear() - 1), ZonedDateTime::plusYears), + ; + + @NonNull + public final Function align; + + @NonNull + public final BiFunction plus; + + Alignment(@NonNull final Function align, @NonNull final BiFunction plus) { + this.align = align; + this.plus = plus; + } + + @NonNull + public Aligned align(@NonNull final ZonedDateTime date) { + return new Aligned(this, date); + } + +} diff --git a/src/main/java/de/ph87/data/series/Series.java b/src/main/java/de/ph87/data/series/Series.java index 9b3bdf5..c84a0f7 100644 --- a/src/main/java/de/ph87/data/series/Series.java +++ b/src/main/java/de/ph87/data/series/Series.java @@ -33,10 +33,15 @@ public class Series { @Column(nullable = false) private int decimals = 1; - public Series(@NonNull final String name, @NonNull final Unit unit) { + @Column(nullable = false) + @Enumerated(EnumType.STRING) + private SeriesType type; + + public Series(@NonNull final String name, @NonNull final Unit unit, @NonNull final SeriesType type) { this.name = name; this.title = name; this.unit = unit; + this.type = type; } } diff --git a/src/main/java/de/ph87/data/series/SeriesDto.java b/src/main/java/de/ph87/data/series/SeriesDto.java index 6838c60..dc44f50 100644 --- a/src/main/java/de/ph87/data/series/SeriesDto.java +++ b/src/main/java/de/ph87/data/series/SeriesDto.java @@ -18,12 +18,15 @@ public class SeriesDto { public final int decimals; + public final SeriesType type; + public SeriesDto(@NonNull final Series series) { this.id = series.getId(); this.name = series.getName(); this.title = series.getTitle(); this.unit = series.getUnit(); this.decimals = series.getDecimals(); + this.type = series.getType(); } public String format(@Nullable final Double value) { diff --git a/src/main/java/de/ph87/data/series/SeriesService.java b/src/main/java/de/ph87/data/series/SeriesService.java index 473406f..2c607c4 100644 --- a/src/main/java/de/ph87/data/series/SeriesService.java +++ b/src/main/java/de/ph87/data/series/SeriesService.java @@ -49,14 +49,18 @@ public class SeriesService { return new SeriesDto(series); } - public Series getOrCreate(@NonNull final String name, @NonNull final Unit unit) { - return seriesRepository + public Series getOrCreate(@NonNull final String name, @NonNull final Unit unit, @NonNull final SeriesType type) { + final Series series = seriesRepository .findByName(name) .orElseGet(() -> { - final Series series = seriesRepository.save(new Series(name, unit)); - publish(series, Action.CREATED); - return series; + final Series fresh = seriesRepository.save(new Series(name, unit, type)); + publish(fresh, Action.CREATED); + return fresh; }); + if (series.getType() != type) { + log.warn("Existing Series type does not match requested type: requested={}, series={}", type, series); + } + return series; } } diff --git a/src/main/java/de/ph87/data/series/SeriesType.java b/src/main/java/de/ph87/data/series/SeriesType.java new file mode 100644 index 0000000..760dd4f --- /dev/null +++ b/src/main/java/de/ph87/data/series/SeriesType.java @@ -0,0 +1,5 @@ +package de.ph87.data.series; + +public enum SeriesType { + VARYING, METER, +} diff --git a/src/main/java/de/ph87/data/series/entry/Entry.java b/src/main/java/de/ph87/data/series/entry/Entry.java deleted file mode 100644 index 71ec48b..0000000 --- a/src/main/java/de/ph87/data/series/entry/Entry.java +++ /dev/null @@ -1,50 +0,0 @@ -package de.ph87.data.series.entry; - -import de.ph87.data.series.*; -import de.ph87.data.value.Value; -import jakarta.persistence.*; -import lombok.*; - -import java.time.*; -import java.time.temporal.*; - -@Entity -@Getter -@ToString -@NoArgsConstructor -public class Entry { - - @EmbeddedId - private EntryId id; - - @Column(nullable = false, name = "`value`") - private double value; - - public Entry(@NonNull final EntryId id, @NonNull final Value value) { - this.id = id; - this.value = value.as(id.getSeries().getUnit()).value; - } - - @Getter - @ToString - @Embeddable - @EqualsAndHashCode - @NoArgsConstructor - public static class EntryId { - - @NonNull - @ManyToOne - private Series series; - - @NonNull - @Column(nullable = false) - private ZonedDateTime date; - - public EntryId(@NonNull final Series series, final @NonNull ZonedDateTime date) { - this.series = series; - this.date = date.truncatedTo(ChronoUnit.MINUTES).minusMinutes(date.getMinute() % 5); - } - - } - -} diff --git a/src/main/java/de/ph87/data/series/entry/EntryRepository.java b/src/main/java/de/ph87/data/series/entry/EntryRepository.java deleted file mode 100644 index 96a6a94..0000000 --- a/src/main/java/de/ph87/data/series/entry/EntryRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.ph87.data.series.entry; - -import lombok.*; -import org.springframework.data.repository.*; - -import java.time.*; -import java.util.*; - -public interface EntryRepository extends ListCrudRepository { - - Optional findById(@NonNull Entry.EntryId id); - - List findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(long id, @NonNull ZonedDateTime begin, @NonNull ZonedDateTime end); - -} diff --git a/src/main/java/de/ph87/data/series/entry/EntryService.java b/src/main/java/de/ph87/data/series/entry/EntryService.java deleted file mode 100644 index 325e69a..0000000 --- a/src/main/java/de/ph87/data/series/entry/EntryService.java +++ /dev/null @@ -1,35 +0,0 @@ -package de.ph87.data.series.entry; - -import de.ph87.data.series.*; -import de.ph87.data.value.Value; -import lombok.*; -import lombok.extern.slf4j.*; -import org.springframework.stereotype.*; -import org.springframework.transaction.annotation.*; - -import java.time.*; - -@Slf4j -@Service -@Transactional -@RequiredArgsConstructor -public class EntryService { - - private final EntryRepository entryRepository; - - private final SeriesService seriesService; - - public void receive(final String name, final @NonNull ZonedDateTime date, final Value value) { - final Series series = seriesService.getOrCreate(name, value.unit); - write(series, date, value); - } - - public void write(@NonNull final Series series, final @NonNull ZonedDateTime date, final Value value) { - final Entry.EntryId id = new Entry.EntryId(series, date); - if (entryRepository.findById(id).isEmpty()) { - final Entry created = entryRepository.save(new Entry(id, value)); - log.debug("Created: {}", created); - } - } - -} diff --git a/src/main/java/de/ph87/data/series/graph/Graph.java b/src/main/java/de/ph87/data/series/graph/Graph.java index c8f09fd..2c4cf2d 100644 --- a/src/main/java/de/ph87/data/series/graph/Graph.java +++ b/src/main/java/de/ph87/data/series/graph/Graph.java @@ -1,14 +1,12 @@ package de.ph87.data.series.graph; import de.ph87.data.series.*; -import de.ph87.data.series.entry.*; import jakarta.annotation.*; import lombok.*; import java.awt.*; import java.awt.geom.*; import java.awt.image.*; -import java.time.*; import java.util.List; import java.util.function.*; @@ -24,10 +22,10 @@ public class Graph { public final SeriesDto series; @NonNull - public final ZonedDateTime begin; + public final Aligned begin; @NonNull - public final ZonedDateTime end; + public final Aligned end; public final int width; @@ -61,7 +59,7 @@ public class Graph { public final int maxLabelWidth; - public Graph(@NonNull final SeriesDto series, @NonNull final List entries, final @NonNull ZonedDateTime begin, final @NonNull ZonedDateTime end, final int width, final int height, final int border) { + public Graph(@NonNull final SeriesDto series, @NonNull final List points, final @NonNull Aligned begin, final @NonNull Aligned end, final int width, final int height, final int border) { this.series = series; this.begin = begin; this.end = end; @@ -74,20 +72,20 @@ public class Graph { double vMax = Double.MIN_VALUE; int maxLabelWidth = 80; final FontMetrics fontMetrics = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).getGraphics().getFontMetrics(); - for (final Entry entry : entries) { - vMin = Math.min(vMin, entry.getValue()); - vMax = max(vMax, entry.getValue()); - vSum += entry.getValue(); - maxLabelWidth = max(maxLabelWidth, fontMetrics.stringWidth(series.format(entry.getValue()))); + for (final GraphPoint point : points) { + vMin = Math.min(vMin, point.getValue()); + vMax = max(vMax, point.getValue()); + vSum += point.getValue(); + maxLabelWidth = max(maxLabelWidth, fontMetrics.stringWidth(series.format(point.getValue()))); } - this.valueAvg = vSum / entries.size(); + this.valueAvg = vSum / points.size(); this.maxLabelWidth = maxLabelWidth; widthInner = width - 3 * border - maxLabelWidth; heightInner = height - 2 * border; - minuteMin = begin.toEpochSecond() / 60; - minuteMax = end.toEpochSecond() / 60; + minuteMin = begin.date.toEpochSecond() / 60; + minuteMax = end.date.toEpochSecond() / 60; minuteRange = minuteMax - minuteMin; minuteScale = (double) widthInner / minuteRange; @@ -96,7 +94,7 @@ public class Graph { valueRange = vMax - vMin; valueScale = heightInner / valueRange; - points = entries.stream().map(toPoint(minuteMin, minuteScale, vMin, valueScale)).toList(); + this.points = points.stream().map(toPoint(minuteMin, minuteScale, vMin, valueScale)).toList(); } public BufferedImage draw() { @@ -131,7 +129,7 @@ public class Graph { return image; } - private void yLabel(final Graphics2D g, final double value, @Nullable final Stroke stroke, final @Nullable Color color) { + private void yLabel(final Graphics2D g, final double value, @Nullable final Stroke stroke, @Nullable final Color color) { final String string = series.format(value); final int offset = maxLabelWidth - g.getFontMetrics().stringWidth(string); final int y = height - ((int) Math.round((value - valueMin) * valueScale) + border); @@ -143,14 +141,14 @@ public class Graph { } } - private Function toPoint(final long minuteMin, final double minuteScale, final double valueMin, final double valueScale) { - return entry -> { - final long minuteEpoch = entry.getId().getDate().toEpochSecond() / 60; + private Function toPoint(final long minuteMin, final double minuteScale, final double valueMin, final double valueScale) { + return point -> { + final long minuteEpoch = point.getDate().toEpochSecond() / 60; final long minuteRelative = minuteEpoch - minuteMin; final double minuteScaled = minuteRelative * minuteScale; final int x = (int) Math.round(minuteScaled); - final double valueRelative = entry.getValue() - valueMin; + final double valueRelative = point.getValue() - valueMin; final double valueScaled = valueRelative * valueScale; final int y = (int) Math.round(valueScaled); diff --git a/src/main/java/de/ph87/data/series/graph/GraphController.java b/src/main/java/de/ph87/data/series/graph/GraphController.java index 6faf57d..427e5ac 100644 --- a/src/main/java/de/ph87/data/series/graph/GraphController.java +++ b/src/main/java/de/ph87/data/series/graph/GraphController.java @@ -1,5 +1,6 @@ package de.ph87.data.series.graph; +import de.ph87.data.series.*; import jakarta.servlet.http.*; import lombok.*; import lombok.extern.slf4j.*; @@ -18,10 +19,11 @@ public class GraphController { private final GraphService graphService; - @GetMapping(path = "{seriesId}/{width}/{height}/{offset}/{duration}", produces = "image/png") - public void graph(@PathVariable final long seriesId, final HttpServletResponse response, @PathVariable final int width, @PathVariable final int height, @PathVariable final String offset, @PathVariable final String duration) throws IOException { - final ZonedDateTime end = ZonedDateTime.now().minus(Duration.parse(offset)); - final ZonedDateTime begin = end.minus(Duration.parse(duration)); + @GetMapping(path = "{seriesId}/{width}/{height}/{intervalName}/{offset}/{duration}", produces = "image/png") + public void graph(@PathVariable final long seriesId, final HttpServletResponse response, @PathVariable final int width, @PathVariable final int height, @PathVariable final String intervalName, @PathVariable final long offset, @PathVariable final long duration) throws IOException { + final Alignment interval = Alignment.valueOf(intervalName); + final Aligned end = interval.align(ZonedDateTime.now()).minus(offset); + final Aligned begin = end.minus(duration); final Graph graph = graphService.getGraph(seriesId, begin, end, width, height, 10); final BufferedImage image = graph.draw(); response.setContentType("image/png"); diff --git a/src/main/java/de/ph87/data/series/graph/GraphPoint.java b/src/main/java/de/ph87/data/series/graph/GraphPoint.java new file mode 100644 index 0000000..1ea041f --- /dev/null +++ b/src/main/java/de/ph87/data/series/graph/GraphPoint.java @@ -0,0 +1,14 @@ +package de.ph87.data.series.graph; + +import lombok.*; + +import java.time.*; + +@Data +public class GraphPoint { + + public final ZonedDateTime date; + + public final double value; + +} diff --git a/src/main/java/de/ph87/data/series/graph/GraphService.java b/src/main/java/de/ph87/data/series/graph/GraphService.java index 53a5871..fe1b727 100644 --- a/src/main/java/de/ph87/data/series/graph/GraphService.java +++ b/src/main/java/de/ph87/data/series/graph/GraphService.java @@ -1,28 +1,32 @@ package de.ph87.data.series.graph; import de.ph87.data.series.*; -import de.ph87.data.series.entry.*; +import de.ph87.data.series.meter.*; +import de.ph87.data.series.varying.*; import lombok.*; import lombok.extern.slf4j.*; import org.springframework.stereotype.*; -import org.springframework.transaction.annotation.*; -import java.time.*; import java.util.*; @Slf4j @Service -@Transactional @RequiredArgsConstructor public class GraphService { - private final EntryRepository entryRepository; - private final SeriesService seriesService; - public Graph getGraph(final long seriesId, @NonNull final ZonedDateTime begin, @NonNull final ZonedDateTime end, final int width, final int height, final int border) { + private final VaryingService varyingService; + + private final MeterService meterService; + + @NonNull + public Graph getGraph(final long seriesId, @NonNull final Aligned begin, @NonNull final Aligned end, final int width, final int height, final int border) { final SeriesDto series = seriesService.getDtoById(seriesId); - final List entries = entryRepository.findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(seriesId, begin, end); + final List entries = switch (series.getType()) { + case METER -> meterService.getPoints(series, begin, end); + case VARYING -> varyingService.getPoints(series, begin, end); + }; return new Graph(series, entries, begin, end, width, height, border); } diff --git a/src/main/java/de/ph87/data/meter/Meter.java b/src/main/java/de/ph87/data/series/meter/Meter.java similarity index 95% rename from src/main/java/de/ph87/data/meter/Meter.java rename to src/main/java/de/ph87/data/series/meter/Meter.java index 41b67a0..c789903 100644 --- a/src/main/java/de/ph87/data/meter/Meter.java +++ b/src/main/java/de/ph87/data/series/meter/Meter.java @@ -1,4 +1,4 @@ -package de.ph87.data.meter; +package de.ph87.data.series.meter; import de.ph87.data.series.*; import jakarta.persistence.*; diff --git a/src/main/java/de/ph87/data/series/meter/MeterInbound.java b/src/main/java/de/ph87/data/series/meter/MeterInbound.java new file mode 100644 index 0000000..99d9c53 --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/MeterInbound.java @@ -0,0 +1,30 @@ +package de.ph87.data.series.meter; + +import de.ph87.data.value.Value; +import lombok.*; + +import java.time.*; + +@Getter +@ToString +public class MeterInbound { + + @NonNull + public final String seriesName; + + @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; + this.meterNumber = meterNumber; + this.date = date; + this.value = value; + } + +} diff --git a/src/main/java/de/ph87/data/meter/MeterRepository.java b/src/main/java/de/ph87/data/series/meter/MeterRepository.java similarity index 87% rename from src/main/java/de/ph87/data/meter/MeterRepository.java rename to src/main/java/de/ph87/data/series/meter/MeterRepository.java index 28f56c4..a4c8aff 100644 --- a/src/main/java/de/ph87/data/meter/MeterRepository.java +++ b/src/main/java/de/ph87/data/series/meter/MeterRepository.java @@ -1,4 +1,4 @@ -package de.ph87.data.meter; +package de.ph87.data.series.meter; import de.ph87.data.series.*; import org.springframework.data.repository.*; diff --git a/src/main/java/de/ph87/data/series/meter/MeterService.java b/src/main/java/de/ph87/data/series/meter/MeterService.java new file mode 100644 index 0000000..baeba21 --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/MeterService.java @@ -0,0 +1,85 @@ +package de.ph87.data.series.meter; + +import de.ph87.data.series.*; +import de.ph87.data.series.graph.*; +import de.ph87.data.series.meter.day.*; +import de.ph87.data.series.meter.five.*; +import de.ph87.data.series.meter.hour.*; +import de.ph87.data.series.meter.month.*; +import de.ph87.data.series.meter.week.*; +import de.ph87.data.series.meter.year.*; +import lombok.*; +import lombok.extern.slf4j.*; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.*; +import org.springframework.transaction.annotation.*; + +import java.util.*; + +@Slf4j +@Service +@Transactional +@RequiredArgsConstructor +public class MeterService { + + private final SeriesService seriesService; + + private final MeterRepository meterRepository; + + private final MeterFiveRepository meterFiveRepository; + + private final MeterHourRepository meterHourRepository; + + private final MeterDayRepository meterDayRepository; + + private final MeterWeekRepository meterWeekRepository; + + private final MeterMonthRepository meterMonthRepository; + + private final MeterYearRepository meterYearRepository; + + @EventListener(MeterInbound.class) + public void onEvent(@NonNull final MeterInbound event) { + final Meter meter = getOrCreate(event); + + 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 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 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 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 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 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))); + } + + @NonNull + private Meter getOrCreate(@NonNull final MeterInbound event) { + final Series series = seriesService.getOrCreate(event.seriesName, event.value.unit, SeriesType.METER); + return meterRepository.findFirstBySeriesOrderByDateDesc(series) + .filter(m -> m.getNumber().equals(event.meterNumber)) + .orElseGet(() -> meterRepository.save(new Meter(series, event.date, event.meterNumber))); + } + + @NonNull + public List getPoints(@NonNull final SeriesDto series, @NonNull final Aligned begin, @NonNull final Aligned end) { + final List graphPoints = switch (begin.interval) { + case FIVE -> meterFiveRepository.findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); + case HOUR -> meterHourRepository.findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); + case DAY -> meterDayRepository.findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); + case WEEK -> meterWeekRepository.findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); + case MONTH -> meterMonthRepository.findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); + case YEAR -> meterYearRepository.findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); + }; + return graphPoints.stream().map(v -> new GraphPoint(v.getId().getDate(), v.getMax() - v.getMin())).toList(); + } + +} diff --git a/src/main/java/de/ph87/data/meter/five/MeterFive.java b/src/main/java/de/ph87/data/series/meter/MeterValue.java similarity index 55% rename from src/main/java/de/ph87/data/meter/five/MeterFive.java rename to src/main/java/de/ph87/data/series/meter/MeterValue.java index 5a97719..d16f41c 100644 --- a/src/main/java/de/ph87/data/meter/five/MeterFive.java +++ b/src/main/java/de/ph87/data/series/meter/MeterValue.java @@ -1,17 +1,17 @@ -package de.ph87.data.meter.five; +package de.ph87.data.series.meter; -import de.ph87.data.meter.*; +import de.ph87.data.series.*; import de.ph87.data.value.Value; import jakarta.persistence.*; import lombok.*; import java.time.*; -@Entity @Getter @ToString +@MappedSuperclass @NoArgsConstructor -public class MeterFive { +public abstract class MeterValue { @EmbeddedId private Id id; @@ -22,24 +22,23 @@ public class MeterFive { @Column(nullable = false) private double max; - public MeterFive(@NonNull final Id id, @NonNull final Value value) { + public MeterValue(@NonNull final Id id, @NonNull final Value value) { this.id = id; - this.min = value.as(id.getMeter().getSeries().getUnit()).value; - this.max = this.min; + final double converted = value.as(id.getMeter().getSeries().getUnit()).value; + this.min = converted; + this.max = converted; } public void update(@NonNull final Value value) { - final double v = value.as(id.getMeter().getSeries().getUnit()).value; - if (v < this.max) { + final double converted = value.as(id.getMeter().getSeries().getUnit()).value; + if (converted < this.max) { throw new RuntimeException(); } - this.max = v; + this.max = converted; } - @Getter - @ToString + @Data @Embeddable - @EqualsAndHashCode @NoArgsConstructor public static class Id { @@ -51,9 +50,9 @@ public class MeterFive { @Column(nullable = false) private ZonedDateTime date; - public Id(@NonNull final Meter meter, @NonNull final ZonedDateTime date) { + public Id(@NonNull final Meter meter, @NonNull final ZonedDateTime date, @NonNull final Alignment interval) { this.meter = meter; - this.date = date; + this.date = interval.align(date).date; } } diff --git a/src/main/java/de/ph87/data/series/meter/MeterValueRepository.java b/src/main/java/de/ph87/data/series/meter/MeterValueRepository.java new file mode 100644 index 0000000..b665bf4 --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/MeterValueRepository.java @@ -0,0 +1,14 @@ +package de.ph87.data.series.meter; + +import lombok.*; +import org.springframework.data.repository.*; + +import java.time.*; +import java.util.*; + +@NoRepositoryBean +public interface MeterValueRepository extends ListCrudRepository { + + List findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(long id, @NonNull ZonedDateTime begin, @NonNull ZonedDateTime end); + +} diff --git a/src/main/java/de/ph87/data/series/meter/day/MeterDay.java b/src/main/java/de/ph87/data/series/meter/day/MeterDay.java new file mode 100644 index 0000000..f109e72 --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/day/MeterDay.java @@ -0,0 +1,18 @@ +package de.ph87.data.series.meter.day; + +import de.ph87.data.series.meter.*; +import de.ph87.data.value.Value; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class MeterDay extends MeterValue { + + public MeterDay(@NonNull final Id id, @NonNull final Value value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/data/series/meter/day/MeterDayRepository.java b/src/main/java/de/ph87/data/series/meter/day/MeterDayRepository.java new file mode 100644 index 0000000..8ced681 --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/day/MeterDayRepository.java @@ -0,0 +1,7 @@ +package de.ph87.data.series.meter.day; + +import de.ph87.data.series.meter.*; + +public interface MeterDayRepository extends MeterValueRepository { + +} diff --git a/src/main/java/de/ph87/data/series/meter/five/MeterFive.java b/src/main/java/de/ph87/data/series/meter/five/MeterFive.java new file mode 100644 index 0000000..210a34e --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/five/MeterFive.java @@ -0,0 +1,18 @@ +package de.ph87.data.series.meter.five; + +import de.ph87.data.series.meter.*; +import de.ph87.data.value.Value; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class MeterFive extends MeterValue { + + public MeterFive(@NonNull final Id id, @NonNull final Value value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/data/series/meter/five/MeterFiveRepository.java b/src/main/java/de/ph87/data/series/meter/five/MeterFiveRepository.java new file mode 100644 index 0000000..f0e4071 --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/five/MeterFiveRepository.java @@ -0,0 +1,7 @@ +package de.ph87.data.series.meter.five; + +import de.ph87.data.series.meter.*; + +public interface MeterFiveRepository extends MeterValueRepository { + +} diff --git a/src/main/java/de/ph87/data/series/meter/hour/MeterHour.java b/src/main/java/de/ph87/data/series/meter/hour/MeterHour.java new file mode 100644 index 0000000..0c7ac30 --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/hour/MeterHour.java @@ -0,0 +1,18 @@ +package de.ph87.data.series.meter.hour; + +import de.ph87.data.series.meter.*; +import de.ph87.data.value.Value; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class MeterHour extends MeterValue { + + public MeterHour(@NonNull final Id id, @NonNull final Value value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/data/series/meter/hour/MeterHourRepository.java b/src/main/java/de/ph87/data/series/meter/hour/MeterHourRepository.java new file mode 100644 index 0000000..21b4b4d --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/hour/MeterHourRepository.java @@ -0,0 +1,7 @@ +package de.ph87.data.series.meter.hour; + +import de.ph87.data.series.meter.*; + +public interface MeterHourRepository extends MeterValueRepository { + +} diff --git a/src/main/java/de/ph87/data/series/meter/month/MeterMonth.java b/src/main/java/de/ph87/data/series/meter/month/MeterMonth.java new file mode 100644 index 0000000..6a53389 --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/month/MeterMonth.java @@ -0,0 +1,18 @@ +package de.ph87.data.series.meter.month; + +import de.ph87.data.series.meter.*; +import de.ph87.data.value.Value; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class MeterMonth extends MeterValue { + + public MeterMonth(@NonNull final Id id, @NonNull final Value value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/data/series/meter/month/MeterMonthRepository.java b/src/main/java/de/ph87/data/series/meter/month/MeterMonthRepository.java new file mode 100644 index 0000000..12b18ba --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/month/MeterMonthRepository.java @@ -0,0 +1,7 @@ +package de.ph87.data.series.meter.month; + +import de.ph87.data.series.meter.*; + +public interface MeterMonthRepository extends MeterValueRepository { + +} diff --git a/src/main/java/de/ph87/data/series/meter/week/MeterWeek.java b/src/main/java/de/ph87/data/series/meter/week/MeterWeek.java new file mode 100644 index 0000000..a0754fe --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/week/MeterWeek.java @@ -0,0 +1,18 @@ +package de.ph87.data.series.meter.week; + +import de.ph87.data.series.meter.*; +import de.ph87.data.value.Value; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class MeterWeek extends MeterValue { + + public MeterWeek(@NonNull final Id id, @NonNull final Value value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/data/series/meter/week/MeterWeekRepository.java b/src/main/java/de/ph87/data/series/meter/week/MeterWeekRepository.java new file mode 100644 index 0000000..af6eb06 --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/week/MeterWeekRepository.java @@ -0,0 +1,7 @@ +package de.ph87.data.series.meter.week; + +import de.ph87.data.series.meter.*; + +public interface MeterWeekRepository extends MeterValueRepository { + +} diff --git a/src/main/java/de/ph87/data/series/meter/year/MeterYear.java b/src/main/java/de/ph87/data/series/meter/year/MeterYear.java new file mode 100644 index 0000000..6d32099 --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/year/MeterYear.java @@ -0,0 +1,18 @@ +package de.ph87.data.series.meter.year; + +import de.ph87.data.series.meter.*; +import de.ph87.data.value.Value; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class MeterYear extends MeterValue { + + public MeterYear(@NonNull final Id id, @NonNull final Value value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/data/series/meter/year/MeterYearRepository.java b/src/main/java/de/ph87/data/series/meter/year/MeterYearRepository.java new file mode 100644 index 0000000..0e42f97 --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/year/MeterYearRepository.java @@ -0,0 +1,7 @@ +package de.ph87.data.series.meter.year; + +import de.ph87.data.series.meter.*; + +public interface MeterYearRepository extends MeterValueRepository { + +} diff --git a/src/main/java/de/ph87/data/series/varying/Varying.java b/src/main/java/de/ph87/data/series/varying/Varying.java new file mode 100644 index 0000000..95c1f02 --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/Varying.java @@ -0,0 +1,67 @@ +package de.ph87.data.series.varying; + +import de.ph87.data.series.*; +import de.ph87.data.value.Value; +import jakarta.persistence.*; +import lombok.*; + +import java.time.*; + +@Getter +@ToString +@MappedSuperclass +@NoArgsConstructor +public abstract class Varying { + + @EmbeddedId + private Id id; + + @Column(nullable = false) + private double min; + + @Column(nullable = false) + private double max; + + @Column(nullable = false) + private double avg; + + @Column(nullable = false) + private int count; + + protected Varying(@NonNull final Id id, @NonNull final Value value) { + this.id = id; + final double converted = value.as(id.getSeries().getUnit()).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(id.getSeries().getUnit()).value; + this.min = Math.min(this.min, converted); + this.max = Math.max(this.max, converted); + this.avg = (this.avg * this.count + converted) / ++this.count; + } + + @Data + @Embeddable + @NoArgsConstructor + public static class Id { + + @NonNull + @ManyToOne + private Series series; + + @NonNull + @Column(nullable = false) + private ZonedDateTime date; + + public Id(@NonNull final Series series, @NonNull final ZonedDateTime date, @NonNull final Alignment interval) { + this.series = series; + this.date = interval.align(date).date; + } + + } + +} diff --git a/src/main/java/de/ph87/data/series/SeriesInbound.java b/src/main/java/de/ph87/data/series/varying/VaryingInbound.java similarity index 61% rename from src/main/java/de/ph87/data/series/SeriesInbound.java rename to src/main/java/de/ph87/data/series/varying/VaryingInbound.java index 6932929..d0d1e12 100644 --- a/src/main/java/de/ph87/data/series/SeriesInbound.java +++ b/src/main/java/de/ph87/data/series/varying/VaryingInbound.java @@ -1,4 +1,4 @@ -package de.ph87.data.series; +package de.ph87.data.series.varying; import de.ph87.data.value.Value; import lombok.*; @@ -7,7 +7,7 @@ import java.time.*; @Getter @ToString -public class SeriesInbound { +public class VaryingInbound { @NonNull public final String name; @@ -17,7 +17,7 @@ public class SeriesInbound { public final Value value; - public SeriesInbound(@NonNull final String name, @NonNull final ZonedDateTime date, 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; diff --git a/src/main/java/de/ph87/data/series/varying/VaryingRepository.java b/src/main/java/de/ph87/data/series/varying/VaryingRepository.java new file mode 100644 index 0000000..aa0c9b7 --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/VaryingRepository.java @@ -0,0 +1,14 @@ +package de.ph87.data.series.varying; + +import lombok.*; +import org.springframework.data.repository.*; + +import java.time.*; +import java.util.*; + +@NoRepositoryBean +public interface VaryingRepository extends ListCrudRepository { + + List findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(long id, @NonNull ZonedDateTime begin, @NonNull ZonedDateTime end); + +} diff --git a/src/main/java/de/ph87/data/series/varying/VaryingService.java b/src/main/java/de/ph87/data/series/varying/VaryingService.java new file mode 100644 index 0000000..0fd99e1 --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/VaryingService.java @@ -0,0 +1,75 @@ +package de.ph87.data.series.varying; + +import de.ph87.data.series.*; +import de.ph87.data.series.graph.*; +import de.ph87.data.series.varying.day.*; +import de.ph87.data.series.varying.five.*; +import de.ph87.data.series.varying.hour.*; +import de.ph87.data.series.varying.month.*; +import de.ph87.data.series.varying.week.*; +import de.ph87.data.series.varying.year.*; +import lombok.*; +import lombok.extern.slf4j.*; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.*; +import org.springframework.transaction.annotation.*; + +import java.util.*; + +@Slf4j +@Service +@Transactional +@RequiredArgsConstructor +public class VaryingService { + + private final SeriesService seriesService; + + private final VaryingFiveRepository varyingFiveRepository; + + private final VaryingDayRepository varyingDayRepository; + + private final VaryingWeekRepository varyingWeekRepository; + + private final VaryingMonthRepository varyingMonthRepository; + + 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); + + 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 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 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 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 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 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))); + } + + @NonNull + public List getPoints(@NonNull final SeriesDto series, @NonNull final Aligned begin, @NonNull final Aligned end) { + final List graphPoints = switch (begin.interval) { + case FIVE -> varyingFiveRepository.findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); + case HOUR -> varyingHourRepository.findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); + case DAY -> varyingDayRepository.findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); + case WEEK -> varyingWeekRepository.findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); + case MONTH -> varyingMonthRepository.findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); + case YEAR -> varyingYearRepository.findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); + }; + return graphPoints.stream().map(v -> new GraphPoint(v.getId().getDate(), v.getMax() - v.getMin())).toList(); + } + +} diff --git a/src/main/java/de/ph87/data/series/varying/day/VaryingDay.java b/src/main/java/de/ph87/data/series/varying/day/VaryingDay.java new file mode 100644 index 0000000..0f714b0 --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/day/VaryingDay.java @@ -0,0 +1,18 @@ +package de.ph87.data.series.varying.day; + +import de.ph87.data.series.varying.*; +import de.ph87.data.value.Value; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class VaryingDay extends Varying { + + public VaryingDay(@NonNull final Id id, @NonNull final Value value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/data/series/varying/day/VaryingDayRepository.java b/src/main/java/de/ph87/data/series/varying/day/VaryingDayRepository.java new file mode 100644 index 0000000..ccbf926 --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/day/VaryingDayRepository.java @@ -0,0 +1,7 @@ +package de.ph87.data.series.varying.day; + +import de.ph87.data.series.varying.*; + +public interface VaryingDayRepository extends VaryingRepository { + +} diff --git a/src/main/java/de/ph87/data/series/varying/five/VaryingFive.java b/src/main/java/de/ph87/data/series/varying/five/VaryingFive.java new file mode 100644 index 0000000..58edcd4 --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/five/VaryingFive.java @@ -0,0 +1,18 @@ +package de.ph87.data.series.varying.five; + +import de.ph87.data.series.varying.*; +import de.ph87.data.value.Value; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class VaryingFive extends Varying { + + public VaryingFive(@NonNull final Varying.Id id, @NonNull final Value value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/data/series/varying/five/VaryingFiveRepository.java b/src/main/java/de/ph87/data/series/varying/five/VaryingFiveRepository.java new file mode 100644 index 0000000..37d3e09 --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/five/VaryingFiveRepository.java @@ -0,0 +1,7 @@ +package de.ph87.data.series.varying.five; + +import de.ph87.data.series.varying.*; + +public interface VaryingFiveRepository extends VaryingRepository { + +} diff --git a/src/main/java/de/ph87/data/series/varying/hour/VaryingHour.java b/src/main/java/de/ph87/data/series/varying/hour/VaryingHour.java new file mode 100644 index 0000000..89dfb00 --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/hour/VaryingHour.java @@ -0,0 +1,18 @@ +package de.ph87.data.series.varying.hour; + +import de.ph87.data.series.varying.*; +import de.ph87.data.value.Value; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class VaryingHour extends Varying { + + public VaryingHour(@NonNull final Id id, @NonNull final Value value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/data/series/varying/hour/VaryingHourRepository.java b/src/main/java/de/ph87/data/series/varying/hour/VaryingHourRepository.java new file mode 100644 index 0000000..3cb94b3 --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/hour/VaryingHourRepository.java @@ -0,0 +1,7 @@ +package de.ph87.data.series.varying.hour; + +import de.ph87.data.series.varying.*; + +public interface VaryingHourRepository extends VaryingRepository { + +} diff --git a/src/main/java/de/ph87/data/series/varying/month/VaryingMonth.java b/src/main/java/de/ph87/data/series/varying/month/VaryingMonth.java new file mode 100644 index 0000000..facd26b --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/month/VaryingMonth.java @@ -0,0 +1,18 @@ +package de.ph87.data.series.varying.month; + +import de.ph87.data.series.varying.*; +import de.ph87.data.value.Value; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class VaryingMonth extends Varying { + + public VaryingMonth(@NonNull final Varying.Id id, @NonNull final Value value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/data/series/varying/month/VaryingMonthRepository.java b/src/main/java/de/ph87/data/series/varying/month/VaryingMonthRepository.java new file mode 100644 index 0000000..9db9927 --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/month/VaryingMonthRepository.java @@ -0,0 +1,7 @@ +package de.ph87.data.series.varying.month; + +import de.ph87.data.series.varying.*; + +public interface VaryingMonthRepository extends VaryingRepository { + +} diff --git a/src/main/java/de/ph87/data/series/varying/week/VaryingWeek.java b/src/main/java/de/ph87/data/series/varying/week/VaryingWeek.java new file mode 100644 index 0000000..749c508 --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/week/VaryingWeek.java @@ -0,0 +1,18 @@ +package de.ph87.data.series.varying.week; + +import de.ph87.data.series.varying.*; +import de.ph87.data.value.Value; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class VaryingWeek extends Varying { + + public VaryingWeek(@NonNull final Id id, @NonNull final Value value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/data/series/varying/week/VaryingWeekRepository.java b/src/main/java/de/ph87/data/series/varying/week/VaryingWeekRepository.java new file mode 100644 index 0000000..581636b --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/week/VaryingWeekRepository.java @@ -0,0 +1,7 @@ +package de.ph87.data.series.varying.week; + +import de.ph87.data.series.varying.*; + +public interface VaryingWeekRepository extends VaryingRepository { + +} diff --git a/src/main/java/de/ph87/data/series/varying/year/VaryingYear.java b/src/main/java/de/ph87/data/series/varying/year/VaryingYear.java new file mode 100644 index 0000000..fff31fb --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/year/VaryingYear.java @@ -0,0 +1,18 @@ +package de.ph87.data.series.varying.year; + +import de.ph87.data.series.varying.*; +import de.ph87.data.value.Value; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class VaryingYear extends Varying { + + public VaryingYear(@NonNull final Varying.Id id, @NonNull final Value value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/data/series/varying/year/VaryingYearRepository.java b/src/main/java/de/ph87/data/series/varying/year/VaryingYearRepository.java new file mode 100644 index 0000000..d57cd9b --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/year/VaryingYearRepository.java @@ -0,0 +1,7 @@ +package de.ph87.data.series.varying.year; + +import de.ph87.data.series.varying.*; + +public interface VaryingYearRepository extends VaryingRepository { + +} diff --git a/src/main/java/de/ph87/data/value/Unit.java b/src/main/java/de/ph87/data/value/Unit.java index a032321..410f6bc 100644 --- a/src/main/java/de/ph87/data/value/Unit.java +++ b/src/main/java/de/ph87/data/value/Unit.java @@ -47,7 +47,7 @@ public enum Unit { public static class NotConvertible extends RuntimeException { - public NotConvertible(final @NonNull Unit source, final Unit target) { + public NotConvertible(@NonNull final Unit source, final Unit target) { super("Cannot convert Units: source=%s, target=%s".formatted(source, target)); }