From 2b65198df7c110d8632865da53ca226d8eb9db8b Mon Sep 17 00:00:00 2001 From: tommi27 Date: Mon, 16 Mar 2020 17:01:21 +0100 Subject: [PATCH 1/2] updated smartplug --- .../smarthut/models/SmartPlug.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java index fe936b3..533f394 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java @@ -1,5 +1,8 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import javax.persistence.Column; import javax.persistence.Entity; import javax.validation.constraints.NotNull; @@ -8,11 +11,42 @@ import javax.validation.constraints.NotNull; @Entity public class SmartPlug extends Switchable { + /** The total amount of power that the smart plug has consumed represented in kW/h */ + @Column @NotNull private static double totalConsumption; + /** Whether the smart plug is on */ @Column(name = "smart_plug_on", nullable = false) @NotNull private boolean on; + public double getTotalConsumption() { + return totalConsumption; + } + + public void resetTotalConsumption() { + totalConsumption = 0; + } + + /** + * Updates the consumption of a smart plug storing it in kW/h assuming that an average smart + * plug consumes 3W and by computing the conversion from watts to kW/h + */ + private static void updateTotalConsumption() { + double averageConsumption = 3; + totalConsumption += (averageConsumption / 1000) * (1 / 3600); + } + + /** Calls updateTotalConsumption every second */ + public void updateConsumptionOverTime() { + final ScheduledExecutorService executorService = + Executors.newSingleThreadScheduledExecutor(); + + while (isOn()) { + executorService.scheduleAtFixedRate( + SmartPlug::updateTotalConsumption, 0, 1, TimeUnit.SECONDS); + } + } + @Override public boolean isOn() { return on; From 835c9e047199105043d71fe44c4692802bb69024 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Tue, 17 Mar 2020 16:54:35 +0100 Subject: [PATCH 2/2] Fixed smaal errors @tommi27 implementation. Fixed precision and scale values or total precision. Optimized update by using JPQL --- .../controller/SmartPlugController.java | 15 +++++++- .../sanmarinoes/smarthut/models/Device.java | 2 +- .../sanmarinoes/smarthut/models/Sensor.java | 2 +- .../smarthut/models/SmartPlug.java | 38 ++++++------------- .../smarthut/models/SmartPlugRepository.java | 23 ++++++++++- ...ensorUpdateTasks.java => UpdateTasks.java} | 25 +++++++++--- .../smarthut/socket/SensorSocketEndpoint.java | 6 +++ src/main/resources/application.properties | 2 +- 8 files changed, 76 insertions(+), 37 deletions(-) rename src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/{SensorUpdateTasks.java => UpdateTasks.java} (74%) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java index 8a57430..32bdf33 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java @@ -5,6 +5,7 @@ import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SmartPlugSaveRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import java.security.Principal; import java.util.*; import java.util.List; import javax.validation.Valid; @@ -14,7 +15,7 @@ import org.springframework.web.bind.annotation.*; @RestController @EnableAutoConfiguration -@RequestMapping("/smartplug") +@RequestMapping("/smartPlug") public class SmartPlugController { @Autowired private SmartPlugRepository smartPlugRepository; @@ -49,6 +50,18 @@ public class SmartPlugController { smartPlugRepository.findById(sp.getId()).orElseThrow(NotFoundException::new), sp); } + @DeleteMapping("/{id}/meter") + public SmartPlug resetMeter(@PathVariable("id") long id, final Principal principal) + throws NotFoundException { + final SmartPlug s = + smartPlugRepository + .findByIdAndUsername(id, principal.getName()) + .orElseThrow(NotFoundException::new); + + s.resetTotalConsumption(); + return smartPlugRepository.save(s); + } + @DeleteMapping("/{id}") public void deleteById(@PathVariable("id") long id) { smartPlugRepository.deleteById(id); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java index 6d0333d..295fe37 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java @@ -36,7 +36,7 @@ public abstract class Device { * The room this device belongs in, as a foreign key id. To use when updating and inserting from * a REST call. */ - @Column(name = "room_id", nullable = false, unique = true) + @Column(name = "room_id", nullable = false) @NotNull private Long roomId; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java index 525ceb3..6b5e6b9 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java @@ -35,7 +35,7 @@ public class Sensor extends InputDevice { } /** The value of this sensor according to its sensor type */ - @Column(nullable = false, length = 10, precision = 1) + @Column(nullable = false, precision = 11, scale = 1) private BigDecimal value; /** The type of this sensor */ diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java index 533f394..4411dc3 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java @@ -1,8 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; +import java.math.BigDecimal; import javax.persistence.Column; import javax.persistence.Entity; import javax.validation.constraints.NotNull; @@ -11,40 +9,26 @@ import javax.validation.constraints.NotNull; @Entity public class SmartPlug extends Switchable { - /** The total amount of power that the smart plug has consumed represented in kW/h */ - @Column @NotNull private static double totalConsumption; + /** The average consumption of an active plug when on in Watt */ + public static final Double AVERAGE_CONSUMPTION_KW = 200.0; + + /** The total amount of power that the smart plug has consumed represented in W/h */ + @Column(precision = 13, scale = 3) + @NotNull + private BigDecimal totalConsumption = BigDecimal.ZERO; /** Whether the smart plug is on */ @Column(name = "smart_plug_on", nullable = false) @NotNull private boolean on; - public double getTotalConsumption() { + public BigDecimal getTotalConsumption() { return totalConsumption; } + /** Resets the consuption meter */ public void resetTotalConsumption() { - totalConsumption = 0; - } - - /** - * Updates the consumption of a smart plug storing it in kW/h assuming that an average smart - * plug consumes 3W and by computing the conversion from watts to kW/h - */ - private static void updateTotalConsumption() { - double averageConsumption = 3; - totalConsumption += (averageConsumption / 1000) * (1 / 3600); - } - - /** Calls updateTotalConsumption every second */ - public void updateConsumptionOverTime() { - final ScheduledExecutorService executorService = - Executors.newSingleThreadScheduledExecutor(); - - while (isOn()) { - executorService.scheduleAtFixedRate( - SmartPlug::updateTotalConsumption, 0, 1, TimeUnit.SECONDS); - } + totalConsumption = BigDecimal.ZERO; } @Override diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlugRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlugRepository.java index 08d145d..8a02243 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlugRepository.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlugRepository.java @@ -1,3 +1,24 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; -public interface SmartPlugRepository extends SwitchableRepository {} +import java.util.Collection; +import javax.transaction.Transactional; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; + +public interface SmartPlugRepository extends SwitchableRepository { + @Transactional + Collection findByOn(boolean on); + + /** + * Updates total consumption of all activated smart plugs by considering a load of + * fakeConsumption W. This query must be executed every second + * + * @see ch.usi.inf.sa4.sanmarinoes.smarthut.scheduled.UpdateTasks + * @param fakeConsumption the fake consumption in watts + */ + @Modifying(clearAutomatically = true) + @Transactional + @Query( + "UPDATE SmartPlug s SET totalConsumption = s.totalConsumption + ?1 / 3600.0 WHERE s.on = true") + void updateTotalConsumption(Double fakeConsumption); +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/SensorUpdateTasks.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/UpdateTasks.java similarity index 74% rename from src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/SensorUpdateTasks.java rename to src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/UpdateTasks.java index b1614aa..932db5c 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/SensorUpdateTasks.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/UpdateTasks.java @@ -2,10 +2,10 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.scheduled; import ch.usi.inf.sa4.sanmarinoes.smarthut.controller.MotionSensorController; import ch.usi.inf.sa4.sanmarinoes.smarthut.controller.SensorController; -import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensorRepository; -import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Sensor; -import ch.usi.inf.sa4.sanmarinoes.smarthut.models.SensorRepository; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint; import java.math.BigDecimal; +import java.util.Collection; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.stream.StreamSupport; @@ -13,18 +13,25 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -/** Generates fake sensor (and motion sensor) updates as required by milestone one */ +/** + * Generates fake sensor (and motion sensor) and smart plug consumption updates as required by + * milestone one + */ @Component -public class SensorUpdateTasks { +public class UpdateTasks { @Autowired private SensorRepository sensorRepository; @Autowired private MotionSensorRepository motionSensorRepository; + @Autowired private SmartPlugRepository smartPlugRepository; + @Autowired private SensorController sensorController; @Autowired private MotionSensorController motionSensorController; + @Autowired private SensorSocketEndpoint sensorSocketEndpoint; + /** Generates fake sensor updates every two seconds with a +/- 1.25% error */ @Scheduled(fixedRate = 2000) public void sensorFakeUpdate() { @@ -59,4 +66,12 @@ public class SensorUpdateTasks { sensor, false)); }); } + + /** Updates power consumption of all activated smart plugs every second */ + @Scheduled(fixedDelay = 1000) + public void smartPlugConsumptionFakeUpdate() { + smartPlugRepository.updateTotalConsumption(SmartPlug.AVERAGE_CONSUMPTION_KW); + final Collection c = smartPlugRepository.findByOn(true); + c.forEach(s -> sensorSocketEndpoint.broadcast(s, sensorRepository.findUser(s.getId()))); + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java index bc2f90e..f1e4808 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java @@ -61,6 +61,12 @@ public class SensorSocketEndpoint extends Endpoint { final Collection sessions = authorizedClients.get(u); return sessions.stream() .parallel() + .filter( + s -> { + if (s.isOpen()) return true; + sessions.remove(s); + return false; + }) .filter(didThrow(s -> s.getBasicRemote().sendText(gson.toJson(message)))) .count(); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f760ec8..2fb519c 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -5,7 +5,7 @@ spring.datasource.password= # Hibernate properties spring.jpa.database=POSTGRESQL -spring.jpa.show-sql=true +spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl spring.jpa.properties.hibernate.format_sql=true