Implemented Many-to-Many behaviour for Switches and Dimmers

This commit is contained in:
Claudio Maggioni (maggicl) 2020-04-16 17:43:25 +02:00
parent 1fe31c0d46
commit 7cbacd7406
14 changed files with 88 additions and 111 deletions

View file

@ -75,20 +75,6 @@ public class ButtonDimmerController
return buttonDimmer.getOutputs(); return buttonDimmer.getOutputs();
} }
@PostMapping("/{id}/lights")
public Set<? extends OutputDevice> addLight(
@PathVariable("id") long inputId, @RequestBody List<Long> lightId)
throws NotFoundException {
return addOutput(inputId, lightId);
}
@DeleteMapping("/{id}/lights")
public Set<? extends OutputDevice> removeLight(
@PathVariable("id") long inputId, @RequestBody List<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) {
buttonDimmerRepository.deleteById(id); buttonDimmerRepository.deleteById(id);

View file

@ -2,10 +2,18 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
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 com.google.gson.Gson;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
/** /**
* An abstract controller for an input device that has output connected to it. Aids to create the * An abstract controller for an input device that has output connected to it. Aids to create the
* output add and output remove route * output add and output remove route
@ -103,4 +111,18 @@ public abstract class InputDeviceConnectionController<
outputReposiory.saveAll(pair.output); outputReposiory.saveAll(pair.output);
return pair.input.getOutputs(); return pair.input.getOutputs();
} }
@PostMapping("/{id}/lights")
public List<OutputDevice> addLight(
@PathVariable("id") long inputId, @RequestBody List<Long> lightId)
throws NotFoundException {
return toList(addOutput(inputId, lightId));
}
@DeleteMapping("/{id}/lights")
public List<OutputDevice> removeLight(
@PathVariable("id") long inputId, @RequestBody List<Long> lightId)
throws NotFoundException {
return toList(removeOutput(inputId, lightId));
}
} }

View file

@ -68,20 +68,6 @@ public class KnobDimmerController
return dimmer.getOutputs(); return dimmer.getOutputs();
} }
@PostMapping("/{id}/lights")
public Set<? extends OutputDevice> addLight(
@PathVariable("id") long inputId, @RequestBody List<Long> lightId)
throws NotFoundException {
return addOutput(inputId, lightId);
}
@DeleteMapping("/{id}/lights")
public Set<? extends OutputDevice> removeLight(
@PathVariable("id") long inputId, @RequestBody List<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) {
knobDimmerRepository.deleteById(id); knobDimmerRepository.deleteById(id);

View file

@ -9,6 +9,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
import java.security.Principal; import java.security.Principal;
import java.util.*; import java.util.*;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
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.*; import org.springframework.boot.autoconfigure.*;
@ -81,20 +82,6 @@ public class SwitchController extends InputDeviceConnectionController<Switch, Sw
return s.getOutputs(); return s.getOutputs();
} }
@PostMapping("/{id}/lights")
public Set<? extends OutputDevice> addSwitchable(
@PathVariable("id") long inputId, @RequestBody List<Long> switchableId)
throws NotFoundException {
return addOutput(inputId, switchableId);
}
@DeleteMapping("/{id}/lights")
public Set<? extends OutputDevice> removeSwitchable(
@PathVariable("id") long inputId, @RequestBody List<Long> switchableId)
throws NotFoundException {
return removeOutput(inputId, switchableId);
}
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public void deleteById(@PathVariable("id") long id) { public void deleteById(@PathVariable("id") long id) {
switchRepository.deleteById(id); switchRepository.deleteById(id);

View file

@ -23,25 +23,25 @@ public interface Connector<I extends InputDevice, O extends OutputDevice> {
void connect(I input, O output, boolean connect); void connect(I input, O output, boolean connect);
/** /**
* Produces a basic implementation of a connector, assuming there is a OneToMany relationship * Produces a basic implementation of a connector, assuming there is a ManyToMany relationship
* between J and K * between J and K
* *
* @param outputsGetter the getter method of the set of outputs on the input class * @param outputsGetter the getter method of the set of outputs on the input class
* @param inputSetter the setter method for the input id on the output class * @param inputsGetter the getter method of the set of outputs on the input class
* @param <J> the input device type * @param <J> the input device type
* @param <K> the output device type * @param <K> the output device type
* @return a Connector implementation for the pair of types J and K * @return a Connector implementation for the pair of types J and K
*/ */
static <J extends InputDevice, K extends OutputDevice> Connector<J, K> basic( static <J extends InputDevice, K extends OutputDevice> Connector<J, K> basic(
Function<J, Set<? super K>> outputsGetter, BiConsumer<K, Long> inputSetter) { Function<J, Set<? super K>> outputsGetter, Function<K, Set<? super J>> inputsGetter) {
return (i, o, connect) -> { return (i, o, connect) -> {
if (connect) { if (connect) {
outputsGetter.apply(i).add(o); outputsGetter.apply(i).add(o);
inputsGetter.apply(o).add(i);
} else { } else {
outputsGetter.apply(i).remove(o); outputsGetter.apply(i).remove(o);
inputsGetter.apply(o).remove(i);
} }
inputSetter.accept(o, connect ? i.getId() : null);
}; };
} }
} }

