diff --git a/src/main/java/de/ph87/data/demo/DemoService.java b/src/main/java/de/ph87/data/demo/DemoService.java deleted file mode 100644 index 288ae69..0000000 --- a/src/main/java/de/ph87/data/demo/DemoService.java +++ /dev/null @@ -1,38 +0,0 @@ -package de.ph87.data.demo; - -import de.ph87.data.series.Series; -import de.ph87.data.series.SeriesMode; -import de.ph87.data.series.SeriesRepository; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.context.event.ApplicationStartedEvent; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import static de.ph87.data.electricity.grid.GridReceiver.GRID_DELIVERY_SERIES_NAME; -import static de.ph87.data.electricity.grid.GridReceiver.GRID_PURCHASE_SERIES_NAME; -import static de.ph87.data.electricity.photovoltaic.PhotovoltaicReceiver.PHOTOVOLTAIC_ENERGY_SERIES_NAME; - -@Slf4j -@Service -@Transactional -@RequiredArgsConstructor -@SuppressWarnings("SameParameterValue") -public class DemoService { - - private final SeriesRepository seriesRepository; - - @EventListener(ApplicationStartedEvent.class) - public void startup() { - series(PHOTOVOLTAIC_ENERGY_SERIES_NAME, SeriesMode.INCREASING); - series(GRID_PURCHASE_SERIES_NAME, SeriesMode.INCREASING); - series(GRID_DELIVERY_SERIES_NAME, SeriesMode.INCREASING); - } - - private void series(@NonNull final String name, @NonNull final SeriesMode mode) { - seriesRepository.findByNameOrAliasesContains(name, name).orElseGet(() -> seriesRepository.save(new Series(name, mode))); - } - -} diff --git a/src/main/java/de/ph87/data/electricity/grid/GridReceiver.java b/src/main/java/de/ph87/data/electricity/grid/GridReceiver.java index 02a697a..92397a2 100644 --- a/src/main/java/de/ph87/data/electricity/grid/GridReceiver.java +++ b/src/main/java/de/ph87/data/electricity/grid/GridReceiver.java @@ -41,10 +41,10 @@ public class GridReceiver { } final double purchaseKWh = energyToKWh(inbound.purchaseWh, "Wh"); - applicationEventPublisher.publishEvent(new ConsumptionEvent(GRID_PURCHASE_SERIES_NAME, inbound.meter, inbound.date, purchaseKWh)); + applicationEventPublisher.publishEvent(new ConsumptionEvent(GRID_PURCHASE_SERIES_NAME, true, inbound.meter, inbound.date, purchaseKWh)); final double deliveryKWh = energyToKWh(inbound.deliveryWh, "Wh"); - applicationEventPublisher.publishEvent(new ConsumptionEvent(GRID_DELIVERY_SERIES_NAME, inbound.meter, inbound.date, deliveryKWh)); + applicationEventPublisher.publishEvent(new ConsumptionEvent(GRID_DELIVERY_SERIES_NAME, true, inbound.meter, inbound.date, deliveryKWh)); } } diff --git a/src/main/java/de/ph87/data/electricity/photovoltaic/PhotovoltaicReceiver.java b/src/main/java/de/ph87/data/electricity/photovoltaic/PhotovoltaicReceiver.java index 6e25f09..a77be18 100644 --- a/src/main/java/de/ph87/data/electricity/photovoltaic/PhotovoltaicReceiver.java +++ b/src/main/java/de/ph87/data/electricity/photovoltaic/PhotovoltaicReceiver.java @@ -23,7 +23,7 @@ public class PhotovoltaicReceiver { public static final String PHOTOVOLTAIC_ENERGY_SERIES_NAME = "photovoltaic.energyKWh"; - private static final Pattern REGEX = Pattern.compile("^(?\\S+) (?\\d+) (?\\d+(:?\\.\\d+)?)(?\\S+) (?\\d+(:?\\.\\d+)?)(?\\S+)$"); + private static final Pattern REGEX = Pattern.compile("^(?\\S+) (?\\d+) (?\\d+(:?\\.\\d+)?)(?\\S+) (?\\d+(:?\\.\\d+)?)(?\\S+)$"); private final ApplicationEventPublisher applicationEventPublisher; @@ -40,10 +40,10 @@ public class PhotovoltaicReceiver { final String serial = matcher.group("serial"); final ZonedDateTime date = ZDT(Long.parseLong(matcher.group("epochSeconds"))); - final double energy = Double.parseDouble(matcher.group("energy")); - final String energyUnit = matcher.group("energyUnit"); - final double energyKWh = energyToKWh(energy, energyUnit); - applicationEventPublisher.publishEvent(new ConsumptionEvent(PHOTOVOLTAIC_ENERGY_SERIES_NAME, serial, date, energyKWh)); + final double producedValue = Double.parseDouble(matcher.group("producedValue")); + final String producedUnit = matcher.group("producedUnit"); + final double producedKWh = energyToKWh(producedValue, producedUnit); + applicationEventPublisher.publishEvent(new ConsumptionEvent(PHOTOVOLTAIC_ENERGY_SERIES_NAME, true, serial, date, producedKWh)); } } diff --git a/src/main/java/de/ph87/data/series/SeriesIntervalKey.java b/src/main/java/de/ph87/data/series/SeriesIntervalKey.java new file mode 100644 index 0000000..680ffc9 --- /dev/null +++ b/src/main/java/de/ph87/data/series/SeriesIntervalKey.java @@ -0,0 +1,37 @@ +package de.ph87.data.series; + +import de.ph87.data.series.consumption.unit.Unit; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.ManyToOne; +import lombok.*; + +import java.io.Serializable; +import java.time.ZonedDateTime; + +@Getter +@ToString +@Embeddable +@EqualsAndHashCode +@NoArgsConstructor +public class SeriesIntervalKey implements Serializable { + + @NonNull + @ManyToOne(optional = false) + private Series series; + + @NonNull + @Column(nullable = false, updatable = false, columnDefinition = "CHAR(1)") + private Unit unit; + + @NonNull + @Column(nullable = false, updatable = false) + private ZonedDateTime aligned; + + public SeriesIntervalKey(@NonNull final Series series, @NonNull final Unit unit, @NonNull final ZonedDateTime unaligned) { + this.series = series; + this.unit = unit; + this.aligned = unit.align(unaligned); + } + +} diff --git a/src/main/java/de/ph87/data/series/SeriesService.java b/src/main/java/de/ph87/data/series/SeriesService.java index 1b73dca..0ec30c3 100644 --- a/src/main/java/de/ph87/data/series/SeriesService.java +++ b/src/main/java/de/ph87/data/series/SeriesService.java @@ -1,17 +1,11 @@ package de.ph87.data.series; -import de.ph87.data.series.consumption.ConsumptionEvent; -import de.ph87.data.series.consumption.ConsumptionEventTooOld; -import de.ph87.data.series.consumption.period.PeriodService; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.Optional; - @Slf4j @Service @Transactional @@ -20,24 +14,13 @@ public class SeriesService { private final SeriesRepository seriesRepository; - private final PeriodService periodService; - - @EventListener(ConsumptionEvent.class) - public void onConsumptionEvent(@NonNull final ConsumptionEvent event) { - log.debug("Handling ConsumptionEvent: {}", event); - final Optional seriesOptional = seriesRepository.findByNameOrAliasesContains(event.getName(), event.getName()); - if (seriesOptional.isEmpty()) { - log.warn("No series found with name or alias: \"{}\"", event.getName()); - return; - } - - final Series series = seriesOptional.get(); - log.debug("Series found: {}", series); - try { - periodService.onConsumptionEvent(series, event); - } catch (ConsumptionEventTooOld e) { - log.warn(e.toString()); + @NonNull + public Series getOrCreateByName(@NonNull final String name, @NonNull final SeriesMode mode) { + final Series series = seriesRepository.findByNameOrAliasesContains(name, name).orElseGet(() -> seriesRepository.save(new Series(name, mode))); + if (series.getMode() != mode) { + throw new RuntimeException("'mode' argument for getOrCreateByName does not match 'mode' of existing Series: mode=%s, series=%s".formatted(mode, series)); } + return series; } } diff --git a/src/main/java/de/ph87/data/series/consumption/ConsumptionEvent.java b/src/main/java/de/ph87/data/series/consumption/ConsumptionEvent.java index 7932654..3c1bfc6 100644 --- a/src/main/java/de/ph87/data/series/consumption/ConsumptionEvent.java +++ b/src/main/java/de/ph87/data/series/consumption/ConsumptionEvent.java @@ -15,6 +15,8 @@ public class ConsumptionEvent { @NonNull private final String name; + private final boolean increasing; + @NonNull private final String periodName; diff --git a/src/main/java/de/ph87/data/series/consumption/ConsumptionEventTooOld.java b/src/main/java/de/ph87/data/series/consumption/ConsumptionEventTooOld.java deleted file mode 100644 index d6364e3..0000000 --- a/src/main/java/de/ph87/data/series/consumption/ConsumptionEventTooOld.java +++ /dev/null @@ -1,12 +0,0 @@ -package de.ph87.data.series.consumption; - -import de.ph87.data.series.consumption.period.Period; -import lombok.NonNull; - -public class ConsumptionEventTooOld extends Exception { - - public ConsumptionEventTooOld(@NonNull final Period period, @NonNull final ConsumptionEvent event) { - super("Date of received Event older than last stored one: event=%s, period=%s".formatted(event, period)); - } - -} diff --git a/src/main/java/de/ph87/data/series/consumption/ConsumptionService.java b/src/main/java/de/ph87/data/series/consumption/ConsumptionService.java index 21511b6..2a5364c 100644 --- a/src/main/java/de/ph87/data/series/consumption/ConsumptionService.java +++ b/src/main/java/de/ph87/data/series/consumption/ConsumptionService.java @@ -1,10 +1,15 @@ package de.ph87.data.series.consumption; +import de.ph87.data.series.Series; +import de.ph87.data.series.SeriesMode; +import de.ph87.data.series.SeriesService; import de.ph87.data.series.consumption.period.Period; +import de.ph87.data.series.consumption.period.PeriodService; import de.ph87.data.series.consumption.unit.Unit; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -19,7 +24,17 @@ public class ConsumptionService { private final ConsumptionRepository consumptionRepository; - public void onConsumptionEvent(@NonNull final Period period, @NonNull final ConsumptionEvent event) { + private final SeriesService seriesService; + + private final PeriodService periodService; + + @EventListener(ConsumptionEvent.class) + public void onConsumptionEvent(@NonNull final ConsumptionEvent event) { + log.debug("Handling ConsumptionEvent: {}", event); + final Series series = seriesService.getOrCreateByName(event.getName(), event.isIncreasing() ? SeriesMode.INCREASING : SeriesMode.DECREASING); + final Period period = periodService.getOrCreatePeriod(series, event); + period.setLastDate(event.getDate()); + period.setLastValue(event.getValue()); for (final Unit unit : Unit.values()) { final ZonedDateTime aligned = unit.align(event.getDate()); final Optional existingOptional = consumptionRepository.findByIdPeriodAndIdUnitAndIdAligned(period, unit, aligned); diff --git a/src/main/java/de/ph87/data/series/consumption/period/PeriodService.java b/src/main/java/de/ph87/data/series/consumption/period/PeriodService.java index 1afeacb..71a6564 100644 --- a/src/main/java/de/ph87/data/series/consumption/period/PeriodService.java +++ b/src/main/java/de/ph87/data/series/consumption/period/PeriodService.java @@ -3,8 +3,6 @@ package de.ph87.data.series.consumption.period; import de.ph87.data.series.Series; import de.ph87.data.series.SeriesMode; import de.ph87.data.series.consumption.ConsumptionEvent; -import de.ph87.data.series.consumption.ConsumptionEventTooOld; -import de.ph87.data.series.consumption.ConsumptionService; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -19,22 +17,10 @@ public class PeriodService { private final PeriodRepository periodRepository; - private final ConsumptionService consumptionService; - - public void onConsumptionEvent(@NonNull final Series series, @NonNull final ConsumptionEvent event) throws ConsumptionEventTooOld { - final Period period = getOrCreatePeriod(series, event); - period.setLastDate(event.getDate()); - period.setLastValue(event.getValue()); - consumptionService.onConsumptionEvent(period, event); - } - @NonNull - private Period getOrCreatePeriod(@NonNull final Series series, @NonNull final ConsumptionEvent event) throws ConsumptionEventTooOld { + public Period getOrCreatePeriod(@NonNull final Series series, @NonNull final ConsumptionEvent event) { if (series.getPeriod() != null) { log.debug("Last Period exists: {}", series.getPeriod()); - if (isEventTooOld(series.getPeriod(), event)) { - throw new ConsumptionEventTooOld(series.getPeriod(), event); - } if (isPeriodValid(series.getPeriod(), event)) { log.debug("Last Period still VALID."); return series.getPeriod(); @@ -49,10 +35,6 @@ public class PeriodService { return newPeriod; } - private static boolean isEventTooOld(@NonNull final Period period, @NonNull final ConsumptionEvent event) { - return !period.getLastDate().isBefore(event.getDate()); - } - private boolean isPeriodValid(@NonNull final Period period, @NonNull final ConsumptionEvent event) { if (!period.getName().equals(event.getPeriodName())) { log.debug("Period name changed: old={}, new={}", period.getName(), event.getPeriodName()); diff --git a/src/main/java/de/ph87/data/series/measure/MeasureEventTooOld.java b/src/main/java/de/ph87/data/series/measure/MeasureEventTooOld.java deleted file mode 100644 index 42d5b1c..0000000 --- a/src/main/java/de/ph87/data/series/measure/MeasureEventTooOld.java +++ /dev/null @@ -1,12 +0,0 @@ -package de.ph87.data.series.measure; - -import de.ph87.data.series.period.Period; -import lombok.NonNull; - -public class MeasureEventTooOld extends Exception { - - public MeasureEventTooOld(@NonNull final Period period, @NonNull final MeasureEvent event) { - super("Date of received MeasureEvent older than last stored one: event=%s, period=%s".formatted(event, period)); - } - -}