Button dimmer now with new rest conversation scheme

This commit is contained in:
Claudio Maggioni 2020-03-13 18:20:16 +01:00
parent 7bb05b705f
commit 3a97d40858
14 changed files with 293 additions and 98 deletions

View file

@ -23,6 +23,7 @@ public class GsonConfig {
private Gson gson() { private 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());
return builder.create(); return builder.create();
} }
} }
@ -34,3 +35,16 @@ class SpringfoxJsonToGsonAdapter implements JsonSerializer<Json> {
return JsonParser.parseString(json.value()); return JsonParser.parseString(json.value());
} }
} }
/** GSON exclusion strategy to exclude attributes with @GsonExclude */
class AnnotationExclusionStrategy implements ExclusionStrategy {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getAnnotation(GsonExclude.class) != null;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}

View file

@ -0,0 +1,10 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface GsonExclude {}

View file

@ -2,11 +2,12 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.ButtonDimmerDimRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.ButtonDimmerSaveRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.ButtonDimmerSaveRequest;
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.ButtonDimmer; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ButtonDimmerRepository;
import java.util.List; import java.util.List;
import java.util.Set;
import javax.validation.Valid; import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@ -15,37 +16,74 @@ import org.springframework.web.bind.annotation.*;
@RestController @RestController
@EnableAutoConfiguration @EnableAutoConfiguration
@RequestMapping("/buttonDimmer") @RequestMapping("/buttonDimmer")
public class ButtonDimmerController { public class ButtonDimmerController
@Autowired private ButtonDimmerRepository buttonDimmerService; extends InputDeviceConnectionController<ButtonDimmer, DimmableLight> {
private ButtonDimmerRepository buttonDimmerRepository;
private DimmableLightRepository dimmableLightRepository;
@Autowired
protected ButtonDimmerController(
ButtonDimmerRepository inputRepository, DimmableLightRepository outputRepository) {
super(inputRepository, outputRepository);
this.buttonDimmerRepository = inputRepository;
this.dimmableLightRepository = outputRepository;
}
@GetMapping @GetMapping
public List<ButtonDimmer> findAll() { public List<ButtonDimmer> findAll() {
return toList(buttonDimmerService.findAll()); return toList(buttonDimmerRepository.findAll());
} }
@GetMapping("/{id}") @GetMapping("/{id}")
public ButtonDimmer findById(@PathVariable("id") long id) throws NotFoundException { public ButtonDimmer findById(@PathVariable("id") long id) throws NotFoundException {
return buttonDimmerService.findById(id).orElseThrow(NotFoundException::new); return buttonDimmerRepository.findById(id).orElseThrow(NotFoundException::new);
} }
@PostMapping @PostMapping
public ButtonDimmer create(@Valid @RequestBody final ButtonDimmerSaveRequest bd) { public ButtonDimmer create(@Valid @RequestBody final ButtonDimmerSaveRequest bd) {
ButtonDimmer newBD = new ButtonDimmer(); ButtonDimmer newBD = new ButtonDimmer();
newBD.setLights(bd.getLights());
newBD.setId(bd.getId());
newBD.setName(bd.getName()); newBD.setName(bd.getName());
newBD.setRoomId(bd.getRoomId()); newBD.setRoomId(bd.getRoomId());
return buttonDimmerService.save(newBD); return buttonDimmerRepository.save(newBD);
} }
@PutMapping @PutMapping("/dim")
public ButtonDimmer update(@Valid @RequestBody ButtonDimmerSaveRequest bd) { public Set<DimmableLight> dim(@Valid @RequestBody final ButtonDimmerDimRequest bd)
return this.create(bd); throws NotFoundException {
final ButtonDimmer buttonDimmer =
buttonDimmerRepository.findById(bd.getId()).orElseThrow(NotFoundException::new);
switch (bd.getDimType()) {
case UP:
buttonDimmer.increaseIntensity();
break;
case DOWN:
buttonDimmer.decreaseIntensity();
break;
}
dimmableLightRepository.saveAll(buttonDimmer.getLights());
return buttonDimmer.getOutputs();
}
@PostMapping("/{id}/lights")
public Set<DimmableLight> addLight(
@PathVariable("id") long inputId, @RequestParam("lightId") Long lightId)
throws NotFoundException {
return addOutput(inputId, lightId);
}
@DeleteMapping("/{id}/lights")
public Set<DimmableLight> removeLight(
@PathVariable("id") long inputId, @RequestParam("lightId") Long lightId)
throws NotFoundException {
return removeOutput(inputId, lightId);
} }
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public void delete(@PathVariable("id") long id) { public void delete(@PathVariable("id") long id) {
buttonDimmerService.deleteById(id); buttonDimmerRepository.deleteById(id);
} }
} }