View file

@ -8,7 +8,7 @@ import javax.validation.constraints.NotNull;
/** Generic abstraction for a smart home device */ /** Generic abstraction for a smart home device */
@Entity @Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) @Inheritance(strategy = InheritanceType.JOINED)
public abstract class Device { public abstract class Device {
/** Ways a device can behave in the automation flow. For now only input/output */ /** Ways a device can behave in the automation flow. For now only input/output */

View file

@ -1,13 +1,13 @@
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 ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
import javax.persistence.Column; import com.google.common.base.Objects;
import javax.persistence.Entity;
import javax.persistence.JoinColumn; import javax.persistence.*;
import javax.persistence.ManyToOne;
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;
import java.util.Set;
/** Represent a dimmable light */ /** Represent a dimmable light */
@Entity @Entity
@ -15,22 +15,18 @@ public class DimmableLight extends Switchable {
public static final Connector<ButtonDimmer, DimmableLight> public static final Connector<ButtonDimmer, DimmableLight>
BUTTON_DIMMER_DIMMABLE_LIGHT_CONNECTOR = BUTTON_DIMMER_DIMMABLE_LIGHT_CONNECTOR =
Connector.basic(ButtonDimmer::getOutputs, DimmableLight::setDimmerId); Connector.basic(ButtonDimmer::getOutputs, DimmableLight::getDimmers);
public static final Connector<KnobDimmer, DimmableLight> KNOB_DIMMER_DIMMABLE_LIGHT_CONNECTOR = public static final Connector<KnobDimmer, DimmableLight> KNOB_DIMMER_DIMMABLE_LIGHT_CONNECTOR =
Connector.basic(KnobDimmer::getOutputs, DimmableLight::setDimmerId); Connector.basic(KnobDimmer::getOutputs, DimmableLight::getDimmers);
public DimmableLight() { public DimmableLight() {
super("dimmableLight"); super("dimmableLight");
} }
@ManyToOne @ManyToMany(mappedBy = "dimmables", cascade = CascadeType.DETACH)
@GsonExclude @GsonExclude
@JoinColumn(name = "dimmer_id", updatable = false, insertable = false) private Set<Dimmer> dimmers;
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
@ -76,14 +72,15 @@ public class DimmableLight extends Switchable {
intensity = on ? oldIntensity : 0; intensity = on ? oldIntensity : 0;
} }
public void setDimmerId(Long dimmerId) { public Set<Dimmer> getDimmers() {
this.dimmerId = dimmerId; return this.dimmers;
super.setSwitchId(null);
} }
@Override @Override
public void setSwitchId(Long switchId) { public String toString() {
super.setSwitchId(switchId); return Objects.toStringHelper(this)
this.dimmerId = null; .add("intensity", intensity)
.add("oldIntensity", oldIntensity)
.toString();
} }
} }

View file

@ -1,12 +1,10 @@
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 java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.persistence.Entity; import javax.persistence.*;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.OneToMany;
import javax.persistence.PreRemove;
/** Represents a generic dimmer input device */ /** Represents a generic dimmer input device */
@Entity @Entity
@ -16,8 +14,14 @@ public abstract class Dimmer extends InputDevice {
super(kind); super(kind);
} }
@OneToMany(mappedBy = "dimmer") @ManyToMany(cascade = CascadeType.DETACH)
private Set<DimmableLight> lights = new HashSet<>(); @GsonExclude
@JoinTable(
name = "dimmer_dimmable",
joinColumns = @JoinColumn(name = "dimmer_id"),
inverseJoinColumns = @JoinColumn(name = "dimmable_id")
)
private Set<DimmableLight> dimmables = new HashSet<>();
/** /**
* Get the lights connected to this dimmer * Get the lights connected to this dimmer
@ -26,18 +30,11 @@ public abstract class Dimmer extends InputDevice {
*/ */
@Override @Override
public Set<DimmableLight> getOutputs() { public Set<DimmableLight> getOutputs() {
return this.lights; return this.dimmables;
} }
/** Add a light to be controller by this dimmer */ /** Add a light to be controller by this dimmer */
public void addDimmableLight(DimmableLight dimmableLight) { public void addDimmableLight(DimmableLight dimmableLight) {
lights.add(dimmableLight); dimmables.add(dimmableLight);
}
@PreRemove
private void removeLightsFromDimmer() {
for (DimmableLight dl : getOutputs()) {
dl.setDimmerId(null);
}
} }
} }

