Add virtual sensors
This commit is contained in:
@@ -11,6 +11,8 @@
|
|||||||
<?import javafx.scene.layout.FlowPane?>
|
<?import javafx.scene.layout.FlowPane?>
|
||||||
<?import javafx.scene.control.CheckBox?>
|
<?import javafx.scene.control.CheckBox?>
|
||||||
<?import javafx.scene.control.Spinner?>
|
<?import javafx.scene.control.Spinner?>
|
||||||
|
<?import javafx.scene.control.TableView?>
|
||||||
|
<?import javafx.scene.control.TableColumn?>
|
||||||
<BorderPane xmlns:fx="http://javafx.com/fxml" prefHeight="400.0" prefWidth="600.0">
|
<BorderPane xmlns:fx="http://javafx.com/fxml" prefHeight="400.0" prefWidth="600.0">
|
||||||
<center>
|
<center>
|
||||||
<SplitPane>
|
<SplitPane>
|
||||||
@@ -29,6 +31,12 @@
|
|||||||
<Button fx:id="visualizeBtn" onAction="#onVisualizeClick">Visualize</Button>
|
<Button fx:id="visualizeBtn" onAction="#onVisualizeClick">Visualize</Button>
|
||||||
</right>
|
</right>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
|
<TableView fx:id="sourcesView">
|
||||||
|
<columns>
|
||||||
|
<TableColumn fx:id="sourceWeight" text="Weight" />
|
||||||
|
<TableColumn fx:id="sourceId" text="Id" />
|
||||||
|
</columns>
|
||||||
|
</TableView>
|
||||||
</VBox>
|
</VBox>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</center>
|
</center>
|
||||||
|
@@ -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.ImageSupplier;
|
||||||
import fr.uca.iut.clfreville2.gui.image.StandardImageSupplier;
|
import fr.uca.iut.clfreville2.gui.image.StandardImageSupplier;
|
||||||
import fr.uca.iut.clfreville2.gui.list.NameableListCell;
|
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.gui.thread.Ticker;
|
||||||
import fr.uca.iut.clfreville2.model.SensorRegistry;
|
import fr.uca.iut.clfreville2.model.SensorRegistry;
|
||||||
import fr.uca.iut.clfreville2.model.binding.ToBooleanBinding;
|
import fr.uca.iut.clfreville2.model.binding.ToBooleanBinding;
|
||||||
import fr.uca.iut.clfreville2.model.sensor.ManualSensor;
|
import fr.uca.iut.clfreville2.model.sensor.ManualSensor;
|
||||||
import fr.uca.iut.clfreville2.model.sensor.Sensor;
|
import fr.uca.iut.clfreville2.model.sensor.Sensor;
|
||||||
|
import fr.uca.iut.clfreville2.model.sensor.VirtualSensor;
|
||||||
import fr.uca.iut.clfreville2.persistence.StubSensorRegistryLoader;
|
import fr.uca.iut.clfreville2.persistence.StubSensorRegistryLoader;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.ListView;
|
import javafx.scene.control.ListView;
|
||||||
import javafx.scene.control.Slider;
|
import javafx.scene.control.Slider;
|
||||||
import javafx.scene.control.Spinner;
|
import javafx.scene.control.Spinner;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
@@ -42,6 +48,15 @@ public class MainWindows {
|
|||||||
@FXML
|
@FXML
|
||||||
private Button visualizeBtn;
|
private Button visualizeBtn;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableView<VirtualSensor.DataSource> sourcesView;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableColumn<VirtualSensor.DataSource, VirtualSensor.DataSource> sourceWeight;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableColumn<VirtualSensor.DataSource, String> sourceId;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Spinner<Integer> updateInterval;
|
private Spinner<Integer> updateInterval;
|
||||||
|
|
||||||
@@ -92,6 +107,7 @@ public class MainWindows {
|
|||||||
private void initialize() {
|
private void initialize() {
|
||||||
bindSensorList();
|
bindSensorList();
|
||||||
bindActiveButtons();
|
bindActiveButtons();
|
||||||
|
bindSources();
|
||||||
bindUpdate();
|
bindUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,10 +118,15 @@ public class MainWindows {
|
|||||||
sensorsList.getSelectionModel().selectedItemProperty().addListener((list, oldValue, newValue) -> {
|
sensorsList.getSelectionModel().selectedItemProperty().addListener((list, oldValue, newValue) -> {
|
||||||
if (oldValue != null) {
|
if (oldValue != null) {
|
||||||
sensorName.textProperty().unbindBidirectional(oldValue.nameProperty());
|
sensorName.textProperty().unbindBidirectional(oldValue.nameProperty());
|
||||||
|
sourcesView.itemsProperty().unbind();
|
||||||
|
sourcesView.setItems(FXCollections.emptyObservableList());
|
||||||
}
|
}
|
||||||
if (newValue != null) {
|
if (newValue != null) {
|
||||||
sensorId.textProperty().bind(newValue.displayNameExpression());
|
sensorId.textProperty().bind(newValue.displayNameExpression());
|
||||||
sensorName.textProperty().bindBidirectional(newValue.nameProperty());
|
sensorName.textProperty().bindBidirectional(newValue.nameProperty());
|
||||||
|
if (newValue instanceof VirtualSensor virtual) {
|
||||||
|
sourcesView.itemsProperty().bind(virtual.sourcesProperty());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sensorId.textProperty().unbind();
|
sensorId.textProperty().unbind();
|
||||||
sensorId.setText(null);
|
sensorId.setText(null);
|
||||||
@@ -122,6 +143,13 @@ public class MainWindows {
|
|||||||
visualizeBtn.visibleProperty().bind(sensorsList.getSelectionModel().selectedItemProperty().isNotNull());
|
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
|
@FXML
|
||||||
private void bindUpdate() {
|
private void bindUpdate() {
|
||||||
updateInterval.getValueFactory().valueProperty().bindBidirectional(ticker.millisPerTickProperty().asObject());
|
updateInterval.getValueFactory().valueProperty().bindBidirectional(ticker.millisPerTickProperty().asObject());
|
||||||
|
@@ -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<VirtualSensor.DataSource, VirtualSensor.DataSource> {
|
||||||
|
|
||||||
|
private final Spinner<Double> weight = new Spinner<>(new SpinnerValueFactory.DoubleSpinnerValueFactory(0.0D, 10.0));
|
||||||
|
private ObjectProperty<Double> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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.AutoSensor;
|
||||||
import fr.uca.iut.clfreville2.model.sensor.ManualSensor;
|
import fr.uca.iut.clfreville2.model.sensor.ManualSensor;
|
||||||
import fr.uca.iut.clfreville2.model.sensor.Sensor;
|
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.sensor.auto.AutoUpdateStrategy;
|
||||||
import fr.uca.iut.clfreville2.model.shared.Tickable;
|
import fr.uca.iut.clfreville2.model.shared.Tickable;
|
||||||
import javafx.beans.property.ListProperty;
|
import javafx.beans.property.ListProperty;
|
||||||
@@ -44,6 +45,16 @@ public class SensorRegistry implements Tickable, Iterable<Sensor> {
|
|||||||
return register(new AutoSensor(nextIdSupplier.getAsInt(), name, strategy));
|
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.
|
* Add a new sensor to the registry.
|
||||||
*
|
*
|
||||||
|
73
src/fr/uca/iut/clfreville2/model/sensor/VirtualSensor.java
Normal file
73
src/fr/uca/iut/clfreville2/model/sensor/VirtualSensor.java
Normal file
@@ -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<DataSource> sources = FXCollections.observableArrayList();
|
||||||
|
private final ListProperty<DataSource> sourcesProperty = new SimpleListProperty<>(sources);
|
||||||
|
private final SimpleDoubleProperty temperature = new SimpleDoubleProperty();
|
||||||
|
private final ChangeListener<Number> 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<DataSource> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,15 +1,19 @@
|
|||||||
package fr.uca.iut.clfreville2.persistence;
|
package fr.uca.iut.clfreville2.persistence;
|
||||||
|
|
||||||
import fr.uca.iut.clfreville2.model.SensorRegistry;
|
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;
|
import fr.uca.iut.clfreville2.model.sensor.auto.RandomVariationStrategy;
|
||||||
|
|
||||||
public class StubSensorRegistryLoader implements SensorRegistryLoader {
|
public class StubSensorRegistryLoader implements SensorRegistryLoader {
|
||||||
@Override
|
@Override
|
||||||
public SensorRegistry load() {
|
public SensorRegistry load() {
|
||||||
SensorRegistry registry = new SensorRegistry();
|
SensorRegistry registry = new SensorRegistry();
|
||||||
registry.createManual("Sensor 1");
|
registry.createManual("Manual 1");
|
||||||
registry.createManual("Sensor 2");
|
registry.createManual("Manual 2");
|
||||||
registry.createAuto("Sensor 3", new RandomVariationStrategy(5));
|
AutoSensor autoSensor = registry.createAuto("Random 1", new RandomVariationStrategy(5));
|
||||||
|
VirtualSensor virtualSensor = registry.createVirtual("Virtual 1");
|
||||||
|
virtualSensor.addSource(autoSensor, 1.0D);
|
||||||
return registry;
|
return registry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user