New abstraction for connections

This commit is contained in:
Claudio Maggioni 2020-03-14 12:40:28 +01:00
parent 6fef7663dd
commit ed2900a3bc
16 changed files with 170 additions and 178 deletions

View file

@ -24,7 +24,7 @@ public class ButtonDimmerController
@Autowired @Autowired
protected ButtonDimmerController( protected ButtonDimmerController(
ButtonDimmerRepository inputRepository, DimmableLightRepository outputRepository) { ButtonDimmerRepository inputRepository, DimmableLightRepository outputRepository) {
super(inputRepository, outputRepository); super(inputRepository, outputRepository, ButtonDimmer.CONNECTOR);
this.buttonDimmerRepository = inputRepository; this.buttonDimmerRepository = inputRepository;
this.dimmableLightRepository = outputRepository; this.dimmableLightRepository = outputRepository;
} }
@ -63,20 +63,20 @@ public class ButtonDimmerController
break; break;
} }
dimmableLightRepository.saveAll(buttonDimmer.getLights()); dimmableLightRepository.saveAll(buttonDimmer.getOutputs());
return buttonDimmer.getOutputs(); return buttonDimmer.getOutputs();
} }
@PostMapping("/{id}/lights") @PostMapping("/{id}/lights")
public Set<DimmableLight> addLight( public Set<? extends OutputDevice> addLight(
@PathVariable("id") long inputId, @RequestParam("lightId") Long lightId) @PathVariable("id") long inputId, @RequestParam("lightId") Long lightId)
throws NotFoundException { throws NotFoundException {
return addOutput(inputId, lightId); return addOutput(inputId, lightId);
} }
@DeleteMapping("/{id}/lights") @DeleteMapping("/{id}/lights")
public Set<DimmableLight> removeLight( public Set<? extends OutputDevice> removeLight(
@PathVariable("id") long inputId, @RequestParam("lightId") Long lightId) @PathVariable("id") long inputId, @RequestParam("lightId") Long lightId)
throws NotFoundException { throws NotFoundException {
return removeOutput(inputId, lightId); return removeOutput(inputId, lightId);

View file

@ -12,8 +12,7 @@ import java.util.Set;
* @param <O> the output device attached to I * @param <O> the output device attached to I
*/ */
public abstract class InputDeviceConnectionController< public abstract class InputDeviceConnectionController<
I extends InputDevice & OutputConnectable<O>, I extends InputDevice, O extends OutputDevice> {
O extends OutputDevice & InputConnectable<? super I>> {
private class IOPair { private class IOPair {
private final I input; private final I input;
@ -29,10 +28,22 @@ public abstract class InputDeviceConnectionController<
private DeviceRepository<O> outputReposiory; private DeviceRepository<O> outputReposiory;
private Connector<I, O> connector;
/**
* Contstructs the controller by requiring essential object for the controller implementation
*
* @param inputRepository the input device repository
* @param outputRepository the output device repository
* @param connector a appropriate Connector instance for the I and O tyoes.
*/
protected InputDeviceConnectionController( protected InputDeviceConnectionController(
DeviceRepository<I> inputRepository, DeviceRepository<O> outputRepository) { DeviceRepository<I> inputRepository,
DeviceRepository<O> outputRepository,
Connector<I, O> connector) {
this.inputRepository = inputRepository; this.inputRepository = inputRepository;
this.outputReposiory = outputRepository; this.outputReposiory = outputRepository;
this.connector = connector;
} }
private IOPair checkConnectionIDs(Long inputId, Long outputId) throws NotFoundException { private IOPair checkConnectionIDs(Long inputId, Long outputId) throws NotFoundException {
@ -55,10 +66,10 @@ public abstract class InputDeviceConnectionController<
* @return the list of output devices attached to the input device of id inputId * @return the list of output devices attached to the input device of id inputId
* @throws NotFoundException if inputId or outputId are not valid * @throws NotFoundException if inputId or outputId are not valid
*/ */
protected Set<O> addOutput(Long inputId, Long outputId) throws NotFoundException { protected Set<? extends OutputDevice> addOutput(Long inputId, Long outputId)
throws NotFoundException {
final IOPair pair = checkConnectionIDs(inputId, outputId); final IOPair pair = checkConnectionIDs(inputId, outputId);
pair.input.getOutputs().add(pair.output); connector.connect(pair.input, pair.output, true);
pair.output.connect(pair.input.getId());
outputReposiory.save(pair.output); outputReposiory.save(pair.output);
return pair.input.getOutputs(); return pair.input.getOutputs();
} }
@ -71,10 +82,10 @@ public abstract class InputDeviceConnectionController<
* @return the list of output devices attached to the input device of id inputId * @return the list of output devices attached to the input device of id inputId
* @throws NotFoundException if inputId or outputId are not valid * @throws NotFoundException if inputId or outputId are not valid
*/ */
protected Set<O> removeOutput(Long inputId, Long outputId) throws NotFoundException { protected Set<? extends OutputDevice> removeOutput(Long inputId, Long outputId)
throws NotFoundException {
final IOPair pair = checkConnectionIDs(inputId, outputId); final IOPair pair = checkConnectionIDs(inputId, outputId);
pair.input.getOutputs().remove(pair.output); connector.connect(pair.input, pair.output, false);
pair.output.connect(null);
outputReposiory.save(pair.output); outputReposiory.save(pair.output);
return pair.input.getOutputs(); return pair.input.getOutputs();
} }

View file

@ -25,7 +25,7 @@ public class KnobDimmerController
@Autowired @Autowired
protected KnobDimmerController( protected KnobDimmerController(
KnobDimmerRepository inputRepository, DimmableLightRepository outputRepository) { KnobDimmerRepository inputRepository, DimmableLightRepository outputRepository) {
super(inputRepository, outputRepository); super(inputRepository, outputRepository, KnobDimmer.CONNECTOR);
this.knobDimmerRepository = inputRepository; this.knobDimmerRepository = inputRepository;
this.dimmableLightRepository = outputRepository; this.dimmableLightRepository = outputRepository;
} }
@ -56,20 +56,20 @@ public class KnobDimmerController
knobDimmerRepository.findById(bd.getId()).orElseThrow(NotFoundException::new); knobDimmerRepository.findById(bd.getId()).orElseThrow(NotFoundException::new);
dimmer.setLightIntensity(bd.getIntensity()); dimmer.setLightIntensity(bd.getIntensity());
dimmableLightRepository.saveAll(dimmer.getLights()); dimmableLightRepository.saveAll(dimmer.getOutputs());
return dimmer.getOutputs(); return dimmer.getOutputs();
} }
@PostMapping("/{id}/lights") @PostMapping("/{id}/lights")
public Set<DimmableLight> addLight( public Set<? extends OutputDevice> addLight(
@PathVariable("id") long inputId, @RequestParam("lightId") Long lightId) @PathVariable("id") long inputId, @RequestParam("lightId") Long lightId)
throws NotFoundException { throws NotFoundException {
return addOutput(inputId, lightId); return addOutput(inputId, lightId);
} }
@DeleteMapping("/{id}/lights") @DeleteMapping("/{id}/lights")
public Set<DimmableLight> removeLight( public Set<? extends OutputDevice> removeLight(
@PathVariable("id") long inputId, @RequestParam("lightId") Long lightId) @PathVariable("id") long inputId, @RequestParam("lightId") Long lightId)
throws NotFoundException { throws NotFoundException {
return removeOutput(inputId, lightId); return removeOutput(inputId, lightId);

View file

@ -1,77 +1,35 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models; package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.Set;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.OneToMany;
/** /**
* Represents a dimmer that can only instruct an increase or decrease of intensity (i.e. like a * Represents a dimmer that can only instruct an increase or decrease of intensity (i.e. like a
* dimmer with a '+' and a '-' button) * dimmer with a '+' and a '-' button)
*/ */
@Entity @Entity
public class ButtonDimmer extends Dimmer implements OutputConnectable<DimmableLight> { public class ButtonDimmer extends Dimmer {
public static final Connector<ButtonDimmer, DimmableLight> CONNECTOR =
Connector.basic(ButtonDimmer::getOutputs, DimmableLight::setDimmerId);
/** The delta amount to apply to a increase or decrease intensity */ /** The delta amount to apply to a increase or decrease intensity */
public static final int DIM_INCREMENT = 10; private static final int DIM_INCREMENT = 10;
public ButtonDimmer() { public ButtonDimmer() {
super("button-dimmer"); super("button-dimmer");
} }
@OneToMany(mappedBy = "dimmer")
private Set<DimmableLight> lights;
/** Increases the current intensity level of the dimmable light by DIM_INCREMENT */ /** 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 : getOutputs()) {
dl.setIntensity(dl.getIntensity() + DIM_INCREMENT); dl.setIntensity(dl.getIntensity() + DIM_INCREMENT);
} }
} }
/** Decreases the current intensity level of the dimmable light by DIM_INCREMENT */ /** 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 : getOutputs()) {
dl.setIntensity(dl.getIntensity() - DIM_INCREMENT); dl.setIntensity(dl.getIntensity() - DIM_INCREMENT);
} }
} }
/**
* Adds a DimmableLight to this set of DimmableLights
*
* @param dl The DimmableLight to be added
*/
public void addLight(DimmableLight dl) {
lights.add(dl);
}
/**
* Removes the given DimmableLight
*
* @param dl The DimmableLight to be removed
*/
public void removeLight(DimmableLight dl) {
lights.remove(dl);
}
/** Clears this set */
public void clearSet() {
lights.clear();
}
/**
* Get the lights
*
* @return duh
*/
public Set<DimmableLight> getLights() {
return this.lights;
}
@Override
public Set<DimmableLight> getOutputs() {
return this.lights;
}
public void setLights(Set<DimmableLight> newLights) {
this.lights = newLights;
}
} }

View file

@ -0,0 +1,47 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;
/**
* A rule on how to connect an input device type to an output device type
*
* @param <I> the input device type
* @param <O> the output device type
*/
@FunctionalInterface
public interface Connector<I extends InputDevice, O extends OutputDevice> {
/**
* Connects or disconnects input to output
*
* @param input the input device
* @param output the output device
* @param connect true if connection, false if disconnection
*/
void connect(I input, O output, boolean connect);
/**
* Produces a basic implementation of a connector, assuming there is a OneToMany relationship
* between J and K
*
* @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 <J> the input device type
* @param <K> the output device type
* @return a Connector implementation for the pair of types J and K
*/
static <J extends InputDevice, K extends OutputDevice> Connector<J, K> basic(
Function<J, Set<K>> outputsGetter, BiConsumer<K, Long> inputSetter) {
return (i, o, connect) -> {
if (connect) {
outputsGetter.apply(i).add(o);
} else {
outputsGetter.apply(i).remove(o);
}
inputSetter.accept(o, connect ? i.getId() : null);
};
}
}

View file

@ -11,7 +11,7 @@ import javax.validation.constraints.NotNull;
/** Represent a dimmable light */ /** Represent a dimmable light */
@Entity @Entity
public class DimmableLight extends Light implements InputConnectable<Dimmer> { public class DimmableLight extends OutputDevice implements Switchable {
public DimmableLight() { public DimmableLight() {
super("light"); super("light");
@ -46,29 +46,24 @@ public class DimmableLight extends Light implements InputConnectable<Dimmer> {
public void setIntensity(Integer intensity) { public void setIntensity(Integer intensity) {
if (intensity <= 0) { if (intensity <= 0) {
this.intensity = 0; this.intensity = 0;
setOn(false); } else if (intensity > 100) {
this.intensity = 100;
} else { } else {
setOn(true); this.intensity = intensity;
if (intensity > 100) {
this.intensity = 100;
} else {
this.intensity = intensity;
}
} }
} }
@Override
public boolean isOn() {
return intensity != 0;
}
@Override @Override
public void setOn(boolean on) { public void setOn(boolean on) {
super.setOn(on); intensity = on ? 100 : 0;
if (on) {
intensity = 100;
} else {
intensity = 0;
}
} }
@Override public void setDimmerId(Long dimmerId) {
public void connect(Long inputId) { this.dimmerId = dimmerId;
this.dimmerId = inputId; };
}
} }

View file

@ -1,8 +1,10 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models; package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.Set;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Inheritance; import javax.persistence.Inheritance;
import javax.persistence.InheritanceType; import javax.persistence.InheritanceType;
import javax.persistence.OneToMany;
/** Represents a generic dimmer input device */ /** Represents a generic dimmer input device */
@Entity @Entity
@ -11,4 +13,17 @@ public abstract class Dimmer extends InputDevice {
public Dimmer(String kind) { public Dimmer(String kind) {
super(kind); super(kind);
} }
@OneToMany(mappedBy = "dimmer")
private Set<DimmableLight> lights;
/**
* Get the lights connected to this dimmer
*
* @return duh
*/
@Override
public Set<DimmableLight> getOutputs() {
return this.lights;
}
} }

View file

@ -1,18 +0,0 @@
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

@ -1,5 +1,6 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models; package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.Set;
import javax.persistence.Entity; import javax.persistence.Entity;
/** /**
@ -11,4 +12,8 @@ public abstract class InputDevice extends Device {
public InputDevice(String kind) { public InputDevice(String kind) {
super(kind, FlowType.INPUT); super(kind, FlowType.INPUT);
} }
public Set<? extends OutputDevice> getOutputs() {
return Set.of();
}
} }

View file

@ -1,44 +1,29 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models; package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.Set;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.OneToMany;
/** /**
* Represents a dimmer able to set absolute intensity values (i.e. knowing the absolute intensity * Represents a dimmer able to set absolute intensity values (i.e. knowing the absolute intensity
* value, like a knob) * value, like a knob)
*/ */
@Entity @Entity
public class KnobDimmer extends Dimmer implements OutputConnectable<DimmableLight> { public class KnobDimmer extends Dimmer {
public static final Connector<KnobDimmer, DimmableLight> CONNECTOR =
Connector.basic(KnobDimmer::getOutputs, DimmableLight::setDimmerId);
public KnobDimmer() { public KnobDimmer() {
super("knob-dimmer"); super("knob-dimmer");
} }
@OneToMany(mappedBy = "dimmer")
private Set<DimmableLight> lights;
/** /**
* Sets absolutely the intensity level of all lights connected * Sets absolutely the intensity level of all lights connected
* *
* @param intensity the intensity (must be from 0 to 100) * @param intensity the intensity (must be from 0 to 100)
*/ */
public void setLightIntensity(int intensity) { public void setLightIntensity(int intensity) {
for (DimmableLight dl : getOutputs()) {
for (DimmableLight dl : lights) {
dl.setIntensity(intensity); dl.setIntensity(intensity);
} }
} }
public void setLights(Set<DimmableLight> lights) {
this.lights = lights;
}
public Set<DimmableLight> getLights() {
return lights;
}
@Override
public Set<DimmableLight> getOutputs() {
return lights;
}
} }

View file

@ -1,31 +0,0 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.validation.constraints.NotNull;
/** Represents a generic light */
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Light extends OutputDevice {
/** Whether the light is on or not */
@Column(name = "light_on", nullable = false)
@NotNull
boolean on;
protected Light(String kind) {
super(kind);
this.on = false;
}
public boolean isOn() {
return on;
}
public void setOn(boolean on) {
this.on = on;
}
}

View file

@ -1,18 +0,0 @@
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

@ -1,8 +1,6 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models; package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import javax.persistence.Entity; import javax.persistence.*;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
/** /**
* Represents a generic output device, i.e. something that causes some behaviour (light, smartPlugs, * Represents a generic output device, i.e. something that causes some behaviour (light, smartPlugs,

View file

@ -1,11 +1,30 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models; package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.validation.constraints.NotNull;
/** Represents a standard non-dimmable light */ /** Represents a standard non-dimmable light */
@Entity @Entity
public class RegularLight extends Light { public class RegularLight extends OutputDevice implements Switchable {
/** Whether the light is on or not */
@Column(name = "light_on", nullable = false)
@NotNull
boolean on;
public RegularLight() { public RegularLight() {
super("regular-light"); super("regular-light");
this.on = false;
}
@Override
public boolean isOn() {
return on;
}
@Override
public void setOn(boolean on) {
this.on = on;
} }
} }

View file

@ -6,17 +6,19 @@ import javax.validation.constraints.NotNull;
/** A smart plug that can be turned either on or off */ /** A smart plug that can be turned either on or off */
@Entity @Entity
public class SmartPlug extends OutputDevice { public class SmartPlug extends OutputDevice implements Switchable {
/** Whether the smart plug is on */ /** Whether the smart plug is on */
@Column(name = "smart_plug_on", nullable = false) @Column(name = "smart_plug_on", nullable = false)
@NotNull @NotNull
private boolean on; private boolean on;
@Override
public boolean isOn() { public boolean isOn() {
return on; return on;
} }
@Override
public void setOn(boolean on) { public void setOn(boolean on) {
this.on = on; this.on = on;
} }

View file

@ -0,0 +1,24 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
/** A device that can be turned either on or off */
public interface Switchable {
/**
* Returns whether the device is on (true) or not (false)
*
* @return whether the device is on (true) or not (false)
*/
boolean isOn();
/**
* Sets the on status of the device
*
* @param on the new on status: true for on, false for off
*/
void setOn(boolean on);
/** Toggle between on are off state */
default void toggle() {
setOn(!isOn());
}
}