Entry -> Varying + Meter cleanup
This commit is contained in:
parent
963648765f
commit
9169060cae
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package de.ph87.data.meter.day;
|
||||
|
||||
import org.springframework.data.repository.*;
|
||||
|
||||
public interface MeterDayRepository extends ListCrudRepository<MeterDay, MeterDay.Id> {
|
||||
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package de.ph87.data.meter.five;
|
||||
|
||||
import org.springframework.data.repository.*;
|
||||
|
||||
public interface MeterFiveRepository extends ListCrudRepository<MeterFive, MeterFive.Id> {
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package de.ph87.data.meter.month;
|
||||
|
||||
import org.springframework.data.repository.*;
|
||||
|
||||
public interface MeterMonthRepository extends ListCrudRepository<MeterMonth, MeterMonth.Id> {
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package de.ph87.data.meter.week;
|
||||
|
||||
import org.springframework.data.repository.*;
|
||||
|
||||
public interface MeterWeekRepository extends ListCrudRepository<MeterWeek, MeterWeek.Id> {
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package de.ph87.data.meter.year;
|
||||
|
||||
import org.springframework.data.repository.*;
|
||||
|
||||
public interface MeterYearRepository extends ListCrudRepository<MeterYear, MeterYear.Id> {
|
||||
|
||||
}
|
||||
26
src/main/java/de/ph87/data/series/Aligned.java
Normal file
26
src/main/java/de/ph87/data/series/Aligned.java
Normal file
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
34
src/main/java/de/ph87/data/series/Alignment.java
Normal file
34
src/main/java/de/ph87/data/series/Alignment.java
Normal file
@ -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<ZonedDateTime, ZonedDateTime> align;
|
||||
|
||||
@NonNull
|
||||
public final BiFunction<ZonedDateTime, Long, ZonedDateTime> plus;
|
||||
|
||||
Alignment(@NonNull final Function<ZonedDateTime, ZonedDateTime> align, @NonNull final BiFunction<ZonedDateTime, Long, ZonedDateTime> plus) {
|
||||
this.align = align;
|
||||
this.plus = plus;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Aligned align(@NonNull final ZonedDateTime date) {
|
||||
return new Aligned(this, date);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
5
src/main/java/de/ph87/data/series/SeriesType.java
Normal file
5
src/main/java/de/ph87/data/series/SeriesType.java
Normal file
@ -0,0 +1,5 @@
|
||||
package de.ph87.data.series;
|
||||
|
||||
public enum SeriesType {
|
||||
VARYING, METER,
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<Entry, Entry.EntryId> {
|
||||
|
||||
Optional<Entry> findById(@NonNull Entry.EntryId id);
|
||||
|
||||
List<Entry> findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(long id, @NonNull ZonedDateTime begin, @NonNull ZonedDateTime end);
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<Entry> 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<GraphPoint> 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<Entry, Point> 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<GraphPoint, Point> 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);
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
14
src/main/java/de/ph87/data/series/graph/GraphPoint.java
Normal file
14
src/main/java/de/ph87/data/series/graph/GraphPoint.java
Normal file
@ -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;
|
||||
|
||||
}
|
||||
@ -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<Entry> entries = entryRepository.findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(seriesId, begin, end);
|
||||
final List<GraphPoint> 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);
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package de.ph87.data.meter;
|
||||
package de.ph87.data.series.meter;
|
||||
|
||||
import de.ph87.data.series.*;
|
||||
import jakarta.persistence.*;
|
||||
30
src/main/java/de/ph87/data/series/meter/MeterInbound.java
Normal file
30
src/main/java/de/ph87/data/series/meter/MeterInbound.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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.*;
|
||||
85
src/main/java/de/ph87/data/series/meter/MeterService.java
Normal file
85
src/main/java/de/ph87/data/series/meter/MeterService.java
Normal file
@ -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<GraphPoint> getPoints(@NonNull final SeriesDto series, @NonNull final Aligned begin, @NonNull final Aligned end) {
|
||||
final List<? extends MeterValue> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<T extends MeterValue> extends ListCrudRepository<T, MeterValue.Id> {
|
||||
|
||||
List<T> findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(long id, @NonNull ZonedDateTime begin, @NonNull ZonedDateTime end);
|
||||
|
||||
}
|
||||
18
src/main/java/de/ph87/data/series/meter/day/MeterDay.java
Normal file
18
src/main/java/de/ph87/data/series/meter/day/MeterDay.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package de.ph87.data.series.meter.day;
|
||||
|
||||
import de.ph87.data.series.meter.*;
|
||||
|
||||
public interface MeterDayRepository extends MeterValueRepository<MeterDay> {
|
||||
|
||||
}
|
||||
18
src/main/java/de/ph87/data/series/meter/five/MeterFive.java
Normal file
18
src/main/java/de/ph87/data/series/meter/five/MeterFive.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package de.ph87.data.series.meter.five;
|
||||
|
||||
import de.ph87.data.series.meter.*;
|
||||
|
||||
public interface MeterFiveRepository extends MeterValueRepository<MeterFive> {
|
||||
|
||||
}
|
||||
18
src/main/java/de/ph87/data/series/meter/hour/MeterHour.java
Normal file
18
src/main/java/de/ph87/data/series/meter/hour/MeterHour.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package de.ph87.data.series.meter.hour;
|
||||
|
||||
import de.ph87.data.series.meter.*;
|
||||
|
||||
public interface MeterHourRepository extends MeterValueRepository<MeterHour> {
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package de.ph87.data.series.meter.month;
|
||||
|
||||
import de.ph87.data.series.meter.*;
|
||||
|
||||
public interface MeterMonthRepository extends MeterValueRepository<MeterMonth> {
|
||||
|
||||
}
|
||||
18
src/main/java/de/ph87/data/series/meter/week/MeterWeek.java
Normal file
18
src/main/java/de/ph87/data/series/meter/week/MeterWeek.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package de.ph87.data.series.meter.week;
|
||||
|
||||
import de.ph87.data.series.meter.*;
|
||||
|
||||
public interface MeterWeekRepository extends MeterValueRepository<MeterWeek> {
|
||||
|
||||
}
|
||||
18
src/main/java/de/ph87/data/series/meter/year/MeterYear.java
Normal file
18
src/main/java/de/ph87/data/series/meter/year/MeterYear.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package de.ph87.data.series.meter.year;
|
||||
|
||||
import de.ph87.data.series.meter.*;
|
||||
|
||||
public interface MeterYearRepository extends MeterValueRepository<MeterYear> {
|
||||
|
||||
}
|
||||
67
src/main/java/de/ph87/data/series/varying/Varying.java
Normal file
67
src/main/java/de/ph87/data/series/varying/Varying.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
@ -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<T extends Varying> extends ListCrudRepository<T, Varying.Id> {
|
||||
|
||||
List<T> findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(long id, @NonNull ZonedDateTime begin, @NonNull ZonedDateTime end);
|
||||
|
||||
}
|
||||
@ -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<GraphPoint> getPoints(@NonNull final SeriesDto series, @NonNull final Aligned begin, @NonNull final Aligned end) {
|
||||
final List<? extends Varying> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package de.ph87.data.series.varying.day;
|
||||
|
||||
import de.ph87.data.series.varying.*;
|
||||
|
||||
public interface VaryingDayRepository extends VaryingRepository<VaryingDay> {
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package de.ph87.data.series.varying.five;
|
||||
|
||||
import de.ph87.data.series.varying.*;
|
||||
|
||||
public interface VaryingFiveRepository extends VaryingRepository<VaryingFive> {
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package de.ph87.data.series.varying.hour;
|
||||
|
||||
import de.ph87.data.series.varying.*;
|
||||
|
||||
public interface VaryingHourRepository extends VaryingRepository<VaryingHour> {
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package de.ph87.data.series.varying.month;
|
||||
|
||||
import de.ph87.data.series.varying.*;
|
||||
|
||||
public interface VaryingMonthRepository extends VaryingRepository<VaryingMonth> {
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package de.ph87.data.series.varying.week;
|
||||
|
||||
import de.ph87.data.series.varying.*;
|
||||
|
||||
public interface VaryingWeekRepository extends VaryingRepository<VaryingWeek> {
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package de.ph87.data.series.varying.year;
|
||||
|
||||
import de.ph87.data.series.varying.*;
|
||||
|
||||
public interface VaryingYearRepository extends VaryingRepository<VaryingYear> {
|
||||
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user