View file

@ -10,7 +10,7 @@ import javax.persistence.InheritanceType;
* environment (sensor) or the user (switch / dimmer). * environment (sensor) or the user (switch / dimmer).
*/ */
@Entity @Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) @Inheritance(strategy = InheritanceType.JOINED)
public abstract class InputDevice extends Device { public abstract class InputDevice extends Device {
public InputDevice(String kind) { public InputDevice(String kind) {
super(kind, FlowType.INPUT); super(kind, FlowType.INPUT);

View file

@ -7,7 +7,7 @@ import javax.persistence.*;
* ...). * ...).
*/ */
@Entity @Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) @Inheritance(strategy = InheritanceType.JOINED)
public abstract class OutputDevice extends Device { public abstract class OutputDevice extends Device {
public OutputDevice(String kind) { public OutputDevice(String kind) {
super(kind, FlowType.OUTPUT); super(kind, FlowType.OUTPUT);

View file

@ -1,5 +1,7 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models; package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import com.google.common.base.Objects;
import java.math.BigDecimal; import java.math.BigDecimal;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
@ -44,4 +46,12 @@ public class SmartPlug extends Switchable {
public SmartPlug() { public SmartPlug() {
super("smartPlug"); super("smartPlug");
} }
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("totalConsumption", totalConsumption)
.add("on", on)
.toString();
}
} }

View file

@ -1,17 +1,22 @@
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 java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.persistence.Column; import javax.persistence.*;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import javax.persistence.PreRemove;
/** A switch input device */ /** A switch input device */
@Entity @Entity
public class Switch extends InputDevice { public class Switch extends InputDevice {
@OneToMany(mappedBy = "switchDevice") @ManyToMany(cascade = CascadeType.DETACH)
@GsonExclude
@JoinTable(
name = "switch_switchable",
joinColumns = @JoinColumn(name = "switch_id"),
inverseJoinColumns = @JoinColumn(name = "switchable_id")
)
private Set<Switchable> switchables = new HashSet<>(); private Set<Switchable> switchables = new HashSet<>();
/** The state of this switch */ /** The state of this switch */
@ -52,11 +57,4 @@ public class Switch extends InputDevice {
public Set<Switchable> getOutputs() { public Set<Switchable> getOutputs() {
return switchables; return switchables;
} }
@PreRemove
public void removeSwitchable() {
for (Switchable s : getOutputs()) {
s.setSwitchId(null);
}
}
} }

View file

@ -2,22 +2,20 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude; import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
import javax.persistence.*; import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
/** A device that can be turned either on or off */ /** A device that can be turned either on or off */
@Entity @Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) @Inheritance(strategy = InheritanceType.JOINED)
public abstract class Switchable extends OutputDevice { public abstract class Switchable extends OutputDevice {
public static final Connector<Switch, Switchable> SWITCH_SWITCHABLE_CONNECTOR = public static final Connector<Switch, Switchable> SWITCH_SWITCHABLE_CONNECTOR =
Connector.basic(Switch::getOutputs, Switchable::setSwitchId); Connector.basic(Switch::getOutputs, Switchable::getSwitches);
@ManyToOne @ManyToMany(mappedBy = "switchables", cascade = CascadeType.DETACH)
@GsonExclude @GsonExclude
@JoinColumn(name = "switch_id", updatable = false, insertable = false) private Set<Switch> inputs = new HashSet<>();
private Switch switchDevice;
@Column(name = "switch_id")
private Long switchId;
protected Switchable(String kind) { protected Switchable(String kind) {
super(kind); super(kind);
@ -37,11 +35,7 @@ public abstract class Switchable extends OutputDevice {
*/ */
public abstract void setOn(boolean on); public abstract void setOn(boolean on);
public Long getSwitchId() { public Set<Switch> getSwitches() {
return switchId; return inputs;
}
public void setSwitchId(Long switchId) {
this.switchId = switchId;
} }
} }

View file

@ -14,7 +14,7 @@ public final class Utils {
void apply(T input) throws Throwable; void apply(T input) throws Throwable;
} }
public static <T> List<T> toList(Iterable<T> iterable) { public static <T> List<T> toList(Iterable<? extends T> iterable) {
return StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList()); return StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList());
} }