Moving msp to kotlin

This commit is contained in:
Alexander Nozik 2017-06-01 16:58:38 +03:00
parent debd4a707d
commit 1d82ccf123
10 changed files with 335 additions and 358 deletions

View File

@ -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

View File

@ -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";

View File

@ -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());
} }
} }

View File

@ -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");
}
}

View File

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

View File

@ -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"
}
}

View File

@ -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()
}
}
}

View File

@ -23,7 +23,9 @@ 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"/>

View File

@ -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

View File

@ -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)
} }
} }