package info.peper.vz.rest; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.util.LinkedList; import java.util.List; import java.util.Locale; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.RequestParam; import org.springframework.web.bind.annotation.RestController; import info.peper.vz.rest.bo.Sums; import info.peper.vz.rest.bo.Sums2; import info.peper.vz.rest.bo.db.Aggregate; import info.peper.vz.rest.bo.db.Aggregate2; import info.peper.vz.rest.bo.db.Data; import info.peper.vz.rest.bo.db.EnergyPrice; @RestController class VzRestController { private static final Logger log = LoggerFactory.getLogger(VzRestController.class); private final AggregateRepository aggregateRep; @Autowired private JdbcTemplate jdbcTemplate; @Autowired private JdbcTemplate jdbcTemplateWithLimitedRows; public VzRestController(final AggregateRepository aggregateRep) { this.aggregateRep = aggregateRep; } @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 MIN(timestamp_start) AS min_timestamp_start, MAX(timestamp_end) AS max_timestamp_end, SUM(sum_positive) AS sum_positive, SUM(sum_negative) AS sum_negative FROM volkszaehler.tobias_aggregate WHERE channel_id=? AND timestamp_start>=? AND timestamp_end<=?;", (rs, rowNum) -> new Aggregate(channelId, rs.getLong("min_timestamp_start"), rs.getLong("max_timestamp_end"), rs.getLong("sum_positive"), rs.getLong("sum_negative")), channelId ,timestampStart, timestampEnd); if (aggregates.size() != 1) { throw new RuntimeException("Interal error in SQL query."); } final Aggregate aggregate = aggregates.iterator().next(); double sumPos = aggregate.getSumPositive(); double sumNeg = aggregate.getSumNegative(); log.debug("sumPos: " + sumPos + " / sumNeg: " + sumNeg); log.debug("timestampStart: " + timestampStart); log.debug("timestampEnd: " + timestampEnd); log.debug("firstTimestamp: " + aggregate.getTimestampStart()); log.debug("lastTimestamp: " + aggregate.getTimestampEnd()); if (timestampStart < aggregate.getTimestampStart() ) { final Sums startSums = getSums(timestampStart, aggregate.getTimestampStart(), channelId); sumPos += startSums.getSumPositive(); sumNeg += startSums.getSumNegative(); log.debug("Start: " + startSums.toString()); } if (timestampEnd > aggregate.getTimestampEnd()) { final Sums endSums = getSums(aggregate.getTimestampEnd(), timestampEnd, channelId); sumPos += endSums.getSumPositive(); sumNeg += endSums.getSumNegative(); log.debug("End: " + endSums.toString()); } sumPos /= 3600; sumNeg /= 3600; return new Sums(Math.round(sumPos), Math.round(sumNeg)); } @GetMapping("/data/sums2") Sums2 getData2(@RequestParam("timestampStart")final long timestampStart, @RequestParam("timestampEnd")final long timestampEnd, @RequestParam("houseId")final int houseId) { final List aggregates = jdbcTemplate.query( "SELECT MIN(timestamp_start) AS min_ts_start, MAX(timestamp_end) AS max_ts_end, SUM(produced_energy)/3600 AS sum_produced, SUM(obtained_energy)/3600 AS sum_obtained, SUM(injected_energy)/3600 AS sum_injected FROM volkszaehler.tobias_aggregate2 WHERE house_id=? AND timestamp_start>=? AND timestamp_end<=?;", (rs, rowNum) -> new Aggregate2(houseId, rs.getLong("min_ts_start"), rs.getLong("max_ts_end"), rs.getLong("sum_produced"), rs.getLong("sum_obtained"), rs.getLong("sum_injected")), houseId ,timestampStart, timestampEnd); if (aggregates.size() != 1) { throw new RuntimeException("Interal error in SQL query."); } final Aggregate2 aggregate = aggregates.iterator().next(); log.debug("energyProduced: " + aggregate.getProducedEnergy() + " / energyObtained: " + aggregate.getObtainedEnergy() + " / injectedEnergy: " + aggregate.getInjectedEnergy()); log.debug("timestampStart: " + timestampStart); log.debug("timestampEnd: " + timestampEnd); log.debug("firstTimestamp: " + aggregate.getTimestampStart()); log.debug("lastTimestamp: " + aggregate.getTimestampEnd()); return new Sums2( Math.round(aggregate.getInjectedEnergy()), Math.round(aggregate.getObtainedEnergy()), Math.round(aggregate.getProducedEnergy())); } @GetMapping("/data/sumsWithFactor") Sums getDataWithFactor(@RequestParam("timestampStart")final long timestampStart, @RequestParam("timestampEnd")final long timestampEnd, @RequestParam("channelId")final int channelId) { // this.jdbcTemplateWithLimitedRows.setMaxRows(1000); 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 firstTimestamp = Long.MAX_VALUE; long lastTimestamp = Long.MIN_VALUE; double sumPos = 0; double sumNeg = 0; log.debug("Number of aggregates: " + aggregates.size()); for (Aggregate ag : aggregates) { if (ag.getTimestampStart() < firstTimestamp) { firstTimestamp = ag.getTimestampStart(); } if (ag.getTimestampEnd() > lastTimestamp) { lastTimestamp = ag.getTimestampEnd(); } sumPos += ag.getSumPositive(); sumNeg += ag.getSumNegative(); } log.debug("sumPos: " + sumPos + " / sumNeg: " + sumNeg); log.debug("timestampStart: " + timestampStart); log.debug("timestampEnd: " + timestampEnd); log.debug("firstTimestamp: " + firstTimestamp); log.debug("lastTimestamp: " + lastTimestamp); if (timestampStart < firstTimestamp ) { final Sums startSums = getSums(timestampStart, firstTimestamp, channelId); sumPos += startSums.getSumPositive(); sumNeg += startSums.getSumNegative(); log.debug("Start: " + startSums.toString()); } if (timestampEnd > lastTimestamp) { final Sums endSums = getSums(lastTimestamp, timestampEnd, channelId); sumPos += endSums.getSumPositive(); sumNeg += endSums.getSumNegative(); log.debug("End: " + endSums.toString()); } sumPos /= 3600; sumNeg /= 3600; return new Sums(Math.round(sumPos), Math.round(sumNeg)); } @GetMapping("/data/sumsWithoutAggregate") Sums getDataWithoutAggregate(@RequestParam("timestampStart")final long timestampStart, @RequestParam("timestampEnd")final long timestampEnd, @RequestParam("channelId")final int channelId) { final Sums startSums = getSums(timestampStart, timestampEnd, channelId); final Sums returnSums = new Sums(Math.round((double)startSums.getSumPositive()/3600), Math.round((double)startSums.getSumNegative()/3600)); return returnSums; } @GetMapping("/data/prices") List getPrices(@RequestParam("timestampStart")final long timestampStart, @RequestParam("timestampEnd")final long timestampEnd, @RequestParam("houseId")final int houseId) { final List prices = jdbcTemplate.query( "SELECT * FROM tobias_energy_price WHERE house_id=? AND timestamp_start < ? AND timestamp_end >= ? ORDER BY timestamp_start;", (rs, rowNum) -> new EnergyPrice(rs.getInt("house_id"), rs.getLong("timestamp_start"), rs.getLong("timestamp_end"), rs.getFloat("price")), houseId ,timestampEnd, timestampStart); return prices; } @GetMapping("/data/summary") String getSummary(@RequestParam("timestampStart")final long timestampStart, @RequestParam("timestampEnd")final long timestampEnd, @RequestParam("houseId")final int houseId, @RequestParam("channelIdMeter")final int channelIdMeter, @RequestParam("channelIdSolar")final int channelIdSolar) { final NumberFormat formatCurrency = new DecimalFormat("#,##0.00", new DecimalFormatSymbols(Locale.GERMAN)); final NumberFormat formatPercent = new DecimalFormat("#,##0.00", new DecimalFormatSymbols(Locale.GERMAN)); final NumberFormat formatEnergy = new DecimalFormat("#,##0", new DecimalFormatSymbols(Locale.GERMAN)); final List prices = this.getPrices(timestampStart, timestampEnd, houseId); final List partsMeter = new LinkedList(); final List partsSolar = new LinkedList(); float savedMoney = 0; long totalSolar = 0; long totalObtained = 0; long totalInjected = 0; for (EnergyPrice price : prices) { final long tsStart = Math.max(price.getTimestampStart(), timestampStart); final long tsEnd = Math.min(price.getTimestampEnd(), timestampEnd); final Sums sumsMeter = getData(tsStart, tsEnd, channelIdMeter); final Sums sumsSolar = getData(tsStart, tsEnd, channelIdSolar); savedMoney += (float)(sumsSolar.getSumPositive() - sumsMeter.getSumNegative())/1000000 * price.getPrice(); totalSolar += sumsSolar.getSumPositive(); totalObtained += sumsMeter.getSumPositive(); totalInjected += sumsMeter.getSumNegative(); partsMeter.add(sumsMeter); partsSolar.add(sumsSolar); } final StringBuilder sb = new StringBuilder(); sb.append("Erzeugter Strom von der Photovoltaik: " + formatEnergy.format(totalSolar/1000000) + " kWh\n"); sb.append("Eingespeister Strom von der Photovoltaik: " + formatEnergy.format(totalInjected/1000000) + " kWh\n"); sb.append("Genutzter Strom von der Photovoltaik: " + formatEnergy.format((totalSolar-totalInjected)/1000000) + " kWh (="); sb.append(formatPercent.format((float)(totalSolar-totalInjected)/(float)totalSolar*100) + " %)\n"); sb.append("Bezogener Strom: " + formatEnergy.format(totalObtained/1000000) + " kWh\n"); sb.append("Autakie: " + formatPercent.format((float)(totalSolar-totalInjected)/(float)(totalObtained+totalSolar-totalInjected)*100) + " %\n"); sb.append("Eingespartes Geld: " + formatCurrency.format(savedMoney) + "€\n"); return sb.toString(); } @GetMapping("/data/summary2") String getSummary2(@RequestParam(name = "timestampStart", defaultValue = "-1")long timestampStart, @RequestParam(name = "timestampEnd", defaultValue = "-1")long timestampEnd, @RequestParam("houseId")final int houseId) { if (timestampStart == -1) { timestampStart = jdbcTemplate.queryForObject("SELECT MIN(timestamp_start) AS ts FROM tobias_aggregate2 WHERE house_id=?;", Long.class, houseId); } if (timestampEnd == -1) { timestampEnd = jdbcTemplate.queryForObject("SELECT MAX(timestamp_end) AS ts FROM tobias_aggregate2 WHERE house_id=?;", Long.class, houseId); } final NumberFormat formatCurrency = new DecimalFormat("#,##0.00", new DecimalFormatSymbols(Locale.GERMAN)); final NumberFormat formatPercent = new DecimalFormat("#,##0.00", new DecimalFormatSymbols(Locale.GERMAN)); final NumberFormat formatEnergy = new DecimalFormat("#,##0", new DecimalFormatSymbols(Locale.GERMAN)); final List prices = this.getPrices(timestampStart, timestampEnd, houseId); float savedMoney = 0; long totalProduced = 0; long totalObtained = 0; long totalInjected = 0; for (EnergyPrice price : prices) { final long tsStart = Math.max(price.getTimestampStart(), timestampStart); final long tsEnd = Math.min(price.getTimestampEnd(), timestampEnd); final Sums2 sums = getData2(tsStart, tsEnd, houseId); savedMoney += (float)(sums.getProduced() - sums.getInjected())/1000000 * price.getPrice(); totalProduced += sums.getProduced(); totalObtained += sums.getObtained(); totalInjected += sums.getInjected(); } final StringBuilder sb = new StringBuilder(); sb.append("Erzeugter Strom von der Photovoltaik: " + formatEnergy.format(totalProduced/1000000) + " kWh\n"); sb.append("Eingespeister Strom von der Photovoltaik: " + formatEnergy.format(totalInjected/1000000) + " kWh\n"); sb.append("Genutzter Strom von der Photovoltaik: " + formatEnergy.format((totalProduced-totalInjected)/1000000) + " kWh (="); sb.append(formatPercent.format((float)(totalProduced-totalInjected)/(float)totalProduced*100) + " %)\n"); sb.append("Bezogener Strom: " + formatEnergy.format(totalObtained/1000000) + " kWh\n"); sb.append("Autakie: " + formatPercent.format((float)(totalProduced-totalInjected)/(float)(totalObtained+totalProduced-totalInjected)*100) + " %\n"); sb.append("Eingespartes Geld: " + formatCurrency.format(savedMoney) + "€\n"); return sb.toString(); } private Sums getSums(final long startTimestamp, final long endTimestamp, final int channelId) { long currentTimestamp = startTimestamp; double wattMillisecondsPos = 0; double wattMillisecondsNeg = 0; while (currentTimestamp < endTimestamp) { final List datas = jdbcTemplateWithLimitedRows.query("SELECT * FROM volkszaehler.data WHERE channel_id=? AND timestamp>? ORDER BY timestamp;", (rs, rowNum) -> new Data(rs.getInt("channel_id"), rs.getLong("timestamp"), rs.getDouble("value")), channelId, currentTimestamp); for (Data data : datas) { if (currentTimestamp <= endTimestamp) { final long tsDiff = Math.min(data.getTimestamp() - currentTimestamp, endTimestamp - currentTimestamp); currentTimestamp = data.getTimestamp(); if (data.getValue() > 1.0) { wattMillisecondsPos += (data.getValue() * tsDiff); } if (data.getValue() < 0.0) { wattMillisecondsNeg += (-data.getValue() * tsDiff); } } } } return new Sums(Math.round(wattMillisecondsPos), Math.round(wattMillisecondsNeg)); } }