Socket now sends sensor updates

This commit is contained in:
Claudio Maggioni 2020-03-15 13:41:57 +01:00
parent fd6103b6da
commit 5a441a6992
12 changed files with 126 additions and 14 deletions

View file

@ -1,3 +1,5 @@
<!-- vim: set ts=2 sw=2 et tw=80: -->
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
@ -25,6 +27,8 @@ connection.onmessage = function(evt) {
"<img src='https://maggioni.xyz/astley.gif'>"; "<img src='https://maggioni.xyz/astley.gif'>";
} else if (data.authenticated === false) { } else if (data.authenticated === false) {
malusa.innerHTML = "<h1>Authentication error</h1>"; malusa.innerHTML = "<h1>Authentication error</h1>";
} else {
malusa.innerHTML += "<p><pre>" + JSON.stringify(JSON.parse(evt.data), null, 2) + "</pre></p>";
} }
}; };

View file

@ -20,7 +20,7 @@ public class GsonConfig {
return converter; return converter;
} }
private Gson gson() { public static Gson gson() {
final GsonBuilder builder = new GsonBuilder(); final GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter()); builder.registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter());
builder.addSerializationExclusionStrategy(new AnnotationExclusionStrategy()); builder.addSerializationExclusionStrategy(new AnnotationExclusionStrategy());

View file

