diff --git a/src/main/java/info/peper/vz/rest/AggregateRepository.java b/src/main/java/info/peper/vz/rest/AggregateRepository.java index 94cf195..84d46c0 100644 --- a/src/main/java/info/peper/vz/rest/AggregateRepository.java +++ b/src/main/java/info/peper/vz/rest/AggregateRepository.java @@ -2,6 +2,8 @@ package info.peper.vz.rest; import org.springframework.data.jpa.repository.JpaRepository; +import info.peper.vz.rest.bo.db.Aggregate; + interface AggregateRepository extends JpaRepository { } diff --git a/src/main/java/info/peper/vz/rest/DataRepository.java b/src/main/java/info/peper/vz/rest/DataRepository.java new file mode 100644 index 0000000..edf7200 --- /dev/null +++ b/src/main/java/info/peper/vz/rest/DataRepository.java @@ -0,0 +1,9 @@ +package info.peper.vz.rest; + +import org.springframework.data.jpa.repository.JpaRepository; + +import info.peper.vz.rest.bo.db.Data; + +interface DataRepository extends JpaRepository { + +} diff --git a/src/main/java/info/peper/vz/rest/FillAggregateTableMain.java b/src/main/java/info/peper/vz/rest/FillAggregateTableMain.java new file mode 100644 index 0000000..78e8e8a --- /dev/null +++ b/src/main/java/info/peper/vz/rest/FillAggregateTableMain.java @@ -0,0 +1,104 @@ +package info.peper.vz.rest; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAccessor; + +public class FillAggregateTableMain { + + private static final DateTimeFormatter DTF = DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(ZoneId.systemDefault()); + private static final int[] CHANNELS_TO_USE = new int[] {1, 4, }; + + public static void main(String[] args) throws Exception { + try (final Connection con = DriverManager.getConnection( + "jdbc:mariadb://mariadb.fritz.box/volkszaehler", + "vz", + getPassword())) { + long startTimeStamp = getTimestamp("2022-06-01T00:00:00"); + long endTimeStamp = startTimeStamp + (24*60*60*1000); + final long finalEndTimeStamp = getTimestamp("2025-02-24T00:00:00"); + while (endTimeStamp < finalEndTimeStamp) { + for (int channelId : CHANNELS_TO_USE) { + final long[] values = getValues(con, startTimeStamp, endTimeStamp, channelId); + saveValues(con, startTimeStamp, endTimeStamp, channelId, values); + System.out.println(DTF.format(Instant.ofEpochMilli(startTimeStamp)) + ": " + channelId); + } + startTimeStamp += 24*60*60*1000; + endTimeStamp += 24*60*60*1000; + } + } + } + + private static String getPassword() throws IOException { + try (final InputStream is = new FileInputStream("db-password.txt")) { + final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is)); + return bufferedReader.readLine(); + } + } + + private static long getTimestamp(final String dateTime) { + final TemporalAccessor tempAccessor = DTF.parse(dateTime); + final Instant instant = Instant.from(tempAccessor); + return Instant.EPOCH.until(instant, ChronoUnit.MILLIS); + } + + private static void saveValues(final Connection con, + final long startTimestamp, + final long endTimestamp, + final int channelId, + final long[] values) throws SQLException { + try (final PreparedStatement stmt = con.prepareStatement("INSERT INTO tobias_aggregate (channel_id, timestamp_start, timestamp_end, sum_positive, sum_negative) VALUES (?, ?, ?, ?, ?)")) { + stmt.setInt(1, channelId); + stmt.setLong(2, startTimestamp); + stmt.setLong(3, endTimestamp); + stmt.setLong(4, values[0]); + stmt.setLong(5, values[1]); + stmt.execute(); + } + } + + private static long[] getValues(final Connection con, + final long startTimestamp, + final long endTimestamp, + final int channelId) throws SQLException { + long currentTimestamp = startTimestamp; + long wattMillisecondsPos = 0; + long wattMillisecondsNeg = 0; + + while (currentTimestamp < endTimestamp) { + try (final PreparedStatement stmt = con.prepareStatement("SELECT * FROM volkszaehler.data WHERE channel_id=? AND timestamp>? ORDER BY timestamp;")) { + stmt.setMaxRows(10000); + stmt.setInt(1, channelId); + stmt.setLong(2, currentTimestamp); + try (final ResultSet rs = stmt.executeQuery()) { + while (rs.next() && currentTimestamp <= endTimestamp) { + final long rsTimestamp = rs.getLong("timestamp"); + final long rsValue = rs.getLong("value"); + final long tsDiff = rsTimestamp - Math.min(endTimestamp, currentTimestamp); + currentTimestamp = rsTimestamp; + if (rsValue > 0) { + wattMillisecondsPos += (rsValue * tsDiff); + } else if (rsValue < 0) { + wattMillisecondsNeg += (-rsValue * tsDiff); + } + } + } + } + } + return new long[] {wattMillisecondsPos/3600, wattMillisecondsNeg/3600}; + + } + +} diff --git a/src/main/java/info/peper/vz/rest/LoadDatabase.java b/src/main/java/info/peper/vz/rest/LoadDatabase.java index b5b9427..fdb4a8d 100644 --- a/src/main/java/info/peper/vz/rest/LoadDatabase.java +++ b/src/main/java/info/peper/vz/rest/LoadDatabase.java @@ -44,29 +44,29 @@ class LoadDatabase { return args -> { LOG.info("################ jdbcTemplate: " + jdbcTemplate.getClass().getName()); - final long startTimestamp = getTimestamp("2022-06-01T00:00:00"); - final long endTimestamp = getTimestamp("2022-06-02T00:00:00"); - final List data = jdbcTemplate.query( - "SELECT * FROM volkszaehler.data WHERE channel_id=? AND timestamp>? AND timestamp<=? ORDER BY timestamp;", - (rs, rowNum) -> new Data(rs.getInt("channel_id"), rs.getLong("timestamp"), rs.getInt("value")), - 1 ,startTimestamp, endTimestamp); - - LOG.info("########## Count: " + data.size()); - long currentTimestamp = startTimestamp; - long wattMillisecondsPos = 0; - long wattMillisecondsNeg = 0; - for (Data d : data) { - final long rsTimestamp = d.timestamp; - final long rsValue = d.value; - final long tsDiff = rsTimestamp - Math.min(endTimestamp, currentTimestamp); - currentTimestamp = rsTimestamp; - if (rsValue > 0) { - wattMillisecondsPos += (rsValue * tsDiff); - } else if (rsValue < 0) { - wattMillisecondsNeg += (-rsValue * tsDiff); - } - } - repository.save(new Aggregate(1, startTimestamp, endTimestamp, wattMillisecondsPos, wattMillisecondsNeg)); +// final long startTimestamp = getTimestamp("2022-06-01T00:00:00"); +// final long endTimestamp = getTimestamp("2022-06-02T00:00:00"); +// final List data = jdbcTemplate.query( +// "SELECT * FROM volkszaehler.data WHERE channel_id=? AND timestamp>? AND timestamp<=? ORDER BY timestamp;", +// (rs, rowNum) -> new Data(rs.getInt("channel_id"), rs.getLong("timestamp"), rs.getInt("value")), +// 1 ,startTimestamp, endTimestamp); +// +// LOG.info("########## Count: " + data.size()); +// long currentTimestamp = startTimestamp; +// long wattMillisecondsPos = 0; +// long wattMillisecondsNeg = 0; +// for (Data d : data) { +// final long rsTimestamp = d.timestamp; +// final long rsValue = d.value; +// final long tsDiff = rsTimestamp - Math.min(endTimestamp, currentTimestamp); +// currentTimestamp = rsTimestamp; +// if (rsValue > 0) { +// wattMillisecondsPos += (rsValue * tsDiff); +// } else if (rsValue < 0) { +// wattMillisecondsNeg += (-rsValue * tsDiff); +// } +// } +// repository.save(new Aggregate(1, startTimestamp, endTimestamp, wattMillisecondsPos, wattMillisecondsNeg)); }; } diff --git a/src/main/java/info/peper/vz/rest/ReadDbMain.java b/src/main/java/info/peper/vz/rest/ReadDbMain.java index feb4cfe..420c24a 100644 --- a/src/main/java/info/peper/vz/rest/ReadDbMain.java +++ b/src/main/java/info/peper/vz/rest/ReadDbMain.java @@ -10,6 +10,7 @@ import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; @@ -22,6 +23,24 @@ public class ReadDbMain { private static final int[] CHANNELS_TO_USE = new int[] {1, 4, }; public static void main(String[] args) throws Exception { + try (final Connection con = DriverManager.getConnection( + "jdbc:mariadb://mariadb.fritz.box/volkszaehler", + "vz", + getPassword()); + final Statement stmt = con.createStatement();) { + stmt.setFetchSize(10); + stmt.setMaxRows(24*60*60); + try (final ResultSet rs = stmt.executeQuery("SELECT * FROM data WHERE channel_id=1 AND timestamp>1656021600000");) { + for (int i = 0; i < 50000; i++) { + if (rs.next()) { + rs.getDouble("value"); + } + } + } + } + } + + public static void oldMain(String[] args) throws Exception { try (final Connection con = DriverManager.getConnection( "jdbc:mariadb://mariadb.fritz.box/volkszaehler", "vz", @@ -35,24 +54,12 @@ public class ReadDbMain { saveValues(con, startTimeStamp, endTimeStamp, channelId, values); System.out.println(DTF.format(Instant.ofEpochMilli(startTimeStamp)) + ": " + channelId); } -// final long[] zaehler = getValues(con, startTimeStamp, endTimeStamp, 1); -// final long[] solar = getValues(con, startTimeStamp, endTimeStamp, 4); -// System.out.println(DTF.format(Instant.ofEpochMilli(startTimeStamp)) + -// "\t" + -// DTF.format(Instant.ofEpochMilli(endTimeStamp)) + -// "\t" + -// zaehler[0] + -// "\t" + -// zaehler[1] + -// "\t" + -// solar[0] -// ); startTimeStamp += 24*60*60*1000; endTimeStamp += 24*60*60*1000; } } } - + private static String getPassword() throws IOException { try (final InputStream is = new FileInputStream("db-password.txt")) { final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is)); @@ -81,33 +88,35 @@ public class ReadDbMain { } } - private static long[] getValues(final Connection con, final long startTimestamp, final long endTimestamp, final int channelId) throws SQLException { - try (final PreparedStatement stmt = con.prepareStatement("SELECT * FROM volkszaehler.data WHERE channel_id=? AND timestamp>? AND timestamp<=? ORDER BY timestamp;")) { - long currentTimestamp = startTimestamp; - long wattMillisecondsPos = 0; - long wattMillisecondsNeg = 0; - stmt.setInt(1, channelId); - stmt.setLong(2, startTimestamp); - stmt.setLong(3, endTimestamp + 24*60*60*1000); - try (final ResultSet rs = stmt.executeQuery()) { - while (rs.next() && currentTimestamp <= endTimestamp) { - final long rsTimestamp = rs.getLong("timestamp"); - final long rsValue = rs.getLong("value"); - final long tsDiff = rsTimestamp - Math.min(endTimestamp, currentTimestamp); - currentTimestamp = rsTimestamp; - if (rsValue > 0) { - wattMillisecondsPos += (rsValue * tsDiff); - } else if (rsValue < 0) { - wattMillisecondsNeg += (-rsValue * tsDiff); + long currentTimestamp = startTimestamp; + long wattMillisecondsPos = 0; + long wattMillisecondsNeg = 0; + + while (currentTimestamp < endTimestamp) { + try (final PreparedStatement stmt = con.prepareStatement("SELECT * FROM volkszaehler.data WHERE channel_id=? AND timestamp>?ORDER BY timestamp;")) { + stmt.setMaxRows(10000); + stmt.setInt(1, channelId); + stmt.setLong(2, currentTimestamp); + try (final ResultSet rs = stmt.executeQuery()) { + while (rs.next() && currentTimestamp <= endTimestamp) { + final long rsTimestamp = rs.getLong("timestamp"); + final long rsValue = rs.getLong("value"); + final long tsDiff = rsTimestamp - Math.min(endTimestamp, currentTimestamp); + currentTimestamp = rsTimestamp; + if (rsValue > 0) { + wattMillisecondsPos += (rsValue * tsDiff); + } else if (rsValue < 0) { + wattMillisecondsNeg += (-rsValue * tsDiff); + } } } } - return new long[] {wattMillisecondsPos/3600, wattMillisecondsNeg/3600}; } + return new long[] {wattMillisecondsPos/3600, wattMillisecondsNeg/3600}; } diff --git a/src/main/java/info/peper/vz/rest/VzRestController.java b/src/main/java/info/peper/vz/rest/VzRestController.java index 98db49d..b37d3ec 100644 --- a/src/main/java/info/peper/vz/rest/VzRestController.java +++ b/src/main/java/info/peper/vz/rest/VzRestController.java @@ -1,18 +1,49 @@ package info.peper.vz.rest; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import info.peper.vz.rest.bo.Sums; +import info.peper.vz.rest.bo.db.Aggregate; + @RestController class VzRestController { - private final AggregateRepository repository; + private final AggregateRepository aggregateRep; + private final DataRepository dataRep; + + @Autowired + private JdbcTemplate jdbcTemplate; - public VzRestController(final AggregateRepository repository) { - this.repository = repository; + public VzRestController(final AggregateRepository aggregateRep, final DataRepository dataRep) { + this.aggregateRep = aggregateRep; + this.dataRep = dataRep; + } + + @GetMapping("/data/sums") + Sums getData(@RequestParam("timestampStart")final long timestampStart, + @RequestParam("timestampEnd")final long timestampEnd, + @RequestParam("channelId")final int channelId) { + final List aggregates = jdbcTemplate.query( + "SELECT * FROM volkszaehler.tobias_aggregate WHERE channel_id=? AND timestamp_start>=? AND timestamp_end<=? ORDER BY timestamp_start;", + (rs, rowNum) -> new Aggregate(rs.getInt("channel_id"), rs.getLong("timestamp_start"), rs.getLong("timestamp_end"), + rs.getLong("sum_positive"), rs.getLong("sum_negative")), + channelId ,timestampStart, timestampEnd); + long sumPos = 0; + long sumNeg = 0; + for (Aggregate ag : aggregates) { + sumPos += ag.getSumPositive(); + sumNeg += ag.getSumNegative(); + } + return new Sums(sumPos, sumNeg); } @GetMapping("/test/{name}") @@ -22,7 +53,7 @@ class VzRestController { @PostMapping("/test") Aggregate newAggregate(@RequestBody final Aggregate aggregate) { - return repository.save(aggregate); + return aggregateRep.save(aggregate); } diff --git a/src/main/java/info/peper/vz/rest/bo/Sums.java b/src/main/java/info/peper/vz/rest/bo/Sums.java new file mode 100644 index 0000000..df7a4a9 --- /dev/null +++ b/src/main/java/info/peper/vz/rest/bo/Sums.java @@ -0,0 +1,45 @@ +package info.peper.vz.rest.bo; + +import java.io.Serializable; + +public class Sums implements Serializable { + private static final long serialVersionUID = -1816023197422851264L; + private final long sumPositive; + private final long sumNegative; + public Sums(long sumPositive, long sumNegative) { + super(); + this.sumPositive = sumPositive; + this.sumNegative = sumNegative; + } + public long getSumPositive() { + return sumPositive; + } + public long getSumNegative() { + return sumNegative; + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (sumNegative ^ (sumNegative >>> 32)); + result = prime * result + (int) (sumPositive ^ (sumPositive >>> 32)); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Sums other = (Sums) obj; + if (sumNegative != other.sumNegative) + return false; + if (sumPositive != other.sumPositive) + return false; + return true; + } + + +} diff --git a/src/main/java/info/peper/vz/rest/Aggregate.java b/src/main/java/info/peper/vz/rest/bo/db/Aggregate.java similarity index 61% rename from src/main/java/info/peper/vz/rest/Aggregate.java rename to src/main/java/info/peper/vz/rest/bo/db/Aggregate.java index c64b80c..11ae68e 100644 --- a/src/main/java/info/peper/vz/rest/Aggregate.java +++ b/src/main/java/info/peper/vz/rest/bo/db/Aggregate.java @@ -1,4 +1,4 @@ -package info.peper.vz.rest; +package info.peper.vz.rest.bo.db; import java.io.Serializable; @@ -7,10 +7,10 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.IdClass; -@Entity(name = "tobias_aggregate_spring") +@Entity(name = "tobias_aggregate") @IdClass(Aggregate.CompositeKey.class) -class Aggregate { - static class CompositeKey implements Serializable { +public class Aggregate { + public static class CompositeKey implements Serializable { private static final long serialVersionUID = 3097284483123288289L; private int channelId; private long timestampStart; @@ -48,7 +48,7 @@ class Aggregate { super(); } - Aggregate(int channelId, long timestampStart, long timestampEnd, long sumPositive, long sumNegative) { + public Aggregate(int channelId, long timestampStart, long timestampEnd, long sumPositive, long sumNegative) { super(); this.channelId = channelId; this.timestampStart = timestampStart; @@ -69,4 +69,43 @@ class Aggregate { private long sumPositive; @Column(name="sum_negative") private long sumNegative; + public int getChannelId() { + return channelId; + } + + public void setChannelId(int channelId) { + this.channelId = channelId; + } + + public long getTimestampStart() { + return timestampStart; + } + + public void setTimestampStart(long timestampStart) { + this.timestampStart = timestampStart; + } + + public long getTimestampEnd() { + return timestampEnd; + } + + public void setTimestampEnd(long timestampEnd) { + this.timestampEnd = timestampEnd; + } + + public long getSumPositive() { + return sumPositive; + } + + public void setSumPositive(long sumPositive) { + this.sumPositive = sumPositive; + } + + public long getSumNegative() { + return sumNegative; + } + + public void setSumNegative(long sumNegative) { + this.sumNegative = sumNegative; + } } diff --git a/src/main/java/info/peper/vz/rest/bo/db/Data.java b/src/main/java/info/peper/vz/rest/bo/db/Data.java new file mode 100644 index 0000000..87ab8c6 --- /dev/null +++ b/src/main/java/info/peper/vz/rest/bo/db/Data.java @@ -0,0 +1,52 @@ +package info.peper.vz.rest.bo.db; + +import java.io.Serializable; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; + +@Entity(name = "data") +@IdClass(Data.CompositeKey.class) +public class Data { + public static class CompositeKey implements Serializable { + private static final long serialVersionUID = -3486278368164696822L; + private int channelId; + private long timestamp; + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + channelId; + result = prime * result + (int) (timestamp ^ (timestamp >>> 32)); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CompositeKey other = (CompositeKey) obj; + if (channelId != other.channelId) + return false; + if (timestamp != other.timestamp) + return false; + return true; + } + } + + @Id + @Column(name="channel_id") + private int channelId; + @Id + @Column(name="timestamp") + private long timestamp; + @Column(name="value") + private double value; + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..dc813d2 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,5 @@ +spring.jpa.hibernate.ddl-auto=update +spring.datasource.url=jdbc:mariadb://mariadb.fritz.box:3306/volkszaehler +spring.datasource.username=vz +spring.datasource.password=2pG7GjQajemwY3f9 +spring.jpa.show-sql: true \ No newline at end of file