Moving msp to kotlin
This commit is contained in:
parent
debd4a707d
commit
1d82ccf123
@ -28,7 +28,6 @@ import javafx.scene.layout.Priority
|
|||||||
import javafx.scene.layout.VBox
|
import javafx.scene.layout.VBox
|
||||||
import javafx.scene.text.Font
|
import javafx.scene.text.Font
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
import java.time.Duration
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,11 +35,8 @@ import java.time.Instant
|
|||||||
*/
|
*/
|
||||||
class PKT8ViewConnection : DeviceViewConnection<PKT8Device>(), MeasurementListener {
|
class PKT8ViewConnection : DeviceViewConnection<PKT8Device>(), MeasurementListener {
|
||||||
private val cryoView by lazy { CryoView() }
|
private val cryoView by lazy { CryoView() }
|
||||||
private val plotView by lazy { CryoPlotView() }
|
|
||||||
|
|
||||||
internal val table = FXCollections.observableHashMap<String, PKT8Result>()
|
internal val table = FXCollections.observableHashMap<String, PKT8Result>()
|
||||||
|
|
||||||
|
|
||||||
val lastUpdateProperty = SimpleObjectProperty<String>("NEVER")
|
val lastUpdateProperty = SimpleObjectProperty<String>("NEVER")
|
||||||
|
|
||||||
|
|
||||||
@ -155,11 +151,7 @@ class PKT8ViewConnection : DeviceViewConnection<PKT8Device>(), MeasurementListen
|
|||||||
|
|
||||||
var rawDataButton: ToggleButton by singleAssign()
|
var rawDataButton: ToggleButton by singleAssign()
|
||||||
|
|
||||||
val plottables: TimePlottableGroup by lazy {
|
val plottables: TimePlottableGroup = TimePlottableGroup()
|
||||||
TimePlottableGroup().apply {
|
|
||||||
setMaxAge(Duration.parse(plotFrameMeta.getString("maxAge", "PT2H")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val root: Parent = borderpane {
|
override val root: Parent = borderpane {
|
||||||
prefWidth = 800.0
|
prefWidth = 800.0
|
||||||
|
@ -28,6 +28,7 @@ import hep.dataforge.control.devices.StateDef;
|
|||||||
import hep.dataforge.control.measurements.AbstractMeasurement;
|
import hep.dataforge.control.measurements.AbstractMeasurement;
|
||||||
import hep.dataforge.control.ports.PortHandler;
|
import hep.dataforge.control.ports.PortHandler;
|
||||||
import hep.dataforge.control.ports.TcpPortHandler;
|
import hep.dataforge.control.ports.TcpPortHandler;
|
||||||
|
import hep.dataforge.description.ValueDef;
|
||||||
import hep.dataforge.events.EventBuilder;
|
import hep.dataforge.events.EventBuilder;
|
||||||
import hep.dataforge.exceptions.ControlException;
|
import hep.dataforge.exceptions.ControlException;
|
||||||
import hep.dataforge.exceptions.MeasurementException;
|
import hep.dataforge.exceptions.MeasurementException;
|
||||||
@ -54,10 +55,16 @@ import java.util.function.Consumer;
|
|||||||
*/
|
*/
|
||||||
@RoleDef(name = Roles.STORAGE_ROLE, objectType = StorageConnection.class)
|
@RoleDef(name = Roles.STORAGE_ROLE, objectType = StorageConnection.class)
|
||||||
@RoleDef(name = Roles.VIEW_ROLE)
|
@RoleDef(name = Roles.VIEW_ROLE)
|
||||||
@StateDef(name = PortSensor.CONNECTED_STATE, writable = true, info = "Connection with the device itself")
|
@StateDef(
|
||||||
@StateDef(name = "storing", writable = true, info = "Define if this device is currently writes to storage")
|
value = @ValueDef(name = PortSensor.CONNECTED_STATE, info = "Connection with the device itself"),
|
||||||
@StateDef(name = "filamentOn", writable = true, info = "Mass-spectrometer filament on")
|
writable = true
|
||||||
@StateDef(name = "filamentStatus", info = "Filament status")
|
)
|
||||||
|
@StateDef(
|
||||||
|
value = @ValueDef(name = "storing", info = "Define if this device is currently writes to storage"),
|
||||||
|
writable = true
|
||||||
|
)
|
||||||
|
@StateDef(value = @ValueDef(name = "filamentOn", info = "Mass-spectrometer filament on"), writable = true)
|
||||||
|
@StateDef(@ValueDef(name = "filamentStatus", info = "Filament status"))
|
||||||
public class MspDevice extends Sensor<DataPoint> implements PortHandler.PortController {
|
public class MspDevice extends Sensor<DataPoint> implements PortHandler.PortController {
|
||||||
public static final String MSP_DEVICE_TYPE = "msp";
|
public static final String MSP_DEVICE_TYPE = "msp";
|
||||||
|
|
||||||
@ -104,8 +111,8 @@ public class MspDevice extends Sensor<DataPoint> implements PortHandler.PortCont
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PeakJumpMeasurement createMeasurement() throws MeasurementException{
|
protected PeakJumpMeasurement createMeasurement() throws MeasurementException {
|
||||||
Meta measurementMeta =meta().getMeta("peakJump");
|
Meta measurementMeta = meta().getMeta("peakJump");
|
||||||
String s = measurementMeta.getString("type", "peakJump");
|
String s = measurementMeta.getString("type", "peakJump");
|
||||||
if (s.equals("peakJump")) {
|
if (s.equals("peakJump")) {
|
||||||
PeakJumpMeasurement measurement = new PeakJumpMeasurement(measurementMeta);
|
PeakJumpMeasurement measurement = new PeakJumpMeasurement(measurementMeta);
|
||||||
|
@ -5,7 +5,6 @@ import hep.dataforge.control.devices.Device;
|
|||||||
import hep.dataforge.meta.Meta;
|
import hep.dataforge.meta.Meta;
|
||||||
import inr.numass.control.DeviceViewConnection;
|
import inr.numass.control.DeviceViewConnection;
|
||||||
import inr.numass.control.DeviceViewFactory;
|
import inr.numass.control.DeviceViewFactory;
|
||||||
import inr.numass.control.msp.fx.MspView;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by darksnake on 09-May-17.
|
* Created by darksnake on 09-May-17.
|
||||||
@ -24,6 +23,6 @@ public class MspDeviceFactory implements DeviceViewFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DeviceViewConnection buildView(Device device) {
|
public DeviceViewConnection buildView(Device device) {
|
||||||
return MspView.build(device.getContext());
|
return MspViewConnection.Companion.build(device.getContext());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2015 Alexander Nozik.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package inr.numass.control.msp.fx;
|
|
||||||
|
|
||||||
import hep.dataforge.control.devices.DeviceFactory;
|
|
||||||
import hep.dataforge.meta.Meta;
|
|
||||||
import inr.numass.control.DeviceViewConnection;
|
|
||||||
import inr.numass.control.NumassControlApplication;
|
|
||||||
import inr.numass.control.msp.MspDevice;
|
|
||||||
import inr.numass.control.msp.MspDeviceFactory;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author darksnake
|
|
||||||
*/
|
|
||||||
public class MspApp extends NumassControlApplication<MspDevice> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected DeviceViewConnection<MspDevice> buildView(MspDevice device) {
|
|
||||||
return MspView.build(device.getContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected DeviceFactory getDeviceFactory() {
|
|
||||||
return new MspDeviceFactory();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setupStage(Stage stage, MspDevice device) {
|
|
||||||
stage.setTitle("Numass mass-spectrometer view");
|
|
||||||
stage.setMinHeight(400);
|
|
||||||
stage.setMinWidth(600);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean acceptDevice(Meta meta) {
|
|
||||||
return Objects.equals(meta.getString("name"), "msp");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,269 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2015 Alexander Nozik.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package inr.numass.control.msp.fx;
|
|
||||||
|
|
||||||
import hep.dataforge.context.Context;
|
|
||||||
import hep.dataforge.control.NamedValueListener;
|
|
||||||
import hep.dataforge.control.devices.Device;
|
|
||||||
import hep.dataforge.control.devices.DeviceListener;
|
|
||||||
import hep.dataforge.exceptions.ControlException;
|
|
||||||
import hep.dataforge.exceptions.PortException;
|
|
||||||
import hep.dataforge.fx.fragments.FragmentWindow;
|
|
||||||
import hep.dataforge.fx.fragments.LogFragment;
|
|
||||||
import hep.dataforge.meta.Meta;
|
|
||||||
import hep.dataforge.meta.MetaBuilder;
|
|
||||||
import hep.dataforge.plots.data.TimePlottable;
|
|
||||||
import hep.dataforge.plots.data.TimePlottableGroup;
|
|
||||||
import hep.dataforge.plots.fx.PlotContainer;
|
|
||||||
import hep.dataforge.plots.jfreechart.JFreeChartFrame;
|
|
||||||
import hep.dataforge.values.Value;
|
|
||||||
import inr.numass.control.DeviceViewConnection;
|
|
||||||
import inr.numass.control.msp.MspDevice;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.value.ChangeListener;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.event.ActionEvent;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.fxml.FXMLLoader;
|
|
||||||
import javafx.fxml.Initializable;
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.Alert;
|
|
||||||
import javafx.scene.control.ComboBox;
|
|
||||||
import javafx.scene.control.ToggleButton;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
|
||||||
import javafx.scene.paint.Paint;
|
|
||||||
import javafx.scene.shape.Circle;
|
|
||||||
import javafx.util.StringConverter;
|
|
||||||
import org.controlsfx.control.ToggleSwitch;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FXML Controller class
|
|
||||||
*
|
|
||||||
* @author darksnake
|
|
||||||
*/
|
|
||||||
public class MspView extends DeviceViewConnection<MspDevice> implements DeviceListener, Initializable, NamedValueListener {
|
|
||||||
|
|
||||||
public static MspView build(Context context) {
|
|
||||||
try {
|
|
||||||
FXMLLoader loader = new FXMLLoader(context.getClassLoader().getResource("fxml/MspView.fxml"));
|
|
||||||
loader.setClassLoader(context.getClassLoader());
|
|
||||||
loader.load();
|
|
||||||
return loader.getController();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new Error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final TimePlottableGroup plottables = new TimePlottableGroup();
|
|
||||||
// private Configuration viewConfig;
|
|
||||||
private JFreeChartFrame plot;
|
|
||||||
private LogFragment logFragment;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private BorderPane root;
|
|
||||||
@FXML
|
|
||||||
private ToggleSwitch filamentButton;
|
|
||||||
@FXML
|
|
||||||
private Circle filamentIndicator;
|
|
||||||
@FXML
|
|
||||||
private ToggleButton measureButton;
|
|
||||||
@FXML
|
|
||||||
private BorderPane plotPane;
|
|
||||||
@FXML
|
|
||||||
public ToggleButton connectButton;
|
|
||||||
@FXML
|
|
||||||
private ToggleButton consoleButton;
|
|
||||||
@FXML
|
|
||||||
private ComboBox<Integer> filamentSelector;
|
|
||||||
@FXML
|
|
||||||
private ToggleButton storeButton;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the controller class.
|
|
||||||
*
|
|
||||||
* @param url
|
|
||||||
* @param rb
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void initialize(URL url, ResourceBundle rb) {
|
|
||||||
logFragment = new LogFragment();
|
|
||||||
new FragmentWindow(logFragment).bindTo(consoleButton);
|
|
||||||
logFragment.addRootLogHandler();
|
|
||||||
filamentSelector.setItems(FXCollections.observableArrayList(1, 2));
|
|
||||||
filamentSelector.setConverter(new StringConverter<Integer>() {
|
|
||||||
@Override
|
|
||||||
public String toString(Integer object) {
|
|
||||||
return "Filament " + object;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Integer fromString(String string) {
|
|
||||||
return Integer.parseInt(string.substring(9));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
filamentSelector.getSelectionModel().select(0);
|
|
||||||
filamentButton.selectedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> {
|
|
||||||
try {
|
|
||||||
filamentSelector.setDisable(newValue);
|
|
||||||
getDevice().setFilamentOn(newValue);
|
|
||||||
} catch (PortException ex) {
|
|
||||||
getDevice().getLogger().error("Failed to toggle filaments");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
filamentButton.disableProperty().bind(connectButton.selectedProperty().not());
|
|
||||||
measureButton.disableProperty().bind(filamentButton.selectedProperty().not());
|
|
||||||
storeButton.disableProperty().bind(measureButton.selectedProperty().not());
|
|
||||||
getStateBinding("filamentStatus").addListener(new ChangeListener<Value>() {
|
|
||||||
@Override
|
|
||||||
public void changed(ObservableValue<? extends Value> observable, Value oldValue, Value newValue) {
|
|
||||||
String filamentState = newValue.stringValue();
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
switch (filamentState) {
|
|
||||||
case "ON":
|
|
||||||
filamentIndicator.setFill(Paint.valueOf("red"));
|
|
||||||
break;
|
|
||||||
case "OFF":
|
|
||||||
filamentIndicator.setFill(Paint.valueOf("blue"));
|
|
||||||
break;
|
|
||||||
case "WARM-UP":
|
|
||||||
case "COOL-DOWN":
|
|
||||||
filamentIndicator.setFill(Paint.valueOf("yellow"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Meta getViewConfig() {
|
|
||||||
return getDevice().meta().getMeta("plotConfig", getDevice().getMeta());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open(MspDevice device) throws Exception {
|
|
||||||
super.open(device);
|
|
||||||
updatePlot();
|
|
||||||
|
|
||||||
bindBooleanToState("connected", connectButton.selectedProperty());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initPlot() {
|
|
||||||
Meta plotConfig = new MetaBuilder("plotFrame")
|
|
||||||
.setNode(new MetaBuilder("yAxis")
|
|
||||||
.setValue("type", "log")
|
|
||||||
.setValue("axisTitle", "partial pressure")
|
|
||||||
.setValue("axisUnits", "mbar")
|
|
||||||
)
|
|
||||||
.setValue("xAxis.type", "time");
|
|
||||||
|
|
||||||
this.plot = new JFreeChartFrame(plotConfig);
|
|
||||||
PlotContainer container = PlotContainer.centerIn(plotPane);
|
|
||||||
container.setPlot(plot);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updatePlot() {
|
|
||||||
if (plot == null) {
|
|
||||||
initPlot();
|
|
||||||
}
|
|
||||||
Meta config = getViewConfig();
|
|
||||||
if (config.hasMeta("plotFrame")) {
|
|
||||||
this.plot.configure(config.getMeta("plotFrame"));
|
|
||||||
}
|
|
||||||
if (config.hasMeta("peakJump.peak")) {
|
|
||||||
for (Meta peakMeta : config.getMetaList("peakJump.peak")) {
|
|
||||||
String mass = peakMeta.getString("mass");
|
|
||||||
if (!this.plottables.has(mass)) {
|
|
||||||
TimePlottable newPlottable = new TimePlottable(mass, mass);
|
|
||||||
newPlottable.configure(peakMeta);
|
|
||||||
newPlottable.setMaxItems(1000);
|
|
||||||
newPlottable.setPrefItems(400);
|
|
||||||
newPlottable.configureValue("titleBase",peakMeta.getString("title",mass));
|
|
||||||
this.plottables.add(newPlottable);
|
|
||||||
plot.add(newPlottable);
|
|
||||||
} else {
|
|
||||||
plottables.get(mass).configure(peakMeta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showError("No peaks defined in config");
|
|
||||||
throw new RuntimeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void evaluateDeviceException(Device device, String message, Throwable exception) {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
logFragment.appendLine("ERROR: " + message);
|
|
||||||
showError(message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private void onPlotToggle(ActionEvent event) throws ControlException {
|
|
||||||
if (measureButton.isSelected()) {
|
|
||||||
getDevice().startMeasurement();
|
|
||||||
} else {
|
|
||||||
getDevice().stopMeasurement(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showError(String message) {
|
|
||||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
|
||||||
alert.setTitle("Error!");
|
|
||||||
alert.setHeaderText(null);
|
|
||||||
alert.setContentText(message);
|
|
||||||
|
|
||||||
alert.showAndWait();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showInfo(String message) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private void onStoreButtonClick(ActionEvent event) {
|
|
||||||
getDevice().setState("storing", storeButton.isSelected());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Node getFXNode() {
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pushValue(String valueName, Value value) {
|
|
||||||
TimePlottable pl = plottables.get(valueName);
|
|
||||||
if (pl != null) {
|
|
||||||
if (value.doubleValue() > 0) {
|
|
||||||
pl.put(value);
|
|
||||||
} else {
|
|
||||||
pl.put(Value.NULL);
|
|
||||||
}
|
|
||||||
String titleBase = pl.getConfig().getString("titleBase");
|
|
||||||
String title = String.format("%s (%.4g)", titleBase, value.doubleValue());
|
|
||||||
pl.configureValue("title", title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package inr.numass.control.msp
|
||||||
|
|
||||||
|
import hep.dataforge.control.devices.DeviceFactory
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import inr.numass.control.DeviceViewConnection
|
||||||
|
import inr.numass.control.NumassControlApplication
|
||||||
|
import javafx.stage.Stage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author darksnake
|
||||||
|
*/
|
||||||
|
class MspApp : NumassControlApplication<MspDevice>() {
|
||||||
|
|
||||||
|
override fun buildView(device: MspDevice): DeviceViewConnection<MspDevice> {
|
||||||
|
return MspViewConnection.build(device.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val deviceFactory: DeviceFactory = MspDeviceFactory()
|
||||||
|
|
||||||
|
|
||||||
|
override fun setupStage(stage: Stage, device: MspDevice) {
|
||||||
|
stage.title = "Numass mass-spectrometer view"
|
||||||
|
stage.minHeight = 400.0
|
||||||
|
stage.minWidth = 600.0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun acceptDevice(meta: Meta): Boolean {
|
||||||
|
return meta.getString("name") == "msp"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,252 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package inr.numass.control.msp
|
||||||
|
|
||||||
|
import hep.dataforge.control.NamedValueListener
|
||||||
|
import hep.dataforge.control.devices.Device
|
||||||
|
import hep.dataforge.control.devices.DeviceListener
|
||||||
|
import hep.dataforge.control.devices.PortSensor
|
||||||
|
import hep.dataforge.control.devices.Sensor
|
||||||
|
import hep.dataforge.control.devices.Sensor.MEASURING_STATE
|
||||||
|
import hep.dataforge.fx.fragments.FragmentWindow
|
||||||
|
import hep.dataforge.fx.fragments.LogFragment
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.MetaBuilder
|
||||||
|
import hep.dataforge.plots.PlotUtils
|
||||||
|
import hep.dataforge.plots.data.TimePlottable
|
||||||
|
import hep.dataforge.plots.data.TimePlottableGroup
|
||||||
|
import hep.dataforge.plots.fx.FXPlotFrame
|
||||||
|
import hep.dataforge.plots.fx.PlotContainer
|
||||||
|
import hep.dataforge.plots.jfreechart.JFreeChartFrame
|
||||||
|
import hep.dataforge.values.Value
|
||||||
|
import inr.numass.control.DeviceViewConnection
|
||||||
|
import inr.numass.control.deviceStateIndicator
|
||||||
|
import javafx.application.Platform
|
||||||
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
|
import javafx.geometry.Insets
|
||||||
|
import javafx.geometry.Orientation
|
||||||
|
import javafx.scene.Node
|
||||||
|
import javafx.scene.Parent
|
||||||
|
import javafx.scene.control.Alert
|
||||||
|
import javafx.scene.control.ToggleButton
|
||||||
|
import javafx.scene.layout.Priority
|
||||||
|
import javafx.scene.layout.VBox
|
||||||
|
import javafx.scene.paint.Paint
|
||||||
|
import org.controlsfx.control.ToggleSwitch
|
||||||
|
import tornadofx.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FXML Controller class
|
||||||
|
|
||||||
|
* @author darksnake
|
||||||
|
*/
|
||||||
|
class MspViewConnection : DeviceViewConnection<MspDevice>(), DeviceListener, NamedValueListener {
|
||||||
|
private val mspView by lazy { MspView() }
|
||||||
|
|
||||||
|
override fun getBoardView(): Parent {
|
||||||
|
return VBox().apply {
|
||||||
|
this += super.getBoardView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFXNode(): Node {
|
||||||
|
if (!isOpen) {
|
||||||
|
throw RuntimeException("Not connected!")
|
||||||
|
}
|
||||||
|
return mspView.root;
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pushValue(valueName: String, value: Value) {
|
||||||
|
val pl = plottables.get(valueName)
|
||||||
|
if (pl != null) {
|
||||||
|
if (value.doubleValue() > 0) {
|
||||||
|
pl.put(value)
|
||||||
|
} else {
|
||||||
|
pl.put(Value.NULL)
|
||||||
|
}
|
||||||
|
val titleBase = pl.config.getString("titleBase")
|
||||||
|
val title = String.format("%s (%.4g)", titleBase, value.doubleValue())
|
||||||
|
pl.configureValue("title", title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inner class MspView : View("Numass mass-spectrometer measurement") {
|
||||||
|
val plotFrameMeta: Meta = device.meta().getMeta("plotConfig", device.meta)
|
||||||
|
|
||||||
|
val plotFrame: FXPlotFrame by lazy {
|
||||||
|
val basePlotConfig = MetaBuilder("plotFrame")
|
||||||
|
.setNode(MetaBuilder("yAxis")
|
||||||
|
.setValue("type", "log")
|
||||||
|
.setValue("axisTitle", "partial pressure")
|
||||||
|
.setValue("axisUnits", "mbar")
|
||||||
|
)
|
||||||
|
.setValue("xAxis.type", "time")
|
||||||
|
|
||||||
|
|
||||||
|
JFreeChartFrame(basePlotConfig).apply {
|
||||||
|
PlotUtils.setXAxis(this, "timestamp", null, "time")
|
||||||
|
configure(plotFrameMeta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val plottables: TimePlottableGroup = TimePlottableGroup()
|
||||||
|
|
||||||
|
private var logButton: ToggleButton by singleAssign()
|
||||||
|
|
||||||
|
private val logWindow = FragmentWindow(LogFragment().apply {
|
||||||
|
addLogHandler(device.logger)
|
||||||
|
})
|
||||||
|
|
||||||
|
val filamentProperty = SimpleObjectProperty<Int>().apply {
|
||||||
|
addListener { _, oldValue, newValue ->
|
||||||
|
if (newValue != oldValue) {
|
||||||
|
device.selectFillament(newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val root = borderpane {
|
||||||
|
minHeight = 400.0
|
||||||
|
minWidth = 600.0
|
||||||
|
top {
|
||||||
|
toolbar {
|
||||||
|
togglebutton("Connect") {
|
||||||
|
isSelected = false
|
||||||
|
bindBooleanToState(PortSensor.CONNECTED_STATE, selectedProperty())
|
||||||
|
}
|
||||||
|
combobox(filamentProperty, listOf(1, 2)) {
|
||||||
|
cellFormat {
|
||||||
|
text = "Filament $it"
|
||||||
|
}
|
||||||
|
selectionModel.selectFirst()
|
||||||
|
disableProperty()
|
||||||
|
.bind(getStateBinding(MEASURING_STATE).booleanBinding { it!!.booleanValue() })
|
||||||
|
}
|
||||||
|
add(ToggleSwitch().apply {
|
||||||
|
padding = Insets(11.0, 0.0, 0.0, 0.0)
|
||||||
|
disableProperty()
|
||||||
|
.bind(getStateBinding(PortSensor.CONNECTED_STATE).booleanBinding { !it!!.booleanValue() })
|
||||||
|
bindBooleanToState("filamentOn", selectedProperty())
|
||||||
|
})
|
||||||
|
deviceStateIndicator(this@MspViewConnection, "filamentStatus") {
|
||||||
|
when (it.stringValue()) {
|
||||||
|
"ON" -> Paint.valueOf("red")
|
||||||
|
"OFF" -> Paint.valueOf("blue")
|
||||||
|
"WARM-UP", "COOL-DOWN" -> Paint.valueOf("yellow")
|
||||||
|
else -> error("Unknown filament state")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
separator(Orientation.VERTICAL)
|
||||||
|
|
||||||
|
togglebutton("Measure") {
|
||||||
|
isSelected = false
|
||||||
|
disableProperty()
|
||||||
|
.bind(getStateBinding(PortSensor.CONNECTED_STATE).booleanBinding { !it!!.booleanValue() })
|
||||||
|
|
||||||
|
bindBooleanToState(Sensor.MEASURING_STATE, selectedProperty())
|
||||||
|
}
|
||||||
|
togglebutton("Store") {
|
||||||
|
isSelected = false
|
||||||
|
disableProperty()
|
||||||
|
.bind(getStateBinding(Sensor.MEASURING_STATE).booleanBinding { !it!!.booleanValue() })
|
||||||
|
bindBooleanToState("storing", selectedProperty())
|
||||||
|
}
|
||||||
|
separator(Orientation.VERTICAL)
|
||||||
|
pane {
|
||||||
|
hgrow = Priority.ALWAYS
|
||||||
|
}
|
||||||
|
separator(Orientation.VERTICAL)
|
||||||
|
|
||||||
|
logButton = togglebutton("Log") {
|
||||||
|
isSelected = false
|
||||||
|
logWindow.bindTo(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PlotContainer.centerIn(this).plot = plotFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
private var plot: JFreeChartFrame? = null
|
||||||
|
private var logFragment: LogFragment? = null
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun open(device: MspDevice) {
|
||||||
|
super.open(device)
|
||||||
|
updatePlot()
|
||||||
|
|
||||||
|
bindBooleanToState("connected", connectButton!!.selectedProperty())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initPlot() {
|
||||||
|
val plotConfig = MetaBuilder("plotFrame")
|
||||||
|
.setNode(MetaBuilder("yAxis")
|
||||||
|
.setValue("type", "log")
|
||||||
|
.setValue("axisTitle", "partial pressure")
|
||||||
|
.setValue("axisUnits", "mbar")
|
||||||
|
)
|
||||||
|
.setValue("xAxis.type", "time")
|
||||||
|
|
||||||
|
this.plot = JFreeChartFrame(plotConfig)
|
||||||
|
val container = PlotContainer.centerIn(plotPane)
|
||||||
|
container.plot = plot
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updatePlot() {
|
||||||
|
if (plot == null) {
|
||||||
|
initPlot()
|
||||||
|
}
|
||||||
|
if (plotFrameMeta.hasMeta("plotFrame")) {
|
||||||
|
this.plot!!.configure(plotFrameMeta.getMeta("plotFrame"))
|
||||||
|
}
|
||||||
|
if (plotFrameMeta.hasMeta("peakJump.peak")) {
|
||||||
|
for (peakMeta in plotFrameMeta.getMetaList("peakJump.peak")) {
|
||||||
|
val mass = peakMeta.getString("mass")
|
||||||
|
if (!this.plottables.has(mass)) {
|
||||||
|
val newPlottable = TimePlottable(mass, mass)
|
||||||
|
newPlottable.configure(peakMeta)
|
||||||
|
newPlottable.setMaxItems(1000)
|
||||||
|
newPlottable.setPrefItems(400)
|
||||||
|
newPlottable.configureValue("titleBase", peakMeta.getString("title", mass))
|
||||||
|
this.plottables.add(newPlottable)
|
||||||
|
plot!!.add(newPlottable)
|
||||||
|
} else {
|
||||||
|
plottables.get(mass).configure(peakMeta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showError("No peaks defined in config")
|
||||||
|
throw RuntimeException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun evaluateDeviceException(device: Device, message: String, exception: Throwable) {
|
||||||
|
Platform.runLater {
|
||||||
|
logFragment!!.appendLine("ERROR: " + message)
|
||||||
|
showError(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showError(message: String) {
|
||||||
|
val alert = Alert(Alert.AlertType.ERROR)
|
||||||
|
alert.title = "Error!"
|
||||||
|
alert.headerText = null
|
||||||
|
alert.contentText = message
|
||||||
|
|
||||||
|
alert.showAndWait()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,26 +23,28 @@ limitations under the License.
|
|||||||
<?import javafx.scene.layout.Pane?>
|
<?import javafx.scene.layout.Pane?>
|
||||||
<?import javafx.scene.shape.Circle?>
|
<?import javafx.scene.shape.Circle?>
|
||||||
<?import org.controlsfx.control.ToggleSwitch?>
|
<?import org.controlsfx.control.ToggleSwitch?>
|
||||||
<BorderPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="400.0" minWidth="600.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="inr.numass.control.msp.fx.MspView">
|
<BorderPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="400.0" minWidth="600.0"
|
||||||
|
xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="inr.numass.control.msp.MspViewConnection">
|
||||||
<top>
|
<top>
|
||||||
<ToolBar prefHeight="50.0" prefWidth="200.0">
|
<ToolBar prefHeight="50.0" prefWidth="200.0">
|
||||||
<ToggleButton fx:id="connectButton" mnemonicParsing="false" text="Connect" />
|
<ToggleButton fx:id="connectButton" mnemonicParsing="false" text="Connect"/>
|
||||||
<ComboBox fx:id="filamentSelector" promptText="Fillament 1" visibleRowCount="2" />
|
<ComboBox fx:id="filamentSelector" promptText="Fillament 1" visibleRowCount="2"/>
|
||||||
<ToggleSwitch fx:id="filamentButton" prefHeight="40.0" prefWidth="35.0">
|
<ToggleSwitch fx:id="filamentButton" prefHeight="40.0" prefWidth="35.0">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets top="11.0" />
|
<Insets top="11.0"/>
|
||||||
</padding>
|
</padding>
|
||||||
</ToggleSwitch>
|
</ToggleSwitch>
|
||||||
<Circle fx:id="filamentIndicator" fill="GRAY" radius="10.0" stroke="BLACK" strokeType="INSIDE" />
|
<Circle fx:id="filamentIndicator" fill="GRAY" radius="10.0" stroke="BLACK" strokeType="INSIDE"/>
|
||||||
<Separator orientation="VERTICAL" prefHeight="20.0" />
|
<Separator orientation="VERTICAL" prefHeight="20.0"/>
|
||||||
<ToggleButton fx:id="measureButton" mnemonicParsing="false" onAction="#onPlotToggle" text="Measure" />
|
<ToggleButton fx:id="measureButton" mnemonicParsing="false" onAction="#onPlotToggle" text="Measure"/>
|
||||||
<ToggleButton fx:id="storeButton" mnemonicParsing="false" onAction="#onStoreButtonClick" text="Store" />
|
<ToggleButton fx:id="storeButton" mnemonicParsing="false" onAction="#onStoreButtonClick" text="Store"/>
|
||||||
<Separator orientation="VERTICAL" prefHeight="20.0" />
|
<Separator orientation="VERTICAL" prefHeight="20.0"/>
|
||||||
<Pane HBox.hgrow="ALWAYS" />
|
<Pane HBox.hgrow="ALWAYS"/>
|
||||||
<ToggleButton fx:id="consoleButton" mnemonicParsing="false" text="Console" />
|
<ToggleButton fx:id="consoleButton" mnemonicParsing="false" text="Console"/>
|
||||||
</ToolBar>
|
</ToolBar>
|
||||||
</top>
|
</top>
|
||||||
<center>
|
<center>
|
||||||
<BorderPane fx:id="plotPane" minHeight="400.0" minWidth="600.0" prefHeight="200.0" prefWidth="200.0" />
|
<BorderPane fx:id="plotPane" minHeight="400.0" minWidth="600.0" prefHeight="200.0" prefWidth="200.0"/>
|
||||||
</center>
|
</center>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
|
@ -11,6 +11,7 @@ import hep.dataforge.fx.fragments.FragmentWindow
|
|||||||
import hep.dataforge.values.Value
|
import hep.dataforge.values.Value
|
||||||
import javafx.beans.binding.ObjectBinding
|
import javafx.beans.binding.ObjectBinding
|
||||||
import javafx.beans.property.BooleanProperty
|
import javafx.beans.property.BooleanProperty
|
||||||
|
import javafx.beans.value.ObservableValue
|
||||||
import javafx.geometry.Pos
|
import javafx.geometry.Pos
|
||||||
import javafx.scene.Parent
|
import javafx.scene.Parent
|
||||||
import javafx.scene.layout.HBox
|
import javafx.scene.layout.HBox
|
||||||
@ -45,6 +46,10 @@ abstract class DeviceViewConnection<D : Device> : DeviceConnection<D>(), DeviceL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getBooleanStateBinding(state: String): ObservableValue<Boolean>{
|
||||||
|
return getStateBinding(state).booleanBinding{it!!.booleanValue()}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind existing boolean property to writable device state
|
* Bind existing boolean property to writable device state
|
||||||
|
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package inr.numass.control
|
package inr.numass.control
|
||||||
|
|
||||||
import hep.dataforge.fx.fragments.FXFragment
|
|
||||||
import hep.dataforge.fx.fragments.FragmentWindow
|
|
||||||
import hep.dataforge.values.Value
|
import hep.dataforge.values.Value
|
||||||
import javafx.beans.value.ObservableValue
|
import javafx.beans.value.ObservableValue
|
||||||
import javafx.event.EventTarget
|
import javafx.event.EventTarget
|
||||||
import javafx.geometry.Orientation
|
import javafx.geometry.Orientation
|
||||||
import javafx.scene.control.ToggleButton
|
|
||||||
import javafx.scene.paint.Color
|
import javafx.scene.paint.Color
|
||||||
import javafx.scene.paint.Paint
|
import javafx.scene.paint.Paint
|
||||||
import javafx.scene.shape.Circle
|
import javafx.scene.shape.Circle
|
||||||
@ -99,3 +96,4 @@ fun EventTarget.deviceStateIndicator(connection: DeviceViewConnection<*>, state:
|
|||||||
separator(Orientation.VERTICAL)
|
separator(Orientation.VERTICAL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user