diff --git a/resources/windows/MainWindows.fxml b/resources/windows/MainWindows.fxml index 155f8fb..b872f9d 100644 --- a/resources/windows/MainWindows.fxml +++ b/resources/windows/MainWindows.fxml @@ -8,8 +8,7 @@ - + diff --git a/src/fr/uca/iut/clfreville2/MainApplication.java b/src/fr/uca/iut/clfreville2/MainApplication.java index ea3323e..fb770ec 100644 --- a/src/fr/uca/iut/clfreville2/MainApplication.java +++ b/src/fr/uca/iut/clfreville2/MainApplication.java @@ -1,7 +1,8 @@ package fr.uca.iut.clfreville2; +import fr.uca.iut.clfreville2.gui.FXMLUtils; +import fr.uca.iut.clfreville2.gui.MainWindows; import javafx.application.Application; -import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; @@ -10,7 +11,7 @@ public class MainApplication extends Application { @Override public void start(Stage primaryStage) throws Exception { - Parent root = FXMLLoader.load(MainApplication.class.getResource("/windows/MainWindows.fxml")); + Parent root = FXMLUtils.load(MainApplication.class.getResource("/windows/MainWindows.fxml"), new MainWindows(primaryStage)); Scene scene = new Scene(root); primaryStage.setScene(scene); primaryStage.show(); diff --git a/src/fr/uca/iut/clfreville2/gui/FXMLUtils.java b/src/fr/uca/iut/clfreville2/gui/FXMLUtils.java new file mode 100644 index 0000000..5d2c067 --- /dev/null +++ b/src/fr/uca/iut/clfreville2/gui/FXMLUtils.java @@ -0,0 +1,18 @@ +package fr.uca.iut.clfreville2.gui; + +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; + +import java.io.IOException; +import java.net.URL; + +public final class FXMLUtils { + + private FXMLUtils() {} + + public static Parent load(URL location, Object controller) throws IOException { + FXMLLoader loader = new FXMLLoader(location); + loader.setController(controller); + return loader.load(); + } +} diff --git a/src/fr/uca/iut/clfreville2/gui/MainWindows.java b/src/fr/uca/iut/clfreville2/gui/MainWindows.java index fa0fa5d..13ff9d9 100644 --- a/src/fr/uca/iut/clfreville2/gui/MainWindows.java +++ b/src/fr/uca/iut/clfreville2/gui/MainWindows.java @@ -3,25 +3,27 @@ package fr.uca.iut.clfreville2.gui; import fr.uca.iut.clfreville2.gui.image.ImageSupplier; import fr.uca.iut.clfreville2.gui.image.StandardImageSupplier; import fr.uca.iut.clfreville2.gui.list.NameableListCell; +import fr.uca.iut.clfreville2.gui.thread.Ticker; import fr.uca.iut.clfreville2.model.SensorRegistry; import fr.uca.iut.clfreville2.model.binding.ToBooleanBinding; import fr.uca.iut.clfreville2.model.sensor.ManualSensor; import fr.uca.iut.clfreville2.model.sensor.Sensor; import fr.uca.iut.clfreville2.persistence.StubSensorRegistryLoader; import javafx.fxml.FXML; -import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.ListView; import javafx.scene.control.Slider; import javafx.scene.control.TextField; import javafx.scene.image.ImageView; import javafx.scene.text.Text; +import javafx.stage.Stage; public class MainWindows { private final SensorRegistry registry = new StubSensorRegistryLoader().load(); private final ImageSupplier imageSupplier = new StandardImageSupplier(); - private ModalFactory modalFactory; + private final Ticker ticker; + private final ModalFactory modalFactory; @FXML private ListView sensorsList; @@ -38,13 +40,20 @@ public class MainWindows { @FXML private Button visualizeBtn; + public MainWindows(Stage primaryStage) { + this.ticker = new Ticker(registry); + this.modalFactory = new ModalFactory(primaryStage); + primaryStage.setOnHidden((e) -> ticker.interrupt()); + ticker.start(); + } + @FXML public void onChangeClick() { Sensor selected = getSelectedSensor(); if (!(selected instanceof ManualSensor sensor)) { return; } - getModalFactory().createModal(selected, root -> { + modalFactory.createModal(selected, root -> { Slider slider = new Slider(-5, 30, sensor.getTemperature()); slider.setBlockIncrement(0.5); slider.setMajorTickUnit(0.5); @@ -61,7 +70,7 @@ public class MainWindows { if (selected == null) { return; } - getModalFactory().createModal(selected, root -> { + modalFactory.createModal(selected, root -> { ImageView imageView = new ImageView(); imageView.setPreserveRatio(true); imageView.setFitHeight(200); @@ -107,15 +116,4 @@ public class MainWindows { private Sensor getSelectedSensor() { return sensorsList.getSelectionModel().getSelectedItem(); } - - private ModalFactory getModalFactory() { - if (modalFactory == null) { - Scene scene = sensorsList.getScene(); - if (scene == null) { - throw new IllegalStateException("No scene found"); - } - modalFactory = new ModalFactory(scene.getWindow()); - } - return modalFactory; - } } diff --git a/src/fr/uca/iut/clfreville2/gui/thread/Ticker.java b/src/fr/uca/iut/clfreville2/gui/thread/Ticker.java new file mode 100644 index 0000000..355460a --- /dev/null +++ b/src/fr/uca/iut/clfreville2/gui/thread/Ticker.java @@ -0,0 +1,40 @@ +package fr.uca.iut.clfreville2.gui.thread; + +import fr.uca.iut.clfreville2.model.shared.Tickable; +import javafx.application.Platform; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; + +public class Ticker extends Thread { + + private final Tickable tickable; + private final IntegerProperty millisPerTick = new SimpleIntegerProperty(250); + + public Ticker(Tickable tickable) { + this.tickable = tickable; + } + + @Override + public void run() { + while (true) { + try { + Thread.sleep(millisPerTick.getValue()); + Platform.runLater(tickable::tick); + } catch (InterruptedException e) { + break; + } + } + } + + public int getMillisPerTick() { + return millisPerTick.get(); + } + + public void setMillisPerTick(int millisPerTick) { + this.millisPerTick.set(millisPerTick); + } + + public IntegerProperty millisPerTickProperty() { + return millisPerTick; + } +} diff --git a/src/fr/uca/iut/clfreville2/model/SensorRegistry.java b/src/fr/uca/iut/clfreville2/model/SensorRegistry.java index 21d8387..4f3a12a 100644 --- a/src/fr/uca/iut/clfreville2/model/SensorRegistry.java +++ b/src/fr/uca/iut/clfreville2/model/SensorRegistry.java @@ -1,7 +1,10 @@ package fr.uca.iut.clfreville2.model; +import fr.uca.iut.clfreville2.model.sensor.AutoSensor; import fr.uca.iut.clfreville2.model.sensor.ManualSensor; import fr.uca.iut.clfreville2.model.sensor.Sensor; +import fr.uca.iut.clfreville2.model.sensor.auto.AutoUpdateStrategy; +import fr.uca.iut.clfreville2.model.shared.Tickable; import javafx.beans.property.ListProperty; import javafx.beans.property.ReadOnlyListProperty; import javafx.beans.property.SimpleListProperty; @@ -14,7 +17,7 @@ import java.util.function.IntSupplier; /** * A registry class to hold thermal sensors instances. */ -public class SensorRegistry implements Iterable { +public class SensorRegistry implements Tickable, Iterable { private final IntSupplier nextIdSupplier = new SequenceIntSupplier(); private final ObservableList sensors = FXCollections.observableArrayList(); @@ -30,6 +33,17 @@ public class SensorRegistry implements Iterable { return register(new ManualSensor(nextIdSupplier.getAsInt(), name)); } + /** + * Create a new auto sensor and add it to the registry. + * + * @param name The name of the sensor. + * @param strategy The strategy to update the sensor. + * @return The sensor created. + */ + public AutoSensor createAuto(String name, AutoUpdateStrategy strategy) { + return register(new AutoSensor(nextIdSupplier.getAsInt(), name, strategy)); + } + /** * Add a new sensor to the registry. * @@ -60,6 +74,15 @@ public class SensorRegistry implements Iterable { return sensorsList; } + @Override + public void tick() { + for (Sensor sensor : sensors) { + if (sensor instanceof Tickable tickable) { + tickable.tick(); + } + } + } + @Override public Iterator iterator() { return getSensors().iterator(); diff --git a/src/fr/uca/iut/clfreville2/model/sensor/AutoSensor.java b/src/fr/uca/iut/clfreville2/model/sensor/AutoSensor.java new file mode 100644 index 0000000..cbae785 --- /dev/null +++ b/src/fr/uca/iut/clfreville2/model/sensor/AutoSensor.java @@ -0,0 +1,41 @@ +package fr.uca.iut.clfreville2.model.sensor; + +import fr.uca.iut.clfreville2.model.sensor.auto.AutoUpdateStrategy; +import fr.uca.iut.clfreville2.model.shared.Tickable; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.ReadOnlyDoubleProperty; +import javafx.beans.property.SimpleDoubleProperty; + +import static java.util.Objects.requireNonNull; + +/** + * A sensor that automatically create new temperatures. + */ +public class AutoSensor extends Sensor implements Tickable { + + private final DoubleProperty temperature = new SimpleDoubleProperty(); + private AutoUpdateStrategy updateStrategy; + + public AutoSensor(int id, String name, AutoUpdateStrategy updateStrategy) { + super(id, name); + this.updateStrategy = requireNonNull(updateStrategy, "update strategy"); + } + + @Override + public void tick() { + temperature.set(updateStrategy.nextValue(this)); + } + + @Override + public ReadOnlyDoubleProperty temperatureProperty() { + return temperature; + } + + public AutoUpdateStrategy getUpdateStrategy() { + return updateStrategy; + } + + public void setUpdateStrategy(AutoUpdateStrategy updateStrategy) { + this.updateStrategy = requireNonNull(updateStrategy, "update strategy"); + } +} diff --git a/src/fr/uca/iut/clfreville2/model/sensor/Sensor.java b/src/fr/uca/iut/clfreville2/model/sensor/Sensor.java index b8e0d43..b503ab0 100644 --- a/src/fr/uca/iut/clfreville2/model/sensor/Sensor.java +++ b/src/fr/uca/iut/clfreville2/model/sensor/Sensor.java @@ -2,16 +2,13 @@ package fr.uca.iut.clfreville2.model.sensor; import fr.uca.iut.clfreville2.model.shared.TemperatureHolder; import fr.uca.iut.clfreville2.model.shared.ObservableIdentifiable; -import javafx.beans.binding.DoubleBinding; import javafx.beans.binding.DoubleExpression; import javafx.beans.binding.StringExpression; import javafx.beans.property.IntegerProperty; -import javafx.beans.property.ReadOnlyDoubleProperty; import javafx.beans.property.ReadOnlyIntegerProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; -import javafx.beans.value.ObservableDoubleValue; import static java.util.Objects.requireNonNull; diff --git a/src/fr/uca/iut/clfreville2/model/sensor/auto/AutoUpdateStrategy.java b/src/fr/uca/iut/clfreville2/model/sensor/auto/AutoUpdateStrategy.java new file mode 100644 index 0000000..4699952 --- /dev/null +++ b/src/fr/uca/iut/clfreville2/model/sensor/auto/AutoUpdateStrategy.java @@ -0,0 +1,15 @@ +package fr.uca.iut.clfreville2.model.sensor.auto; + +import fr.uca.iut.clfreville2.model.sensor.AutoSensor; + +@FunctionalInterface +public interface AutoUpdateStrategy { + + /** + * Computes the next value of the sensor. + * + * @param currentState The current state of the sensor. + * @return The next value of the sensor. + */ + double nextValue(AutoSensor currentState); +} diff --git a/src/fr/uca/iut/clfreville2/model/sensor/auto/RandomVariationStrategy.java b/src/fr/uca/iut/clfreville2/model/sensor/auto/RandomVariationStrategy.java new file mode 100644 index 0000000..140243f --- /dev/null +++ b/src/fr/uca/iut/clfreville2/model/sensor/auto/RandomVariationStrategy.java @@ -0,0 +1,25 @@ +package fr.uca.iut.clfreville2.model.sensor.auto; + +import fr.uca.iut.clfreville2.model.sensor.AutoSensor; + +import java.util.Random; + +public class RandomVariationStrategy implements AutoUpdateStrategy { + + private final Random random; + private final int maxVariation; + + public RandomVariationStrategy(int maxVariation) { + this(new Random(), maxVariation); + } + + public RandomVariationStrategy(Random random, int maxVariation) { + this.random = random; + this.maxVariation = maxVariation; + } + + @Override + public double nextValue(AutoSensor currentState) { + return currentState.getTemperature() + random.nextInt(maxVariation * 2) - maxVariation; + } +} diff --git a/src/fr/uca/iut/clfreville2/model/shared/ObservableIdentifiable.java b/src/fr/uca/iut/clfreville2/model/shared/ObservableIdentifiable.java index fa7d81f..ca4fd72 100644 --- a/src/fr/uca/iut/clfreville2/model/shared/ObservableIdentifiable.java +++ b/src/fr/uca/iut/clfreville2/model/shared/ObservableIdentifiable.java @@ -31,5 +31,12 @@ public interface ObservableIdentifiable extends Identifiable { * @return The display name. * @see #getDisplayName() */ - StringExpression displayNameExpression(); + default StringExpression displayNameExpression() { + return nameProperty(); + } + + @Override + default String getDisplayName() { + return displayNameExpression().get(); + } } diff --git a/src/fr/uca/iut/clfreville2/model/shared/Tickable.java b/src/fr/uca/iut/clfreville2/model/shared/Tickable.java new file mode 100644 index 0000000..114ff5a --- /dev/null +++ b/src/fr/uca/iut/clfreville2/model/shared/Tickable.java @@ -0,0 +1,10 @@ +package fr.uca.iut.clfreville2.model.shared; + +@FunctionalInterface +public interface Tickable { + + /** + * Tick the current object. + */ + void tick(); +} diff --git a/src/fr/uca/iut/clfreville2/persistence/StubSensorRegistryLoader.java b/src/fr/uca/iut/clfreville2/persistence/StubSensorRegistryLoader.java index f910142..15624f3 100644 --- a/src/fr/uca/iut/clfreville2/persistence/StubSensorRegistryLoader.java +++ b/src/fr/uca/iut/clfreville2/persistence/StubSensorRegistryLoader.java @@ -1,6 +1,7 @@ package fr.uca.iut.clfreville2.persistence; import fr.uca.iut.clfreville2.model.SensorRegistry; +import fr.uca.iut.clfreville2.model.sensor.auto.RandomVariationStrategy; public class StubSensorRegistryLoader implements SensorRegistryLoader { @Override @@ -8,7 +9,7 @@ public class StubSensorRegistryLoader implements SensorRegistryLoader { SensorRegistry registry = new SensorRegistry(); registry.createManual("Sensor 1"); registry.createManual("Sensor 2"); - registry.createManual("Sensor 3"); + registry.createAuto("Sensor 3", new RandomVariationStrategy(5)); return registry; } }