diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..e9441bb
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding/=UTF-8
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index eeac0e7..5e4ec05 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -1,4 +1,5 @@
eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.methodParameters=generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
org.eclipse.jdt.core.compiler.compliance=17
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
diff --git a/pom.xml b/pom.xml
index 62c77e4..b7cb7b8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,6 +2,12 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.4.3
+
info.peper
rest-vz
0.0.1-SNAPSHOT
@@ -13,7 +19,15 @@
org.mariadb.jdbc
mariadb-java-client
- 3.5.2
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
\ No newline at end of file
diff --git a/src/main/java/info/peper/vz/rest/Aggregate.java b/src/main/java/info/peper/vz/rest/Aggregate.java
new file mode 100644
index 0000000..c64b80c
--- /dev/null
+++ b/src/main/java/info/peper/vz/rest/Aggregate.java
@@ -0,0 +1,72 @@
+package info.peper.vz.rest;
+
+import java.io.Serializable;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.IdClass;
+
+@Entity(name = "tobias_aggregate_spring")
+@IdClass(Aggregate.CompositeKey.class)
+class Aggregate {
+ static class CompositeKey implements Serializable {
+ private static final long serialVersionUID = 3097284483123288289L;
+ private int channelId;
+ private long timestampStart;
+ private long timestampEnd;
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + channelId;
+ result = prime * result + (int) (timestampEnd ^ (timestampEnd >>> 32));
+ result = prime * result + (int) (timestampStart ^ (timestampStart >>> 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 (timestampEnd != other.timestampEnd)
+ return false;
+ if (timestampStart != other.timestampStart)
+ return false;
+ return true;
+ }
+ }
+
+ Aggregate() {
+ super();
+ }
+
+ Aggregate(int channelId, long timestampStart, long timestampEnd, long sumPositive, long sumNegative) {
+ super();
+ this.channelId = channelId;
+ this.timestampStart = timestampStart;
+ this.timestampEnd = timestampEnd;
+ this.sumPositive = sumPositive;
+ this.sumNegative = sumNegative;
+ }
+ @Id
+ @Column(name="channel_id")
+ private int channelId;
+ @Id
+ @Column(name="timestamp_start")
+ private long timestampStart;
+ @Id
+ @Column(name="timestamp_end")
+ private long timestampEnd;
+ @Column(name="sum_positive")
+ private long sumPositive;
+ @Column(name="sum_negative")
+ private long sumNegative;
+}
diff --git a/src/main/java/info/peper/vz/rest/AggregateRepository.java b/src/main/java/info/peper/vz/rest/AggregateRepository.java
new file mode 100644
index 0000000..94cf195
--- /dev/null
+++ b/src/main/java/info/peper/vz/rest/AggregateRepository.java
@@ -0,0 +1,7 @@
+package info.peper.vz.rest;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+interface AggregateRepository extends JpaRepository {
+
+}
diff --git a/src/main/java/info/peper/vz/rest/LoadDatabase.java b/src/main/java/info/peper/vz/rest/LoadDatabase.java
new file mode 100644
index 0000000..b5b9427
--- /dev/null
+++ b/src/main/java/info/peper/vz/rest/LoadDatabase.java
@@ -0,0 +1,79 @@
+package info.peper.vz.rest;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalAccessor;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+@Configuration
+class LoadDatabase {
+
+ private static final DateTimeFormatter DTF = DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(ZoneId.systemDefault());
+ private static final int[] CHANNELS_TO_USE = new int[] {1, 4, };
+ private static final Logger LOG = LoggerFactory.getLogger(LoadDatabase.class);
+
+ static class Data {
+ final int channelId;
+ final long timestamp;
+ final int value;
+
+ Data(int channelId, long timestamp, int value) {
+ super();
+ this.channelId = channelId;
+ this.timestamp = timestamp;
+ this.value = value;
+ }
+
+ }
+
+ @Autowired
+ JdbcTemplate jdbcTemplate;
+
+ @Bean
+ CommandLineRunner initDatabase(AggregateRepository repository) {
+
+ 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));
+ };
+ }
+
+ long getTimestamp(final String dateTime) {
+ final TemporalAccessor tempAccessor = DTF.parse(dateTime);
+ final Instant instant = Instant.from(tempAccessor);
+ return Instant.EPOCH.until(instant, ChronoUnit.MILLIS);
+ }
+
+}
diff --git a/src/main/java/info/peper/vz/rest/ReadDbMain.java b/src/main/java/info/peper/vz/rest/ReadDbMain.java
index 83168c2..feb4cfe 100644
--- a/src/main/java/info/peper/vz/rest/ReadDbMain.java
+++ b/src/main/java/info/peper/vz/rest/ReadDbMain.java
@@ -19,28 +19,34 @@ import java.time.temporal.TemporalAccessor;
public class ReadDbMain {
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("2023-01-01T00:00:00");
+ long startTimeStamp = getTimestamp("2022-06-01T00:00:00");
long endTimeStamp = startTimeStamp + (24*60*60*1000);
- final long finalEndTimeStamp = getTimestamp("2025-02-23T00:00:00");
+ final long finalEndTimeStamp = getTimestamp("2025-02-24T00:00:00");
while (endTimeStamp < finalEndTimeStamp) {
- 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]
- );
+ 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);
+ }
+// 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;
}
@@ -60,6 +66,22 @@ public class ReadDbMain {
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,
@@ -70,7 +92,7 @@ public class ReadDbMain {
long wattMillisecondsNeg = 0;
stmt.setInt(1, channelId);
stmt.setLong(2, startTimestamp);
- stmt.setLong(3, endTimestamp + 1*60*1000);
+ 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");
diff --git a/src/main/java/info/peper/vz/rest/VzRestApplication.java b/src/main/java/info/peper/vz/rest/VzRestApplication.java
new file mode 100644
index 0000000..e2c7988
--- /dev/null
+++ b/src/main/java/info/peper/vz/rest/VzRestApplication.java
@@ -0,0 +1,12 @@
+package info.peper.vz.rest;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class VzRestApplication {
+
+ public static void main(String... args) {
+ SpringApplication.run(VzRestApplication.class, args);
+ }
+}
diff --git a/src/main/java/info/peper/vz/rest/VzRestController.java b/src/main/java/info/peper/vz/rest/VzRestController.java
new file mode 100644
index 0000000..98db49d
--- /dev/null
+++ b/src/main/java/info/peper/vz/rest/VzRestController.java
@@ -0,0 +1,29 @@
+package info.peper.vz.rest;
+
+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.RestController;
+
+@RestController
+class VzRestController {
+
+ private final AggregateRepository repository;
+
+ public VzRestController(final AggregateRepository repository) {
+ this.repository = repository;
+ }
+
+ @GetMapping("/test/{name}")
+ String hello(@PathVariable("name")final String name) {
+ return "Hello " + name + "!";
+ }
+
+ @PostMapping("/test")
+ Aggregate newAggregate(@RequestBody final Aggregate aggregate) {
+ return repository.save(aggregate);
+ }
+
+
+}