diff --git a/resources/windows/MainWindows.fxml b/resources/windows/MainWindows.fxml index 001e922..736e5f3 100644 --- a/resources/windows/MainWindows.fxml +++ b/resources/windows/MainWindows.fxml @@ -11,6 +11,8 @@ + +
@@ -29,6 +31,12 @@ + + + + + +
diff --git a/src/fr/uca/iut/clfreville2/gui/MainWindows.java b/src/fr/uca/iut/clfreville2/gui/MainWindows.java index ae6e6fb..436bd66 100644 --- a/src/fr/uca/iut/clfreville2/gui/MainWindows.java +++ b/src/fr/uca/iut/clfreville2/gui/MainWindows.java @@ -3,18 +3,24 @@ 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.table.WeightSpinnerTableCell; 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.model.sensor.VirtualSensor; import fr.uca.iut.clfreville2.persistence.StubSensorRegistryLoader; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.ListView; import javafx.scene.control.Slider; import javafx.scene.control.Spinner; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.image.ImageView; import javafx.scene.text.Text; @@ -42,6 +48,15 @@ public class MainWindows { @FXML private Button visualizeBtn; + @FXML + private TableView sourcesView; + + @FXML + private TableColumn sourceWeight; + + @FXML + private TableColumn sourceId; + @FXML private Spinner updateInterval; @@ -92,6 +107,7 @@ public class MainWindows { private void initialize() { bindSensorList(); bindActiveButtons(); + bindSources(); bindUpdate(); } @@ -102,10 +118,15 @@ public class MainWindows { sensorsList.getSelectionModel().selectedItemProperty().addListener((list, oldValue, newValue) -> { if (oldValue != null) { sensorName.textProperty().unbindBidirectional(oldValue.nameProperty()); + sourcesView.itemsProperty().unbind(); + sourcesView.setItems(FXCollections.emptyObservableList()); } if (newValue != null) { sensorId.textProperty().bind(newValue.displayNameExpression()); sensorName.textProperty().bindBidirectional(newValue.nameProperty()); + if (newValue instanceof VirtualSensor virtual) { + sourcesView.itemsProperty().bind(virtual.sourcesProperty()); + } } else { sensorId.textProperty().unbind(); sensorId.setText(null); @@ -122,6 +143,13 @@ public class MainWindows { visualizeBtn.visibleProperty().bind(sensorsList.getSelectionModel().selectedItemProperty().isNotNull()); } + @FXML + private void bindSources() { + sourceWeight.setCellValueFactory(cell -> new SimpleObjectProperty<>(cell.getValue())); + sourceWeight.setCellFactory(cell -> new WeightSpinnerTableCell()); + sourceId.setCellValueFactory(cell -> cell.getValue().sensor().idProperty().asString()); + } + @FXML private void bindUpdate() { updateInterval.getValueFactory().valueProperty().bindBidirectional(ticker.millisPerTickProperty().asObject()); diff --git a/src/fr/uca/iut/clfreville2/gui/table/WeightSpinnerTableCell.java b/src/fr/uca/iut/clfreville2/gui/table/WeightSpinnerTableCell.java new file mode 100644 index 0000000..d19289b --- /dev/null +++ b/src/fr/uca/iut/clfreville2/gui/table/WeightSpinnerTableCell.java @@ -0,0 +1,28 @@ +package fr.uca.iut.clfreville2.gui.table; + +import fr.uca.iut.clfreville2.model.sensor.VirtualSensor; +import javafx.beans.property.ObjectProperty; +import javafx.scene.control.Spinner; +import javafx.scene.control.SpinnerValueFactory; +import javafx.scene.control.TableCell; + +public class WeightSpinnerTableCell extends TableCell { + + private final Spinner weight = new Spinner<>(new SpinnerValueFactory.DoubleSpinnerValueFactory(0.0D, 10.0)); + private ObjectProperty weightValueObj; + + @Override + protected void updateItem(VirtualSensor.DataSource item, boolean empty) { + super.updateItem(item, empty); + if (empty) { + if (weightValueObj != null) { + weight.getValueFactory().valueProperty().unbindBidirectional(weightValueObj); + } + setGraphic(null); + } else { + weightValueObj = item.weightProperty().asObject(); + weight.getValueFactory().valueProperty().bindBidirectional(weightValueObj); + setGraphic(weight); + } + } +} diff --git a/src/fr/uca/iut/clfreville2/model/SensorRegistry.java b/src/fr/uca/iut/clfreville2/model/SensorRegistry.java index 4f3a12a..f1355c1 100644 --- a/src/fr/uca/iut/clfreville2/model/SensorRegistry.java +++ b/src/fr/uca/iut/clfreville2/model/SensorRegistry.java @@ -3,6 +3,7 @@ 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.VirtualSensor; import fr.uca.iut.clfreville2.model.sensor.auto.AutoUpdateStrategy; import fr.uca.iut.clfreville2.model.shared.Tickable; import javafx.beans.property.ListProperty; @@ -44,6 +45,16 @@ public class SensorRegistry implements Tickable, Iterable { return register(new AutoSensor(nextIdSupplier.getAsInt(), name, strategy)); } + /** + * Create a new virtual sensor and add it to the registry. + * + * @param name The name of the sensor. + * @return The sensor created. + */ + public VirtualSensor createVirtual(String name) { + return register(new VirtualSensor(nextIdSupplier.getAsInt(), name)); + } + /** * Add a new sensor to the registry. * diff --git a/src/fr/uca/iut/clfreville2/model/sensor/VirtualSensor.java b/src/fr/uca/iut/clfreville2/model/sensor/VirtualSensor.java new file mode 100644 index 0000000..ee6fdcb --- /dev/null +++ b/src/fr/uca/iut/clfreville2/model/sensor/VirtualSensor.java @@ -0,0 +1,73 @@ +package fr.uca.iut.clfreville2.model.sensor; + +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.ListProperty; +import javafx.beans.property.ReadOnlyDoubleProperty; +import javafx.beans.property.ReadOnlyListProperty; +import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.SimpleListProperty; +import javafx.beans.value.ChangeListener; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +import static java.util.Objects.requireNonNull; + +public class VirtualSensor extends Sensor { + + private final ObservableList sources = FXCollections.observableArrayList(); + private final ListProperty sourcesProperty = new SimpleListProperty<>(sources); + private final SimpleDoubleProperty temperature = new SimpleDoubleProperty(); + private final ChangeListener changeHandler = (s, old, next) -> compute(); + + public VirtualSensor(int id, String name) { + super(id, name); + } + + public void addSource(Sensor sensor, double weight) { + sources.add(new DataSource(requireNonNull(sensor, "sensor"), weight)); + sensor.temperatureProperty().addListener(changeHandler); + } + + public boolean removeSource(Sensor sensor) { + return sources.removeIf(source -> { + if (source.sensor().equals(sensor)) { + sensor.temperatureProperty().removeListener(changeHandler); + return true; + } + return false; + }); + } + + @Override + public ReadOnlyDoubleProperty temperatureProperty() { + return temperature; + } + + public ReadOnlyListProperty sourcesProperty() { + return sourcesProperty; + } + + protected void compute() { + temperature.set(sources.stream() + .mapToDouble(sensor -> sensor.temperature() * sensor.weight()) + .sum() / + sources.stream() + .mapToDouble(DataSource::weight) + .sum()); + } + + public record DataSource(Sensor sensor, DoubleProperty weightProperty) { + + public DataSource(Sensor sensor, double weight) { + this(sensor, new SimpleDoubleProperty(weight)); + } + + public double temperature() { + return sensor().getTemperature(); + } + + public double weight() { + return weightProperty.doubleValue(); + } + } +} diff --git a/src/fr/uca/iut/clfreville2/persistence/StubSensorRegistryLoader.java b/src/fr/uca/iut/clfreville2/persistence/StubSensorRegistryLoader.java index 15624f3..40a7ba4 100644 --- a/src/fr/uca/iut/clfreville2/persistence/StubSensorRegistryLoader.java +++ b/src/fr/uca/iut/clfreville2/persistence/StubSensorRegistryLoader.java @@ -1,15 +1,19 @@ package fr.uca.iut.clfreville2.persistence; import fr.uca.iut.clfreville2.model.SensorRegistry; +import fr.uca.iut.clfreville2.model.sensor.AutoSensor; +import fr.uca.iut.clfreville2.model.sensor.VirtualSensor; import fr.uca.iut.clfreville2.model.sensor.auto.RandomVariationStrategy; public class StubSensorRegistryLoader implements SensorRegistryLoader { @Override public SensorRegistry load() { SensorRegistry registry = new SensorRegistry(); - registry.createManual("Sensor 1"); - registry.createManual("Sensor 2"); - registry.createAuto("Sensor 3", new RandomVariationStrategy(5)); + registry.createManual("Manual 1"); + registry.createManual("Manual 2"); + AutoSensor autoSensor = registry.createAuto("Random 1", new RandomVariationStrategy(5)); + VirtualSensor virtualSensor = registry.createVirtual("Virtual 1"); + virtualSensor.addSource(autoSensor, 1.0D); return registry; } }