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;
}
}