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 fe936b3..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,5 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import java.math.BigDecimal; import javax.persistence.Column; import javax.persistence.Entity; import javax.validation.constraints.NotNull; @@ -8,11 +9,28 @@ import javax.validation.constraints.NotNull; @Entity public class SmartPlug extends Switchable { + /** 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 BigDecimal getTotalConsumption() { + return totalConsumption; + } + + /** Resets the consuption meter */ + public void resetTotalConsumption() { + totalConsumption = BigDecimal.ZERO; + } + @Override public boolean isOn() { return on; 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