@ -5,6 +5,9 @@ import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SensorSaveRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SensorSaveRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
import java.math.BigDecimal;
import java.security.Principal;
import java.util.*; import java.util.*;
import java.util.List; import java.util.List;
import javax.validation.Valid; import javax.validation.Valid;
@ -19,6 +22,8 @@ public class SensorController {
@Autowired private SensorRepository sensorRepository; @Autowired private SensorRepository sensorRepository;
@Autowired private SensorSocketEndpoint sensorSocketEndpoint;
@GetMapping @GetMapping
public List<Sensor> findAll() { public List<Sensor> findAll() {
return toList(sensorRepository.findAll()); return toList(sensorRepository.findAll());
@ -35,10 +40,32 @@ public class SensorController {
newSensor.setSensor(s.getSensor()); newSensor.setSensor(s.getSensor());
newSensor.setName(s.getName()); newSensor.setName(s.getName());
newSensor.setRoomId(s.getRoomId()); newSensor.setRoomId(s.getRoomId());
newSensor.setValue(s.getValue());
return sensorRepository.save(newSensor); return sensorRepository.save(newSensor);
} }
@PutMapping("/{id}/value")
public Sensor updateValue(
@PathVariable("id") Long sensorId,
@RequestParam("value") BigDecimal value,
final Principal principal)
throws NotFoundException {
final Sensor sensor =
sensorRepository
.findByIdAndUsername(sensorId, principal.getName())
.orElseThrow(NotFoundException::new);
sensor.setValue(value);
final Sensor toReturn = sensorRepository.save(sensor);
System.out.println("sensor: " + sensor);
sensorSocketEndpoint.broadcast(sensor, sensorRepository.findUser(sensorId));
return toReturn;
}
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public void deleteById(@PathVariable("id") long id) { public void deleteById(@PathVariable("id") long id) {
sensorRepository.deleteById(id); sensorRepository.deleteById(id);

View file

@ -2,6 +2,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Sensor; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Sensor;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.math.BigDecimal;
import javax.persistence.EnumType; import javax.persistence.EnumType;
import javax.persistence.Enumerated; import javax.persistence.Enumerated;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@ -28,6 +29,8 @@ public class SensorSaveRequest {
@Enumerated(value = EnumType.STRING) @Enumerated(value = EnumType.STRING)
private Sensor.SensorType sensor; private Sensor.SensorType sensor;
@NotNull private BigDecimal value;
/** /**
* The room this device belongs in, as a foreign key id. To use when updating and inserting from * The room this device belongs in, as a foreign key id. To use when updating and inserting from
* a REST call. * a REST call.
@ -60,4 +63,12 @@ public class SensorSaveRequest {
public void setSensor(Sensor.SensorType sensor) { public void setSensor(Sensor.SensorType sensor) {
this.sensor = sensor; this.sensor = sensor;
} }
public BigDecimal getValue() {
return value;
}
public void setValue(BigDecimal value) {
this.value = value;
}
} }

View file

@ -1,5 +1,6 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models; package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import javax.persistence.*; import javax.persistence.*;
@ -26,6 +27,11 @@ public abstract class Device {
@ApiModelProperty(hidden = true) @ApiModelProperty(hidden = true)
private long id; private long id;
@ManyToOne
@JoinColumn(name = "room_id", updatable = false, insertable = false)
@GsonExclude
private Room room;
/** /**
* The room this device belongs in, as a foreign key id. To use when updating and inserting from * The room this device belongs in, as a foreign key id. To use when updating and inserting from
* a REST call. * a REST call.

View file

@ -1,6 +1,8 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models; package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
@ -10,4 +12,23 @@ import org.springframework.data.repository.query.Param;
*/ */
public interface DeviceRepository<T extends Device> extends CrudRepository<T, Long> { public interface DeviceRepository<T extends Device> extends CrudRepository<T, Long> {
List<T> findByRoomId(@Param("roomId") long roomId); List<T> findByRoomId(@Param("roomId") long roomId);
/**
* Finds devices by their id and a username
*
* @param id the device id
* @param username a User's username
* @return an optional device, empty if none found
*/
@Query("SELECT d FROM Device d JOIN d.room r JOIN r.user u WHERE d.id = ?1 AND u.username = ?2")
Optional<T> findByIdAndUsername(Long id, String username);
/**
* Find the user associated with a device through a room
*
* @param deviceId the device id
* @return a user object
*/
@Query("SELECT u FROM Device d JOIN d.room r JOIN r.user u WHERE d.id = ?1")
User findUser(Long deviceId);
} }

View file

@ -1,5 +1,6 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models; package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import javax.persistence.*; import javax.persistence.*;
@ -128,6 +129,11 @@ public class Room {
@Column(name = "image", columnDefinition = "TEXT") @Column(name = "image", columnDefinition = "TEXT")
private String image; private String image;
@ManyToOne
@JoinColumn(name = "user_id", updatable = false, insertable = false)
@GsonExclude
private User user;
/** /**
* User that owns the house this room is in as a foreign key id. To use when updating and * User that owns the house this room is in as a foreign key id. To use when updating and
* inserting from a REST call. * inserting from a REST call.

View file

@ -1,6 +1,8 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models; package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.math.BigDecimal;
import java.util.Map;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EnumType; import javax.persistence.EnumType;
@ -11,6 +13,12 @@ import javax.validation.constraints.NotNull;
@Entity @Entity
public class Sensor extends InputDevice { public class Sensor extends InputDevice {
private static final Map<SensorType, Integer> TYPICAL_VALUES =
Map.of(
SensorType.TEMPERATURE, 17,
SensorType.HUMIDITY, 40,
SensorType.LIGHT, 1000);
/** Type of sensor, i.e. of the thing the sensor measures. */ /** Type of sensor, i.e. of the thing the sensor measures. */
public enum SensorType { public enum SensorType {
/** A sensor that measures temperature in degrees celsius */ /** A sensor that measures temperature in degrees celsius */
@ -27,8 +35,8 @@ public class Sensor extends InputDevice {
} }
/** The value of this sensor according to its sensor type */ /** The value of this sensor according to its sensor type */
@Column(nullable = false) @Column(nullable = false, length = 10, precision = 1)
private int value; private BigDecimal value;
/** The type of this sensor */ /** The type of this sensor */
@Column(nullable = false) @Column(nullable = false)
@ -42,19 +50,22 @@ public class Sensor extends InputDevice {
public void setSensor(SensorType sensor) { public void setSensor(SensorType sensor) {
this.sensor = sensor; this.sensor = sensor;
// TODO: setup hook for sockets live update
} }
public int getValue() { public BigDecimal getValue() {
return this.value; return this.value;
} }
public void setValue(int newValue) { public void setValue(BigDecimal newValue) {
this.value = newValue; this.value = newValue;
} }
public Sensor() { public Sensor() {
super("sensor"); super("sensor");
} }
@Override
public String toString() {
return "Sensor{" + "value=" + value + ", sensor=" + sensor + '}';
}
} }

View file

@ -1,6 +1,7 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models; package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import java.util.Objects;
import javax.persistence.*; import javax.persistence.*;
/** A user of the Smarthut application */ /** A user of the Smarthut application */
@ -105,4 +106,22 @@ public class User {
+ isEnabled + isEnabled
+ '}'; + '}';
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return id.equals(user.id)
&& name.equals(user.name)
&& username.equals(user.username)
&& password.equals(user.password)
&& email.equals(user.email)
&& isEnabled.equals(user.isEnabled);
}
@Override
public int hashCode() {
return Objects.hash(id, name, username, password, email, isEnabled);
}
} }

View file

@ -1,5 +1,6 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.socket; package ch.usi.inf.sa4.sanmarinoes.smarthut.socket;
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonConfig;
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.JWTTokenUtils; import ch.usi.inf.sa4.sanmarinoes.smarthut.config.JWTTokenUtils;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
@ -18,7 +19,7 @@ import org.springframework.stereotype.Component;
@Component @Component
public class AuthenticationMessageListener { public class AuthenticationMessageListener {
private Gson gson = new Gson(); private Gson gson = GsonConfig.gson();
private JWTTokenUtils jwtTokenUtils; private JWTTokenUtils jwtTokenUtils;

View file

@ -2,6 +2,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.socket;
import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.didThrow; import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.didThrow;
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonConfig;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
@ -16,7 +17,7 @@ import org.springframework.stereotype.Component;
@Component @Component
public class SensorSocketEndpoint extends Endpoint { public class SensorSocketEndpoint extends Endpoint {
private Gson gson = new Gson(); private Gson gson = GsonConfig.gson();
private AuthenticationMessageListener authenticationMessageListener; private AuthenticationMessageListener authenticationMessageListener;
@ -58,9 +59,10 @@ public class SensorSocketEndpoint extends Endpoint {
*/ */
public long broadcast(Object message, User u) { public long broadcast(Object message, User u) {
final Collection<Session> sessions = authorizedClients.get(u); final Collection<Session> sessions = authorizedClients.get(u);
System.out.println(authorizedClients + " " + sessions + " " + u);
return sessions.stream() return sessions.stream()
.parallel() .parallel()
.filter(didThrow(s -> s.getAsyncRemote().sendObject(gson.toJson(message)))) .filter(didThrow(s -> s.getBasicRemote().sendText(gson.toJson(message))))
.count(); .count();
} }

View file

@ -1,8 +1,6 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.utils; package ch.usi.inf.sa4.sanmarinoes.smarthut.utils;
import java.util.List; import java.util.List;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
@ -11,14 +9,20 @@ import java.util.stream.StreamSupport;
public final class Utils { public final class Utils {
private Utils() {} private Utils() {}
@FunctionalInterface
public interface ConsumerWithException<T> {
void apply(T input) throws Throwable;
}
public static <T> List<T> toList(Iterable<T> iterable) { public static <T> List<T> toList(Iterable<T> iterable) {
return StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList()); return StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList());
} }
public static <T> Predicate<T> didThrow(Function<T, Future<?>> consumer) { public static <T> Predicate<T> didThrow(ConsumerWithException<T> consumer) {
return (t) -> { return (t) -> {
try { try {
consumer.apply(t).get(); consumer.apply(t);
System.out.println("successful");
return true; return true;
} catch (Throwable e) { } catch (Throwable e) {
System.err.println(e.getMessage()); System.err.println(e.getMessage());