AggregationReceiver, Heating, Measure, Counter
This commit is contained in:
parent
c85fbb12c3
commit
e090e1530d
75
src/main/java/de/ph87/data/aggregation/Aggregation.java
Normal file
75
src/main/java/de/ph87/data/aggregation/Aggregation.java
Normal file
@ -0,0 +1,75 @@
|
||||
package de.ph87.data.aggregation;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import static de.ph87.data.common.DateTimeHelpers.ZDT;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public class Aggregation {
|
||||
|
||||
@NonNull
|
||||
public final String name;
|
||||
|
||||
@NonNull
|
||||
public final ZonedDateTime date;
|
||||
|
||||
public final int count;
|
||||
|
||||
public final int errors;
|
||||
|
||||
@NonNull
|
||||
public final ZonedDateTime firstTime;
|
||||
|
||||
@NonNull
|
||||
public final ZonedDateTime lastTime;
|
||||
|
||||
public final double firstValue;
|
||||
|
||||
public final double lastValue;
|
||||
|
||||
public final double min;
|
||||
|
||||
public final double max;
|
||||
|
||||
public final double sum;
|
||||
|
||||
public final double squares;
|
||||
|
||||
public final boolean terminal;
|
||||
|
||||
public Aggregation(
|
||||
final String name,
|
||||
final long date,
|
||||
final int count,
|
||||
final int errors,
|
||||
final long firstTime,
|
||||
final long lastTime,
|
||||
final double firstValue,
|
||||
final double lastValue,
|
||||
final double min,
|
||||
final double max,
|
||||
final double sum,
|
||||
final double squares,
|
||||
final boolean terminal
|
||||
) {
|
||||
this.name = name;
|
||||
this.date = ZDT(date);
|
||||
this.count = count;
|
||||
this.errors = errors;
|
||||
this.firstTime = ZDT(firstTime);
|
||||
this.lastTime = ZDT(lastTime);
|
||||
this.firstValue = firstValue;
|
||||
this.lastValue = lastValue;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.sum = sum;
|
||||
this.squares = squares;
|
||||
this.terminal = terminal;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package de.ph87.data.aggregation;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static de.ph87.data.heating.HeatingConstants.*;
|
||||
|
||||
public class AggregationConstants {
|
||||
|
||||
public static final Map<String, String> HEATING_TOPIC_MAP = Map.of(
|
||||
"aggregation/heizung/abgas/temperatur", HEATING_EXHAUST_TEMPERATURE_SERIES_NAME,
|
||||
"aggregation/heizung/puffer/vorlauf/temperatur", HEATING_BUFFER_SUPPLY_TEMPERATURE_SERIES_NAME,
|
||||
"aggregation/heizung/puffer/ruecklauf/temperatur", HEATING_BUFFER_RETURN_TEMPERATURE_SERIES_NAME,
|
||||
"aggregation/heizung/puffer/eingang/temperatur", HEATING_BUFFER_COLD_TEMPERATURE_SERIES_NAME,
|
||||
"aggregation/heizung/puffer/speicher/temperatur", HEATING_BUFFER_INNER_TEMPERATURE_SERIES_NAME,
|
||||
"aggregation/heizung/puffer/ausgang/temperatur", HEATING_BUFFER_HOT_TEMPERATURE_SERIES_NAME,
|
||||
"aggregation/heizung/puffer/zirkulation/temperatur", HEATING_BUFFER_CIRCULATION_TEMPERATURE_SERIES_NAME,
|
||||
"aggregation/heizung/heizkreis/vorlauf/temperatur", HEATING_LOOP_SUPPLY_TEMPERATURE_SERIES_NAME,
|
||||
"aggregation/heizung/heizkreis/ruecklauf/temperatur", HEATING_LOOP_RETURN_TEMPERATURE_SERIES_NAME
|
||||
);
|
||||
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package de.ph87.data.aggregation;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import de.ph87.data.mqtt.MqttEvent;
|
||||
import de.ph87.data.series.measure.MeasureEvent;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static de.ph87.data.aggregation.AggregationConstants.HEATING_TOPIC_MAP;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AggregationReceiver {
|
||||
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@EventListener(MqttEvent.class)
|
||||
public void onEvent(@NonNull final MqttEvent event) {
|
||||
final String seriesName = HEATING_TOPIC_MAP.get(event.topic);
|
||||
if (seriesName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Aggregation aggregation;
|
||||
try {
|
||||
aggregation = objectMapper.readValue(event.payload, Aggregation.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Failed to parse Aggregation: error={}, event={}", e, event);
|
||||
return;
|
||||
}
|
||||
|
||||
applicationEventPublisher.publishEvent(new MeasureEvent(seriesName, aggregation.lastTime, aggregation.lastValue));
|
||||
}
|
||||
|
||||
}
|
||||
23
src/main/java/de/ph87/data/heating/HeatingConstants.java
Normal file
23
src/main/java/de/ph87/data/heating/HeatingConstants.java
Normal file
@ -0,0 +1,23 @@
|
||||
package de.ph87.data.heating;
|
||||
|
||||
public class HeatingConstants {
|
||||
|
||||
public static final String HEATING_EXHAUST_TEMPERATURE_SERIES_NAME = "heating.exhaust.temperature";
|
||||
|
||||
public static final String HEATING_BUFFER_SUPPLY_TEMPERATURE_SERIES_NAME = "heating.buffer.supply.temperature";
|
||||
|
||||
public static final String HEATING_BUFFER_RETURN_TEMPERATURE_SERIES_NAME = "heating.buffer.return.temperature";
|
||||
|
||||
public static final String HEATING_BUFFER_COLD_TEMPERATURE_SERIES_NAME = "heating.buffer.cold.temperature";
|
||||
|
||||
public static final String HEATING_BUFFER_INNER_TEMPERATURE_SERIES_NAME = "heating.buffer.inner.temperature";
|
||||
|
||||
public static final String HEATING_BUFFER_HOT_TEMPERATURE_SERIES_NAME = "heating.buffer.hot.temperature";
|
||||
|
||||
public static final String HEATING_BUFFER_CIRCULATION_TEMPERATURE_SERIES_NAME = "heating.buffer.circulation.temperature";
|
||||
|
||||
public static final String HEATING_LOOP_SUPPLY_TEMPERATURE_SERIES_NAME = "heating.loop.supply.temperature";
|
||||
|
||||
public static final String HEATING_LOOP_RETURN_TEMPERATURE_SERIES_NAME = "heating.loop.return.temperature";
|
||||
|
||||
}
|
||||
@ -5,6 +5,8 @@ import lombok.NonNull;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public enum SeriesMode {
|
||||
MEASURE((first, second) -> second - first),
|
||||
COUNTER((first, second) -> second - first),
|
||||
INCREASING((first, second) -> second - first),
|
||||
DECREASING((first, second) -> first - second),
|
||||
;
|
||||
|
||||
26
src/main/java/de/ph87/data/series/counter/Counter.java
Normal file
26
src/main/java/de/ph87/data/series/counter/Counter.java
Normal file
@ -0,0 +1,26 @@
|
||||
package de.ph87.data.series.counter;
|
||||
|
||||
import de.ph87.data.series.SeriesIntervalKey;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
public class Counter {
|
||||
|
||||
@NonNull
|
||||
@EmbeddedId
|
||||
private SeriesIntervalKey id;
|
||||
|
||||
@Setter
|
||||
private int count;
|
||||
|
||||
public Counter(@NonNull final SeriesIntervalKey id, final int count) {
|
||||
this.id = id;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
}
|
||||
23
src/main/java/de/ph87/data/series/counter/CounterEvent.java
Normal file
23
src/main/java/de/ph87/data/series/counter/CounterEvent.java
Normal file
@ -0,0 +1,23 @@
|
||||
package de.ph87.data.series.counter;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public class CounterEvent {
|
||||
|
||||
@NonNull
|
||||
private final String name;
|
||||
|
||||
@NonNull
|
||||
private final ZonedDateTime date;
|
||||
|
||||
private final int count;
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package de.ph87.data.series.counter;
|
||||
|
||||
import de.ph87.data.series.SeriesIntervalKey;
|
||||
import org.springframework.data.repository.ListCrudRepository;
|
||||
|
||||
public interface CounterRepository extends ListCrudRepository<Counter, SeriesIntervalKey> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package de.ph87.data.series.counter;
|
||||
|
||||
import de.ph87.data.series.Series;
|
||||
import de.ph87.data.series.SeriesIntervalKey;
|
||||
import de.ph87.data.series.SeriesMode;
|
||||
import de.ph87.data.series.SeriesService;
|
||||
import de.ph87.data.series.consumption.unit.Unit;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class CounterService {
|
||||
|
||||
private final CounterRepository counterRepository;
|
||||
|
||||
private final SeriesService seriesService;
|
||||
|
||||
@EventListener(CounterEvent.class)
|
||||
public void onCounterEvent(@NonNull final CounterEvent event) {
|
||||
final Series series = seriesService.getOrCreateByName(event.getName(), SeriesMode.COUNTER);
|
||||
for (final Unit unit : Unit.values()) {
|
||||
final SeriesIntervalKey id = new SeriesIntervalKey(series, unit, event.getDate());
|
||||
counterRepository.findById(id)
|
||||
.stream()
|
||||
.peek(existing -> existing.setCount(existing.getCount() + event.getCount()))
|
||||
.findFirst()
|
||||
.orElseGet(() -> counterRepository.save(new Counter(id, event.getCount())));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
84
src/main/java/de/ph87/data/series/measure/Measure.java
Normal file
84
src/main/java/de/ph87/data/series/measure/Measure.java
Normal file
@ -0,0 +1,84 @@
|
||||
package de.ph87.data.series.measure;
|
||||
|
||||
import de.ph87.data.series.SeriesIntervalKey;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
public class Measure {
|
||||
|
||||
@NonNull
|
||||
@EmbeddedId
|
||||
private SeriesIntervalKey id;
|
||||
|
||||
@NonNull
|
||||
@Column(nullable = false)
|
||||
private ZonedDateTime firstDate;
|
||||
|
||||
@Column(nullable = false)
|
||||
private double firstValue;
|
||||
|
||||
@Setter
|
||||
@NonNull
|
||||
@Column(nullable = false)
|
||||
private ZonedDateTime lastDate;
|
||||
|
||||
@Setter
|
||||
@Column(nullable = false)
|
||||
private double lastValue;
|
||||
|
||||
@Setter
|
||||
@Column(nullable = false)
|
||||
private int count;
|
||||
|
||||
@Setter
|
||||
@Column(nullable = false)
|
||||
private double min;
|
||||
|
||||
@Setter
|
||||
@Column(nullable = false)
|
||||
private double max;
|
||||
|
||||
@Setter
|
||||
@Column(nullable = false)
|
||||
private double mean;
|
||||
|
||||
@Setter
|
||||
@Column(nullable = false)
|
||||
private double variance;
|
||||
|
||||
public Measure(@NonNull final SeriesIntervalKey id, @NonNull final ZonedDateTime date, final double value) {
|
||||
this.id = id;
|
||||
this.firstDate = date;
|
||||
this.firstValue = value;
|
||||
this.lastDate = date;
|
||||
this.lastValue = value;
|
||||
this.count = 1;
|
||||
this.min = value;
|
||||
this.max = value;
|
||||
this.mean = value;
|
||||
this.variance = 0;
|
||||
}
|
||||
|
||||
public void update(@NonNull final MeasureEvent event) {
|
||||
final double x = event.getValue();
|
||||
|
||||
this.count++;
|
||||
this.lastDate = event.getDate();
|
||||
this.lastValue = x;
|
||||
this.min = Math.min(this.min, x);
|
||||
this.max = Math.max(this.max, x);
|
||||
|
||||
final double oldMean = this.mean;
|
||||
this.mean = oldMean + (x - oldMean) / this.count;
|
||||
this.variance = this.variance + (x - oldMean) * (x - this.mean) / this.count;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package de.ph87.data.series.measure;
|
||||
|
||||
import de.ph87.data.series.SeriesIntervalKey;
|
||||
import org.springframework.data.repository.ListCrudRepository;
|
||||
|
||||
public interface MeasureRepository extends ListCrudRepository<Measure, SeriesIntervalKey> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package de.ph87.data.series.measure;
|
||||
|
||||
import de.ph87.data.series.Series;
|
||||
import de.ph87.data.series.SeriesIntervalKey;
|
||||
import de.ph87.data.series.SeriesMode;
|
||||
import de.ph87.data.series.SeriesService;
|
||||
import de.ph87.data.series.consumption.unit.Unit;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class MeasureService {
|
||||
|
||||
private final MeasureRepository measureRepository;
|
||||
|
||||
private final SeriesService seriesService;
|
||||
|
||||
@EventListener(MeasureEvent.class)
|
||||
public void onMeasureEvent(@NonNull final MeasureEvent event) {
|
||||
final Series series = seriesService.getOrCreateByName(event.getName(), SeriesMode.MEASURE);
|
||||
for (final Unit unit : Unit.values()) {
|
||||
final SeriesIntervalKey id = new SeriesIntervalKey(series, unit, event.getDate());
|
||||
measureRepository.findById(id)
|
||||
.stream()
|
||||
.peek(existing -> existing.update(event))
|
||||
.findFirst()
|
||||
.orElseGet(() -> measureRepository.save(new Measure(id, event.getDate(), event.getValue())));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user