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.*;
|
||||||
import com.fasterxml.jackson.databind.annotation.*;
|
import com.fasterxml.jackson.databind.annotation.*;
|
||||||
import de.ph87.data.message.*;
|
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.Value;
|
||||||
import de.ph87.data.value.*;
|
import de.ph87.data.value.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.extern.slf4j.*;
|
import lombok.extern.slf4j.*;
|
||||||
|
import org.springframework.context.*;
|
||||||
import org.springframework.stereotype.*;
|
import org.springframework.stereotype.*;
|
||||||
|
|
||||||
import java.time.*;
|
import java.time.*;
|
||||||
@ -25,7 +26,7 @@ public class EspHomeHandler implements IMessageHandler {
|
|||||||
|
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
private final EntryService entryService;
|
private final ApplicationEventPublisher applicationEventPublisher;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(@NonNull final Message message) throws Exception {
|
public void handle(@NonNull final Message message) throws Exception {
|
||||||
@ -58,7 +59,7 @@ public class EspHomeHandler implements IMessageHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Value value = new Value(inbound.value, unitFromPayload);
|
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) {
|
private String propertyReplace(final String property) {
|
||||||
|
|||||||
@ -2,11 +2,12 @@ package de.ph87.data.message.handler;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.*;
|
import com.fasterxml.jackson.databind.*;
|
||||||
import de.ph87.data.message.*;
|
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.Value;
|
||||||
import de.ph87.data.value.*;
|
import de.ph87.data.value.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.extern.slf4j.*;
|
import lombok.extern.slf4j.*;
|
||||||
|
import org.springframework.context.*;
|
||||||
import org.springframework.stereotype.*;
|
import org.springframework.stereotype.*;
|
||||||
|
|
||||||
import java.time.*;
|
import java.time.*;
|
||||||
@ -21,7 +22,7 @@ public class HeizungHandler implements IMessageHandler {
|
|||||||
|
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
private final EntryService entryService;
|
private final ApplicationEventPublisher applicationEventPublisher;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(@NonNull final Message message) throws Exception {
|
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 String property = matcher.group("property");
|
||||||
final Inbound inbound = objectMapper.readValue(message.payload, Inbound.class);
|
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
|
@Getter
|
||||||
|
|||||||
@ -2,12 +2,13 @@ package de.ph87.data.message.handler;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.*;
|
import com.fasterxml.jackson.databind.*;
|
||||||
import de.ph87.data.message.*;
|
import de.ph87.data.message.*;
|
||||||
import de.ph87.data.meter.*;
|
import de.ph87.data.series.meter.*;
|
||||||
import de.ph87.data.series.entry.*;
|
import de.ph87.data.series.varying.*;
|
||||||
import de.ph87.data.value.Value;
|
import de.ph87.data.value.Value;
|
||||||
import de.ph87.data.value.*;
|
import de.ph87.data.value.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.extern.slf4j.*;
|
import lombok.extern.slf4j.*;
|
||||||
|
import org.springframework.context.*;
|
||||||
import org.springframework.stereotype.*;
|
import org.springframework.stereotype.*;
|
||||||
|
|
||||||
import java.time.*;
|
import java.time.*;
|
||||||
@ -21,18 +22,16 @@ public class OpenDTUHandler implements IMessageHandler {
|
|||||||
|
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
private final EntryService entryService;
|
private final ApplicationEventPublisher applicationEventPublisher;
|
||||||
|
|
||||||
private final MeterService meterService;
|
|
||||||
|
|
||||||
@Override
|
@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)) {
|
if (!"openDTU/pv/patrix/json".equals(message.topic)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Inbound inbound = objectMapper.readValue(message.payload, Inbound.class);
|
final Inbound inbound = objectMapper.readValue(message.payload, Inbound.class);
|
||||||
entryService.receive("power/produced", inbound.date, inbound.power.as(Unit.POWER_W));
|
applicationEventPublisher.publishEvent(new VaryingInbound("power/produced", inbound.date, inbound.power.as(Unit.POWER_W)));
|
||||||
meterService.receive("energy/produced", METER_NUMBER, inbound.date, inbound.energy);
|
applicationEventPublisher.publishEvent(new MeterInbound("energy/produced", METER_NUMBER, inbound.date, inbound.energy));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
|||||||
@ -2,11 +2,12 @@ package de.ph87.data.message.handler;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.*;
|
import com.fasterxml.jackson.databind.*;
|
||||||
import de.ph87.data.message.*;
|
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.Value;
|
||||||
import de.ph87.data.value.*;
|
import de.ph87.data.value.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.extern.slf4j.*;
|
import lombok.extern.slf4j.*;
|
||||||
|
import org.springframework.context.*;
|
||||||
import org.springframework.stereotype.*;
|
import org.springframework.stereotype.*;
|
||||||
|
|
||||||
import java.time.*;
|
import java.time.*;
|
||||||
@ -18,7 +19,7 @@ public class SimpleJsonHandler implements IMessageHandler {
|
|||||||
|
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
private final EntryService entryService;
|
private final ApplicationEventPublisher applicationEventPublisher;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(@NonNull final Message message) throws Exception {
|
public void handle(@NonNull final Message message) throws Exception {
|
||||||
@ -26,7 +27,7 @@ public class SimpleJsonHandler implements IMessageHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Inbound inbound = objectMapper.readValue(message.payload, Inbound.class);
|
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
|
@Getter
|
||||||
|
|||||||
@ -2,12 +2,13 @@ package de.ph87.data.message.handler;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.*;
|
import com.fasterxml.jackson.databind.*;
|
||||||
import de.ph87.data.message.*;
|
import de.ph87.data.message.*;
|
||||||
import de.ph87.data.meter.*;
|
import de.ph87.data.series.meter.*;
|
||||||
import de.ph87.data.series.entry.*;
|
import de.ph87.data.series.varying.*;
|
||||||
import de.ph87.data.value.Value;
|
import de.ph87.data.value.Value;
|
||||||
import de.ph87.data.value.*;
|
import de.ph87.data.value.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.extern.slf4j.*;
|
import lombok.extern.slf4j.*;
|
||||||
|
import org.springframework.context.*;
|
||||||
import org.springframework.stereotype.*;
|
import org.springframework.stereotype.*;
|
||||||
|
|
||||||
import java.time.*;
|
import java.time.*;
|
||||||
@ -21,9 +22,7 @@ public class SmartMeterHandler implements IMessageHandler {
|
|||||||
|
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
private final EntryService entryService;
|
private final ApplicationEventPublisher applicationEventPublisher;
|
||||||
|
|
||||||
private final MeterService meterService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(@NonNull final Message message) throws Exception {
|
public void handle(@NonNull final Message message) throws Exception {
|
||||||
@ -31,9 +30,9 @@ public class SmartMeterHandler implements IMessageHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Inbound inbound = objectMapper.readValue(message.payload, Inbound.class);
|
final Inbound inbound = objectMapper.readValue(message.payload, Inbound.class);
|
||||||
entryService.receive("power/balance", inbound.date, inbound.powerBalance.as(Unit.POWER_W));
|
applicationEventPublisher.publishEvent(new VaryingInbound("power/balance", inbound.date, inbound.powerBalance.as(Unit.POWER_W)));
|
||||||
meterService.receive("energy/purchased", METER_NUMBER, inbound.date, inbound.energyPurchased);
|
applicationEventPublisher.publishEvent(new MeterInbound("energy/purchased", METER_NUMBER, inbound.date, inbound.energyPurchased));
|
||||||
meterService.receive("energy/delivered", METER_NUMBER, inbound.date, inbound.energyDelivered);
|
applicationEventPublisher.publishEvent(new MeterInbound("energy/delivered", METER_NUMBER, inbound.date, inbound.energyDelivered));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@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)
|
@Column(nullable = false)
|
||||||
private int decimals = 1;
|
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.name = name;
|
||||||
this.title = name;
|
this.title = name;
|
||||||
this.unit = unit;
|
this.unit = unit;
|
||||||
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,12 +18,15 @@ public class SeriesDto {
|
|||||||
|
|
||||||
public final int decimals;
|
public final int decimals;
|
||||||
|
|
||||||
|
public final SeriesType type;
|
||||||
|
|
||||||
public SeriesDto(@NonNull final Series series) {
|
public SeriesDto(@NonNull final Series series) {
|
||||||
this.id = series.getId();
|
this.id = series.getId();
|
||||||
this.name = series.getName();
|
this.name = series.getName();
|
||||||
this.title = series.getTitle();
|
this.title = series.getTitle();
|
||||||
this.unit = series.getUnit();
|
this.unit = series.getUnit();
|
||||||
this.decimals = series.getDecimals();
|
this.decimals = series.getDecimals();
|
||||||
|
this.type = series.getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String format(@Nullable final Double value) {
|
public String format(@Nullable final Double value) {
|
||||||
|
|||||||
@ -49,14 +49,18 @@ public class SeriesService {
|
|||||||
return new SeriesDto(series);
|
return new SeriesDto(series);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Series getOrCreate(@NonNull final String name, @NonNull final Unit unit) {
|
public Series getOrCreate(@NonNull final String name, @NonNull final Unit unit, @NonNull final SeriesType type) {
|
||||||
return seriesRepository
|
final Series series = seriesRepository
|
||||||
.findByName(name)
|
.findByName(name)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
final Series series = seriesRepository.save(new Series(name, unit));
|
final Series fresh = seriesRepository.save(new Series(name, unit, type));
|
||||||
publish(series, Action.CREATED);
|
publish(fresh, Action.CREATED);
|
||||||
return series;
|
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;
|
package de.ph87.data.series.graph;
|
||||||
|
|
||||||
import de.ph87.data.series.*;
|
import de.ph87.data.series.*;
|
||||||
import de.ph87.data.series.entry.*;
|
|
||||||
import jakarta.annotation.*;
|
import jakarta.annotation.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.geom.*;
|
import java.awt.geom.*;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.time.*;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.*;
|
import java.util.function.*;
|
||||||
|
|
||||||
@ -24,10 +22,10 @@ public class Graph {
|
|||||||
public final SeriesDto series;
|
public final SeriesDto series;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public final ZonedDateTime begin;
|
public final Aligned begin;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public final ZonedDateTime end;
|
public final Aligned end;
|
||||||
|
|
||||||
public final int width;
|
public final int width;
|
||||||
|
|
||||||
@ -61,7 +59,7 @@ public class Graph {
|
|||||||
|
|
||||||
public final int maxLabelWidth;
|
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.series = series;
|
||||||
this.begin = begin;
|
this.begin = begin;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
@ -74,20 +72,20 @@ public class Graph {
|
|||||||
double vMax = Double.MIN_VALUE;
|
double vMax = Double.MIN_VALUE;
|
||||||
int maxLabelWidth = 80;
|
int maxLabelWidth = 80;
|
||||||
final FontMetrics fontMetrics = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).getGraphics().getFontMetrics();
|
final FontMetrics fontMetrics = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).getGraphics().getFontMetrics();
|
||||||
for (final Entry entry : entries) {
|
for (final GraphPoint point : points) {
|
||||||
vMin = Math.min(vMin, entry.getValue());
|
vMin = Math.min(vMin, point.getValue());
|
||||||
vMax = max(vMax, entry.getValue());
|
vMax = max(vMax, point.getValue());
|
||||||
vSum += entry.getValue();
|
vSum += point.getValue();
|
||||||
maxLabelWidth = max(maxLabelWidth, fontMetrics.stringWidth(series.format(entry.getValue())));
|
maxLabelWidth = max(maxLabelWidth, fontMetrics.stringWidth(series.format(point.getValue())));
|
||||||
}
|
}
|
||||||
this.valueAvg = vSum / entries.size();
|
this.valueAvg = vSum / points.size();
|
||||||
this.maxLabelWidth = maxLabelWidth;
|
this.maxLabelWidth = maxLabelWidth;
|
||||||
|
|
||||||
widthInner = width - 3 * border - maxLabelWidth;
|
widthInner = width - 3 * border - maxLabelWidth;
|
||||||
heightInner = height - 2 * border;
|
heightInner = height - 2 * border;
|
||||||
|
|
||||||
minuteMin = begin.toEpochSecond() / 60;
|
minuteMin = begin.date.toEpochSecond() / 60;
|
||||||
minuteMax = end.toEpochSecond() / 60;
|
minuteMax = end.date.toEpochSecond() / 60;
|
||||||
minuteRange = minuteMax - minuteMin;
|
minuteRange = minuteMax - minuteMin;
|
||||||
minuteScale = (double) widthInner / minuteRange;
|
minuteScale = (double) widthInner / minuteRange;
|
||||||
|
|
||||||
@ -96,7 +94,7 @@ public class Graph {
|
|||||||
valueRange = vMax - vMin;
|
valueRange = vMax - vMin;
|
||||||
valueScale = heightInner / valueRange;
|
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() {
|
public BufferedImage draw() {
|
||||||
@ -131,7 +129,7 @@ public class Graph {
|
|||||||
return image;
|
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 String string = series.format(value);
|
||||||
final int offset = maxLabelWidth - g.getFontMetrics().stringWidth(string);
|
final int offset = maxLabelWidth - g.getFontMetrics().stringWidth(string);
|
||||||
final int y = height - ((int) Math.round((value - valueMin) * valueScale) + border);
|
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) {
|
private Function<GraphPoint, Point> toPoint(final long minuteMin, final double minuteScale, final double valueMin, final double valueScale) {
|
||||||
return entry -> {
|
return point -> {
|
||||||
final long minuteEpoch = entry.getId().getDate().toEpochSecond() / 60;
|
final long minuteEpoch = point.getDate().toEpochSecond() / 60;
|
||||||
final long minuteRelative = minuteEpoch - minuteMin;
|
final long minuteRelative = minuteEpoch - minuteMin;
|
||||||
final double minuteScaled = minuteRelative * minuteScale;
|
final double minuteScaled = minuteRelative * minuteScale;
|
||||||
final int x = (int) Math.round(minuteScaled);
|
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 double valueScaled = valueRelative * valueScale;
|
||||||
final int y = (int) Math.round(valueScaled);
|
final int y = (int) Math.round(valueScaled);
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package de.ph87.data.series.graph;
|
package de.ph87.data.series.graph;
|
||||||
|
|
||||||
|
import de.ph87.data.series.*;
|
||||||
import jakarta.servlet.http.*;
|
import jakarta.servlet.http.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.extern.slf4j.*;
|
import lombok.extern.slf4j.*;
|
||||||
@ -18,10 +19,11 @@ public class GraphController {
|
|||||||
|
|
||||||
private final GraphService graphService;
|
private final GraphService graphService;
|
||||||
|
|
||||||
@GetMapping(path = "{seriesId}/{width}/{height}/{offset}/{duration}", produces = "image/png")
|
@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 offset, @PathVariable final String duration) throws IOException {
|
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 ZonedDateTime end = ZonedDateTime.now().minus(Duration.parse(offset));
|
final Alignment interval = Alignment.valueOf(intervalName);
|
||||||
final ZonedDateTime begin = end.minus(Duration.parse(duration));
|
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 Graph graph = graphService.getGraph(seriesId, begin, end, width, height, 10);
|
||||||
final BufferedImage image = graph.draw();
|
final BufferedImage image = graph.draw();
|
||||||
response.setContentType("image/png");
|
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;
|
package de.ph87.data.series.graph;
|
||||||
|
|
||||||
import de.ph87.data.series.*;
|
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.*;
|
||||||
import lombok.extern.slf4j.*;
|
import lombok.extern.slf4j.*;
|
||||||
import org.springframework.stereotype.*;
|
import org.springframework.stereotype.*;
|
||||||
import org.springframework.transaction.annotation.*;
|
|
||||||
|
|
||||||
import java.time.*;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@Transactional
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class GraphService {
|
public class GraphService {
|
||||||
|
|
||||||
private final EntryRepository entryRepository;
|
|
||||||
|
|
||||||
private final SeriesService seriesService;
|
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 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);
|
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 de.ph87.data.series.*;
|
||||||
import jakarta.persistence.*;
|
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 de.ph87.data.series.*;
|
||||||
import org.springframework.data.repository.*;
|
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 de.ph87.data.value.Value;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
|
||||||
import java.time.*;
|
import java.time.*;
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Getter
|
@Getter
|
||||||
@ToString
|
@ToString
|
||||||
|
@MappedSuperclass
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class MeterFive {
|
public abstract class MeterValue {
|
||||||
|
|
||||||
@EmbeddedId
|
@EmbeddedId
|
||||||
private Id id;
|
private Id id;
|
||||||
@ -22,24 +22,23 @@ public class MeterFive {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private double max;
|
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.id = id;
|
||||||
this.min = value.as(id.getMeter().getSeries().getUnit()).value;
|
final double converted = value.as(id.getMeter().getSeries().getUnit()).value;
|
||||||
this.max = this.min;
|
this.min = converted;
|
||||||
|
this.max = converted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(@NonNull final Value value) {
|
public void update(@NonNull final Value value) {
|
||||||
final double v = value.as(id.getMeter().getSeries().getUnit()).value;
|
final double converted = value.as(id.getMeter().getSeries().getUnit()).value;
|
||||||
if (v < this.max) {
|
if (converted < this.max) {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
this.max = v;
|
this.max = converted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Data
|
||||||
@ToString
|
|
||||||
@Embeddable
|
@Embeddable
|
||||||
@EqualsAndHashCode
|
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public static class Id {
|
public static class Id {
|
||||||
|
|
||||||
@ -51,9 +50,9 @@ public class MeterFive {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private ZonedDateTime date;
|
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.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 de.ph87.data.value.Value;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
@ -7,7 +7,7 @@ import java.time.*;
|
|||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@ToString
|
@ToString
|
||||||
public class SeriesInbound {
|
public class VaryingInbound {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public final String name;
|
public final String name;
|
||||||
@ -17,7 +17,7 @@ public class SeriesInbound {
|
|||||||
|
|
||||||
public final Value value;
|
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.name = name;
|
||||||
this.date = date;
|
this.date = date;
|
||||||
this.value = value;
|
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 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));
|
super("Cannot convert Units: source=%s, target=%s".formatted(source, target));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user