View file

@ -33,19 +33,12 @@ public class DimmableLightController {
public DimmableLight create(@Valid @RequestBody DimmableLightSaveRequest dl) { public DimmableLight create(@Valid @RequestBody DimmableLightSaveRequest dl) {
DimmableLight newDL = new DimmableLight(); DimmableLight newDL = new DimmableLight();
newDL.setIntensity(dl.getIntensity()); newDL.setIntensity(dl.getIntensity());
newDL.setId(dl.getId());
newDL.setName(dl.getName()); newDL.setName(dl.getName());
newDL.setRoomId(dl.getRoomId()); newDL.setRoomId(dl.getRoomId());
return dimmableLightService.save(newDL); return dimmableLightService.save(newDL);
} }
@PutMapping
public DimmableLight update(@Valid @RequestBody DimmableLightSaveRequest dl) {
dl.setId(0);
return this.create(dl);
}
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public void delete(@PathVariable("id") long id) { public void delete(@PathVariable("id") long id) {
dimmableLightService.deleteById(id); dimmableLightService.deleteById(id);

View file

@ -0,0 +1,81 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
import java.util.Set;
/**
* An abstract controller for an input device that has output connected to it. Aids to create the
* output add and output remove route
*
* @param <I> the type of device this controller is for
* @param <O> the output device attached to I
*/
public abstract class InputDeviceConnectionController<
I extends InputDevice & OutputConnectable<O>,
O extends OutputDevice & InputConnectable<I>> {
private class IOPair {
private final I input;
private final O output;
private IOPair(I input, O output) {
this.input = input;
this.output = output;
}
}
private DeviceRepository<I> inputRepository;
private DeviceRepository<O> outputReposiory;
protected InputDeviceConnectionController(
DeviceRepository<I> inputRepository, DeviceRepository<O> outputRepository) {
this.inputRepository = inputRepository;
this.outputReposiory = outputRepository;
}
private IOPair checkConnectionIDs(Long inputId, Long outputId) throws NotFoundException {
final I input =
inputRepository
.findById(inputId)
.orElseThrow(() -> new NotFoundException("input device"));
final O output =
outputReposiory
.findById(outputId)
.orElseThrow(() -> new NotFoundException("output device"));
return new IOPair(input, output);
}
/**
* Implements the output device connection creation (add) route
*
* @param inputId input device id
* @param outputId output device id
* @return the list of output devices attached to the input device of id inputId
* @throws NotFoundException if inputId or outputId are not valid
*/
protected Set<O> addOutput(Long inputId, Long outputId) throws NotFoundException {
final IOPair pair = checkConnectionIDs(inputId, outputId);
pair.input.getOutputs().add(pair.output);
pair.output.connect(pair.input.getId());
outputReposiory.save(pair.output);
return pair.input.getOutputs();
}
/**
* Implements the output device connection destruction (remove) route
*
* @param inputId input device id
* @param outputId output device id
* @return the list of output devices attached to the input device of id inputId
* @throws NotFoundException if inputId or outputId are not valid
*/
protected Set<O> removeOutput(Long inputId, Long outputId) throws NotFoundException {
final IOPair pair = checkConnectionIDs(inputId, outputId);
pair.input.getOutputs().remove(pair.output);
pair.output.connect(null);
outputReposiory.save(pair.output);
return pair.input.getOutputs();
}
}

View file

@ -0,0 +1,34 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import javax.validation.constraints.NotNull;
/** A 'dim' event from a button dimmer. */
public class ButtonDimmerDimRequest {
/** The device id */
@NotNull private Long id;
public enum DimType {
UP,
DOWN;
}
/** Whether the dim is up or down */
@NotNull private DimType dimType;
public DimType getDimType() {
return dimType;
}
public void setDimType(DimType dimType) {
this.dimType = dimType;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View file

@ -1,21 +1,8 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableLight;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Room;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.*;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
public class ButtonDimmerSaveRequest { public class ButtonDimmerSaveRequest {
@Lob private Set<DimmableLight> lights = new HashSet<DimmableLight>();
/** Device identifier */
private long id;
/** The room this device belongs in */
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.
@ -25,18 +12,6 @@ public class ButtonDimmerSaveRequest {
/** The name of the device as assigned by the user (e.g. 'Master bedroom light') */ /** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
@NotNull private String name; @NotNull private String name;
public Set<DimmableLight> getLights() {
return this.lights;
}
public void setLights(Set<DimmableLight> newLights) {
this.lights = newLights;
}
public void setRoom(Room room) {
this.room = room;
}
public void setRoomId(Long roomId) { public void setRoomId(Long roomId) {
this.roomId = roomId; this.roomId = roomId;
} }
@ -45,14 +20,6 @@ public class ButtonDimmerSaveRequest {
this.name = name; this.name = name;
} }
public long getId() {
return id;
}
public Room getRoom() {
return room;
}
public Long getRoomId() { public Long getRoomId() {
return roomId; return roomId;
} }
@ -60,8 +27,4 @@ public class ButtonDimmerSaveRequest {
public String getName() { public String getName() {
return name; return name;
} }
public void setId(long id) {
this.id = id;
}
} }

View file

@ -1,6 +1,5 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Room;
import javax.validation.constraints.Max; import javax.validation.constraints.Max;
import javax.validation.constraints.Min; import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@ -13,12 +12,6 @@ public class DimmableLightSaveRequest {
@Max(100) @Max(100)
private Integer intensity = 0; private Integer intensity = 0;
/** Device identifier */
private long id;
/** The room this device belongs in */
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.
@ -28,10 +21,6 @@ public class DimmableLightSaveRequest {
/** The name of the device as assigned by the user (e.g. 'Master bedroom light') */ /** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
@NotNull private String name; @NotNull private String name;
public void setRoom(Room room) {
this.room = room;
}
public void setRoomId(Long roomId) { public void setRoomId(Long roomId) {
this.roomId = roomId; this.roomId = roomId;
} }
@ -40,14 +29,6 @@ public class DimmableLightSaveRequest {
this.name = name; this.name = name;
} }
public long getId() {
return id;
}
public Room getRoom() {
return room;
}
public Long getRoomId() { public Long getRoomId() {
return roomId; return roomId;
} }
@ -60,14 +41,7 @@ public class DimmableLightSaveRequest {
return intensity; return intensity;
} }
public void setIntensity(Integer intensity) throws IllegalArgumentException { public void setIntensity(Integer intensity) {
if (intensity < 0 || intensity > 100) {
throw new IllegalArgumentException("The intensity level can't go below 0 or above 100");
}
this.intensity = intensity; this.intensity = intensity;
} }
public void setId(long id) {
this.id = id;
}
} }

View file

@ -6,6 +6,10 @@ import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(code = HttpStatus.NOT_FOUND) @ResponseStatus(code = HttpStatus.NOT_FOUND)
public class NotFoundException extends Exception { public class NotFoundException extends Exception {
public NotFoundException() { public NotFoundException() {
super("Not Found"); super("Not found");
}
public NotFoundException(String what) {
super(what + " not found");
} }
} }

View file

@ -1,6 +1,5 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models; package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.Set; import java.util.Set;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
@ -10,7 +9,10 @@ import javax.persistence.OneToMany;
* dimmer with a '+' and a '-' button) * dimmer with a '+' and a '-' button)
*/ */
@Entity @Entity
public class ButtonDimmer extends Dimmer { public class ButtonDimmer extends Dimmer implements OutputConnectable<DimmableLight> {
/** The delta amount to apply to a increase or decrease intensity */
public static final int DIM_INCREMENT = 10;
public ButtonDimmer() { public ButtonDimmer() {
super("button-dimmer"); super("button-dimmer");
} }
@ -18,17 +20,19 @@ public class ButtonDimmer extends Dimmer {
@OneToMany(mappedBy = "dimmer") @OneToMany(mappedBy = "dimmer")
private Set<DimmableLight> lights; private Set<DimmableLight> lights;
/** Increases the current intensity level of the dimmable light by 1 */ /** Increases the current intensity level of the dimmable light by DIM_INCREMENT */
public void increaseIntensity() { public void increaseIntensity() {
for (DimmableLight dl : lights) { for (DimmableLight dl : lights) {
dl.setIntensity(dl.getIntensity() + 1); dl.setIntensity(dl.getIntensity() + DIM_INCREMENT);
System.out.println("malusa: " + dl.getIntensity());
} }
} }
/** Decreases the current intensity level of the dimmable light by 1 */ /** Decreases the current intensity level of the dimmable light by DIM_INCREMENT */
public void decreaseIntensity() { public void decreaseIntensity() {
for (DimmableLight dl : lights) { for (DimmableLight dl : lights) {
dl.setIntensity(dl.getIntensity() - 1); dl.setIntensity(dl.getIntensity() - DIM_INCREMENT);
System.out.println("malusa: " + dl.getIntensity());
} }
} }
@ -64,6 +68,11 @@ public class ButtonDimmer extends Dimmer {
return this.lights; return this.lights;
} }
@Override
public Set<DimmableLight> getOutputs() {
return this.lights;
}
public void setLights(Set<DimmableLight> newLights) { public void setLights(Set<DimmableLight> newLights) {
this.lights = newLights; this.lights = newLights;
} }

View file

@ -1,7 +1,9 @@
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 javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.validation.constraints.Max; import javax.validation.constraints.Max;
import javax.validation.constraints.Min; import javax.validation.constraints.Min;
@ -9,18 +11,24 @@ import javax.validation.constraints.NotNull;
/** Represent a dimmable light */ /** Represent a dimmable light */
@Entity @Entity
public class DimmableLight extends Light { public class DimmableLight extends Light implements InputConnectable<ButtonDimmer> {
public DimmableLight() { public DimmableLight() {
super("light"); super("light");
} }
@ManyToOne private Dimmer dimmer; @ManyToOne
@GsonExclude
@JoinColumn(name = "dimmer_id", updatable = false, insertable = false)
private Dimmer dimmer;
@Column(name = "dimmer_id")
private Long dimmerId;
/** The light intensity value. Goes from 0 (off) to 100 (on) */ /** The light intensity value. Goes from 0 (off) to 100 (on) */
@NotNull @NotNull
@Column(nullable = false) @Column(nullable = false)
@Min(1) @Min(0)
@Max(100) @Max(100)
private Integer intensity = 0; private Integer intensity = 0;
@ -28,10 +36,39 @@ public class DimmableLight extends Light {
return intensity; return intensity;
} }
public void setIntensity(Integer intensity) throws IllegalArgumentException { /**
if (intensity < 0 || intensity > 100) { * Sets the intensity to a certain level. Out of bound values are corrected to the respective
throw new IllegalArgumentException("The intensity level can't go below 0 or above 100"); * extremums. An intensity level of 0 turns the light off, but keeps the old intensity level
* stored.
*
* @param intensity the intensity level (may be out of bounds)
*/
public void setIntensity(Integer intensity) {
if (intensity <= 0) {
this.intensity = 0;
setOn(false);
} else {
setOn(true);
if (intensity > 100) {
this.intensity = 100;
} else {
this.intensity = intensity;
}
} }
this.intensity = intensity; }
@Override
public void setOn(boolean on) {
super.setOn(on);
if (on) {
intensity = 100;
} else {
intensity = 0;
}
}
@Override
public void connect(Long inputId) {
this.dimmerId = inputId;
} }
} }

View file

@ -0,0 +1,18 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
/**
* An output device to which an input can be connected
*
* @param <O> the type of input device that can be connected to this device
*/
public interface InputConnectable<O extends InputDevice> {
/**
* Connect an input device to this output device. This method should set the corresponding FK in
* the entity.
*
* @param inputId the input device id, null for deleting the connection
*/
void connect(Long inputId);
}

View file

@ -0,0 +1,18 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.Set;
/**
* An input device to which outputs can be connected
*
* @param <O> the type of output device that can be connected to this device
*/
public interface OutputConnectable<O extends OutputDevice> {
/**
* Get the set of all output devices connected to this device
*
* @return The set of outputs connected to this device
*/
Set<O> getOutputs();
}

View file

@ -42,6 +42,8 @@ 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 int getValue() {