vac to kotlin finished

This commit is contained in:
Alexander Nozik 2017-06-08 08:19:08 +03:00
parent 5e21fa5a0f
commit a7b5ec1101
28 changed files with 349 additions and 735 deletions

View File

@ -1,4 +1,3 @@
apply plugin: 'application'
if (!hasProperty('mainClass')) {
@ -14,18 +13,18 @@ dependencies {
compile project(':numass-control')
}
task debug(dependsOn: classes, type: JavaExec) {
task testDevice(dependsOn: classes, type: JavaExec) {
main mainClass
args = ["--config.resource=/config-debug/devices.xml"]
args = ["--config.resource=/config-test/devices.xml"]
classpath = sourceSets.main.runtimeClasspath
description "Start application in debug mode with default virtual port"
group "debug"
group "test"
}
task testRun(dependsOn: classes, type: JavaExec) {
main mainClass
args = ["--config=D:/temp/test/numass-devices.xml", "--device=thermo-1"]
classpath = sourceSets.main.runtimeClasspath
description "Start application using real device"
group "debug"
}
//task testRun(dependsOn: classes, type: JavaExec) {
// main mainClass
// args = ["--config=D:/temp/test/numass-devices.xml", "--device=thermo-1"]
// classpath = sourceSets.main.runtimeClasspath
// description "Start application using real device"
// group "debug"
//}

View File

@ -16,7 +16,6 @@
package inr.numass.control.cryotemp
import hep.dataforge.control.connections.Roles
import hep.dataforge.control.devices.DeviceFactory
import hep.dataforge.meta.Meta
import inr.numass.control.DeviceViewConnection
import inr.numass.control.NumassControlApplication
@ -32,7 +31,7 @@ class PKT8App : NumassControlApplication<PKT8Device>() {
}
}
override val deviceFactory: DeviceFactory = PKT8DeviceFactory()
override val deviceFactory = PKT8DeviceFactory()
override fun setupStage(stage: Stage, device: PKT8Device) {
stage.title = "Numass temperature view " + device.name

View File

@ -9,7 +9,7 @@ import inr.numass.control.DeviceViewFactory
/**
* Created by darksnake on 09-May-17.
*/
class PKT8DeviceFactory : DeviceViewFactory {
class PKT8DeviceFactory : DeviceViewFactory<PKT8Device> {
override fun getType(): String {
return PKT8Device.PKT8_DEVICE_TYPE
}
@ -18,7 +18,7 @@ class PKT8DeviceFactory : DeviceViewFactory {
return PKT8Device(context, meta)
}
override fun buildView(device: Device): DeviceViewConnection<*> {
override fun buildView(device: Device): DeviceViewConnection<PKT8Device> {
return PKT8ViewConnection()
}
}

View File

@ -21,7 +21,6 @@ import javafx.collections.FXCollections
import javafx.collections.MapChangeListener
import javafx.collections.ObservableList
import javafx.geometry.Orientation
import javafx.scene.Node
import javafx.scene.Parent
import javafx.scene.control.ToggleButton
import javafx.scene.layout.Priority
@ -34,7 +33,10 @@ import java.time.Instant
* Created by darksnake on 30-May-17.
*/
class PKT8ViewConnection : DeviceViewConnection<PKT8Device>(), MeasurementListener {
private val cryoView by lazy { CryoView() }
override fun buildView(): View {
return CryoView()
}
internal val table = FXCollections.observableHashMap<String, PKT8Result>()
val lastUpdateProperty = SimpleObjectProperty<String>("NEVER")
@ -46,13 +48,6 @@ class PKT8ViewConnection : DeviceViewConnection<PKT8Device>(), MeasurementListen
}
}
override fun getFXNode(): Node {
if (!isOpen) {
throw RuntimeException("Not connected!")
}
return cryoView.root;
}
override fun onMeasurementFailed(measurement: Measurement<*>, exception: Throwable) {
}
@ -66,7 +61,7 @@ class PKT8ViewConnection : DeviceViewConnection<PKT8Device>(), MeasurementListen
}
}
inner class CryoView() : View() {
inner class CryoView : View() {
private var plotButton: ToggleButton by singleAssign()
private var logButton: ToggleButton by singleAssign()

View File

@ -15,7 +15,6 @@
*/
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
@ -30,7 +29,7 @@ class MspApp : NumassControlApplication<MspDevice>() {
return MspViewConnection()
}
override val deviceFactory: DeviceFactory = MspDeviceFactory()
override val deviceFactory = MspDeviceFactory()
override fun setupStage(stage: Stage, device: MspDevice) {

View File

@ -9,7 +9,7 @@ import inr.numass.control.DeviceViewFactory
/**
* Created by darksnake on 09-May-17.
*/
class MspDeviceFactory : DeviceViewFactory {
class MspDeviceFactory : DeviceViewFactory<MspDevice> {
override fun getType(): String {
return MspDevice.MSP_DEVICE_TYPE
}
@ -19,7 +19,7 @@ class MspDeviceFactory : DeviceViewFactory {
return device
}
override fun buildView(device: Device): DeviceViewConnection<*> {
override fun buildView(device: Device): DeviceViewConnection<MspDevice> {
return MspViewConnection()
}
}

View File

@ -41,7 +41,6 @@ import javafx.geometry.Insets
import javafx.geometry.Orientation
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
@ -63,7 +62,7 @@ class MspViewConnection() : DeviceViewConnection<MspDevice>(), DeviceListener, N
}
override fun buildView(): View {
return MspView();
return MspView()
}
override fun pushValue(valueName: String, value: Value) {
@ -110,8 +109,6 @@ class MspViewConnection() : DeviceViewConnection<MspDevice>(), DeviceListener, N
}
}
private var logButton: ToggleButton by singleAssign()
private val logWindow = FragmentWindow(LogFragment().apply {
addLogHandler(device.logger)
})
@ -174,7 +171,7 @@ class MspViewConnection() : DeviceViewConnection<MspDevice>(), DeviceListener, N
}
separator(Orientation.VERTICAL)
logButton = togglebutton("Log") {
togglebutton("Log") {
isSelected = false
logWindow.bindTo(this)
}
@ -199,7 +196,6 @@ class MspViewConnection() : DeviceViewConnection<MspDevice>(), DeviceListener, N
pl.configureValue("title", title)
}
}
}
}

View File

@ -24,43 +24,56 @@ import java.util.*
/**
* Created by darksnake on 14-May-17.
*/
abstract class DeviceViewConnection<D : Device>() : Component(), Connection<D>, DeviceListener, FXObject {
abstract class DeviceViewConnection<D : Device> : Component(), Connection, DeviceListener, FXObject {
private val bindings = HashMap<String, ObjectBinding<Value>>()
var device: D by singleAssign()
private val deviceProperty = SimpleObjectProperty<D>(this, "device", null)
val device: D
get() {
val res = deviceProperty.get();
if (res == null) {
throw RuntimeException("Not connected!");
} else {
return res
}
}
val viewProperty = SimpleObjectProperty<View>(this, "view", null)
var view: View? by viewProperty
private val viewProperty = SimpleObjectProperty<View>(this, "view", null)
val view: View
get() {
if (viewProperty.get() == null) {
viewProperty.set(buildView(device))
}
return viewProperty.get();
}
override fun isOpen(): Boolean {
return this.view != null
return this.deviceProperty.get() != null
}
override fun open(device: D) {
if(!isOpen) {
this.device = device
this.view = buildView();
} else{
override fun open(obj: Any) {
if (!isOpen) {
@Suppress("UNCHECKED_CAST")
deviceProperty.set(obj as D)
} else {
log.warning("Connection already opened")
}
}
override fun close() {
view?.close()
this.view = null
if (viewProperty.isNotNull.get()) {
view.close()
}
deviceProperty.set(null)
}
override fun getFXNode(): Node {
if (view == null) {
throw RuntimeException("Connection not opened");
} else {
return view!!.root;
}
return view.root;
}
abstract fun buildView(): View;
abstract fun buildView(device: D): View;
/**
* Get binding for a given device state
@ -74,7 +87,7 @@ abstract class DeviceViewConnection<D : Device>() : Component(), Connection<D>,
object : ObjectBinding<Value>() {
override fun computeValue(): Value {
if (isOpen) {
return device!!.getState(stateName)
return device.getState(stateName)
} else {
return Value.NULL
}
@ -103,7 +116,7 @@ abstract class DeviceViewConnection<D : Device>() : Component(), Connection<D>,
property.addListener { observable, oldValue, newValue ->
if (isOpen && oldValue != newValue) {
runAsync {
device!!.setState(state, newValue).get().booleanValue();
device.setState(state, newValue).get().booleanValue();
} ui {
property.set(it)
}
@ -128,7 +141,7 @@ abstract class DeviceViewConnection<D : Device>() : Component(), Connection<D>,
}
togglebutton("View") {
isSelected = false
FragmentWindow(FXFragment.buildFromNode(device?.name) { fxNode }).bindTo(this)
FragmentWindow(FXFragment.buildFromNode(device.name) { fxNode }).bindTo(this)
}
}
}

View File

@ -3,10 +3,10 @@ package inr.numass.control
import hep.dataforge.control.devices.Device
import hep.dataforge.control.devices.DeviceFactory
interface DeviceViewFactory : DeviceFactory {
interface DeviceViewFactory<D : Device> : DeviceFactory<D> {
/**
* Create but do not connect view connection for the device
* @return
*/
fun buildView(device: Device): DeviceViewConnection<*>
fun buildView(device: Device): DeviceViewConnection<D>
}

View File

@ -1,16 +1,23 @@
package inr.numass.control
import hep.dataforge.kodex.KMetaBuilder
import hep.dataforge.plots.PlotFrame
import hep.dataforge.plots.Plottable
import hep.dataforge.plots.fx.PlotContainer
import hep.dataforge.plots.jfreechart.JFreeChartFrame
import hep.dataforge.values.Value
import javafx.beans.value.ObservableValue
import javafx.event.EventTarget
import javafx.geometry.Orientation
import javafx.scene.Node
import javafx.scene.layout.BorderPane
import javafx.scene.paint.Color
import javafx.scene.paint.Paint
import javafx.scene.shape.Circle
import javafx.scene.shape.StrokeType
import org.controlsfx.control.ToggleSwitch
import tornadofx.*
import java.util.*
/**
@ -127,4 +134,16 @@ fun Node.deviceStateToggle(connection: DeviceViewConnection<*>, state: String, t
fun EventTarget.switch(text: String = "", op: (ToggleSwitch.() -> Unit)? = null): ToggleSwitch {
val switch = ToggleSwitch(text)
return opcr(this, switch, op)
}
/**
* Add plot
*/
fun BorderPane.plot(plottables: Iterable<Plottable> = Collections.emptyList(), metaTransform: (KMetaBuilder.() -> Unit)? = null): PlotFrame {
val meta = KMetaBuilder("plotFrame");
metaTransform?.invoke(meta)
val plot = JFreeChartFrame(meta)
plot.addAll(plottables)
PlotContainer.centerIn(this).plot = plot
return plot;
}

View File

@ -3,9 +3,9 @@ package inr.numass.control
import ch.qos.logback.classic.Level
import hep.dataforge.control.connections.Roles
import hep.dataforge.control.devices.Device
import hep.dataforge.control.devices.DeviceFactory
import hep.dataforge.exceptions.ControlException
import hep.dataforge.meta.Meta
import hep.dataforge.utils.ContextMetaFactory
import javafx.scene.Scene
import javafx.stage.Stage
import org.slf4j.LoggerFactory
@ -31,8 +31,6 @@ abstract class NumassControlApplication<D : Device> : App() {
stage.scene = scene
stage.show()
setupStage(stage, device)
setDFStageIcon(stage)
}
@ -49,7 +47,7 @@ abstract class NumassControlApplication<D : Device> : App() {
* @return
*/
protected abstract val deviceFactory: DeviceFactory
protected abstract val deviceFactory: ContextMetaFactory<D>
protected abstract fun setupStage(stage: Stage, device: D)

View File

@ -3,10 +3,18 @@ apply plugin: 'application'
version = "0.5.0"
if (!hasProperty('mainClass')) {
ext.mainClass = 'inr.numass.readvac.ReadVac'
ext.mainClass = 'inr.numass.control.readvac.ReadVac'
}
mainClassName = mainClass
dependencies {
compile project(':numass-control')
}
task testDevice(dependsOn: classes, type: JavaExec) {
main mainClass
args = ["--config.resource=/config-test/devices.xml"]
classpath = sourceSets.main.runtimeClasspath
description "Start application in debug mode with default virtual port"
group "test"
}

View File

@ -13,16 +13,12 @@ import hep.dataforge.control.measurements.SimpleMeasurement;
import hep.dataforge.control.ports.ComPortHandler;
import hep.dataforge.control.ports.PortFactory;
import hep.dataforge.control.ports.PortHandler;
import hep.dataforge.description.ValueDef;
import hep.dataforge.exceptions.ControlException;
import hep.dataforge.meta.Meta;
/**
* @author Alexander Nozik
*/
@ValueDef(name = "port")
@ValueDef(name = "delay")
@ValueDef(name = "timeout")
public class CM32Device extends PortSensor<Double> {
public CM32Device() {
}
@ -68,7 +64,7 @@ public class CM32Device extends PortSensor<Double> {
this.progressUpdate("No signal");
updateState(CONNECTED_STATE, false);
return null;
} else if (answer.indexOf("PM1:mbar") < -1) {
} else if (!answer.contains("PM1:mbar")) {
this.progressUpdate("Wrong answer: " + answer);
updateState(CONNECTED_STATE, false);
return null;

View File

@ -7,7 +7,7 @@ package inr.numass.control.readvac;
import hep.dataforge.context.Context;
import hep.dataforge.control.RoleDef;
import hep.dataforge.control.collectors.PointCollector;
import hep.dataforge.control.collectors.RegularPointCollector;
import hep.dataforge.control.collectors.ValueCollector;
import hep.dataforge.control.connections.Roles;
import hep.dataforge.control.connections.StorageConnection;
@ -18,7 +18,6 @@ import hep.dataforge.control.measurements.AbstractMeasurement;
import hep.dataforge.control.measurements.Measurement;
import hep.dataforge.description.ValueDef;
import hep.dataforge.exceptions.ControlException;
import hep.dataforge.exceptions.MeasurementException;
import hep.dataforge.meta.Meta;
import hep.dataforge.storage.api.PointLoader;
import hep.dataforge.storage.commons.LoaderFactory;
@ -30,6 +29,7 @@ import hep.dataforge.values.Value;
import hep.dataforge.values.ValueType;
import inr.numass.control.StorageHelper;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
@ -53,7 +53,6 @@ import static hep.dataforge.control.devices.PortSensor.CONNECTED_STATE;
public class VacCollectorDevice extends Sensor<DataPoint> {
private Map<String, Sensor<Double>> sensorMap = new LinkedHashMap<>();
private int delay = 5000;
private StorageHelper helper = new StorageHelper(VacCollectorDevice.this, this::buildLoader);
public VacCollectorDevice() {
@ -72,8 +71,7 @@ public class VacCollectorDevice extends Sensor<DataPoint> {
}
}
@SuppressWarnings("unchecked")
public void setSensors(Sensor... sensors) {
public void setSensors(Sensor<Double>... sensors) {
setSensors(Arrays.asList(sensors));
}
@ -98,13 +96,13 @@ public class VacCollectorDevice extends Sensor<DataPoint> {
return "Numass vacuum";
}
public void setDelay(int delay) throws MeasurementException {
this.delay = delay;
if (isMeasuring()) {
getMeasurement().stop(false);
getMeasurement().start();
}
}
// public void setDelay(int delay) throws MeasurementException {
// this.delay = delay;
// if (isMeasuring()) {
// getMeasurement().stop(false);
// getMeasurement().start();
// }
// }
@Override
public void shutdown() throws ControlException {
@ -130,9 +128,13 @@ public class VacCollectorDevice extends Sensor<DataPoint> {
return sensorMap.values();
}
private Duration getAveragingDuration() {
return Duration.parse(meta().getString("averagingDuration", "PT30S"));
}
private class VacuumMeasurement extends AbstractMeasurement<DataPoint> {
private final ValueCollector collector = new PointCollector(this::result, sensorMap.keySet());
private final ValueCollector collector = new RegularPointCollector(getAveragingDuration(), this::result);
private ScheduledExecutorService executor;
private ScheduledFuture<?> currentTask;
@ -142,11 +144,10 @@ public class VacCollectorDevice extends Sensor<DataPoint> {
}
@Override
public void start() {
executor = Executors
.newSingleThreadScheduledExecutor((Runnable r) -> new Thread(r, "VacuumMeasurement thread"));
executor = Executors.newSingleThreadScheduledExecutor((Runnable r) -> new Thread(r, "VacuumMeasurement thread"));
int delay = meta().getInt("delay", 5) * 1000;
currentTask = executor.scheduleWithFixedDelay(() -> {
sensorMap.values().forEach((sensor) -> {
try {

View File

@ -1,52 +0,0 @@
package inr.numass.control.readvac;
import hep.dataforge.context.Context;
import hep.dataforge.control.devices.Device;
import hep.dataforge.control.devices.Sensor;
import hep.dataforge.meta.Meta;
import inr.numass.control.DeviceViewConnection;
import inr.numass.control.DeviceViewFactory;
import java.util.List;
import java.util.stream.Collectors;
/**
* A factory for vacuum measurements collector
* Created by darksnake on 16-May-17.
*/
public class VacDeviceFactory implements DeviceViewFactory {
@Override
public String getType() {
return "numass:vac";
}
public Sensor<Double> buildSensor(Context context, Meta sensorConfig) {
switch (sensorConfig.getString("sensorType", "")) {
case "mks":
return new MKSVacDevice(context, sensorConfig);
case "CM32":
return new CM32Device(context, sensorConfig);
case "meradat":
return new MeradatVacDevice(context, sensorConfig);
case "baratron":
return new MKSBaratronDevice(context, sensorConfig);
default:
throw new RuntimeException("Unknown vacuum sensor type");
}
}
@Override
public VacCollectorDevice build(Context context, Meta config) {
List<Sensor<Double>> sensors = config.getMetaList("sensor").stream()
.map(sensorConfig -> buildSensor(context, sensorConfig)).collect(Collectors.toList());
VacCollectorDevice collector = new VacCollectorDevice(context, config);
collector.setSensors(sensors);
return collector;
}
@Override
public DeviceViewConnection buildView(Device device) {
return VacCollectorView.build(device.getContext());
}
}

View File

@ -1,41 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package inr.numass.control.readvac;
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;
import java.util.Objects;
/**
* @author Alexander Nozik
*/
public class ReadVac extends NumassControlApplication<VacCollectorDevice> {
@Override
protected DeviceViewConnection<VacCollectorDevice> buildView(VacCollectorDevice device) {
return VacCollectorView.build(device.getContext());
}
@Override
protected DeviceFactory getDeviceFactory() {
return new VacDeviceFactory();
}
@Override
protected void setupStage(Stage stage, VacCollectorDevice device) {
stage.setTitle("Numass vacuum measurements");
}
@Override
protected boolean acceptDevice(Meta meta) {
return Objects.equals(meta.getString("type", ""), "numass:vac");
}
}

View File

@ -0,0 +1,34 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package inr.numass.control.readvac
import hep.dataforge.meta.Meta
import hep.dataforge.utils.ContextMetaFactory
import inr.numass.control.DeviceViewConnection
import inr.numass.control.NumassControlApplication
import javafx.stage.Stage
/**
* @author Alexander Nozik
*/
class ReadVac : NumassControlApplication<VacCollectorDevice>() {
override fun buildView(device: VacCollectorDevice): DeviceViewConnection<VacCollectorDevice> {
return VacCollectorViewConnection()
}
override val deviceFactory: ContextMetaFactory<VacCollectorDevice> = VacDeviceFactory()
override fun setupStage(stage: Stage, device: VacCollectorDevice) {
stage.title = "Numass vacuum measurements"
}
override fun acceptDevice(meta: Meta): Boolean {
return meta.getString("type", "") == "numass:vac"
}
}

View File

@ -1,90 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package inr.numass.control.readvac;
import hep.dataforge.control.connections.Roles;
import hep.dataforge.control.devices.Sensor;
import hep.dataforge.control.virtual.Virtual;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.time.Duration;
/**
*
* @author Alexander Nozik
*/
public class TestVac extends Application {
VacCollectorView controller;
@Override
public void start(Stage stage) {
try {
Sensor<Double> sensor1 = Virtual.randomDoubleSensor("vac1", Duration.ofMillis(200), 1e-5, 2e-6);
Sensor<Double> sensor2 = Virtual.randomDoubleSensor("vac2", Duration.ofMillis(200), 2e-5, 2e-6);
Sensor<Double> sensor3 = Virtual.randomDoubleSensor("vac3", Duration.ofMillis(200), 1e-7, 1e-8);
// Sensor<Double> poweredSensor = new VirtualSensorFactory<Double>(
// "vac4",
// (sensor) -> {
// if (sensor.getState("power").booleanValue()) {
// return 1e-6;
// } else {
// return null;
// }
// })
// .addState("power")
// .setMeta(new MetaBuilder("device")
// .setValue("color", "magenta")
// .setValue("thickness", 3)
// .setValue("powerButton", true))
// .addCommand("setPower", new BiConsumer<Sensor<Double>, Value>() {
// @Override
// public void accept(Sensor<Double> sensor, Value power) {
//
// }
// })
// .build();
VacCollectorDevice collector = new VacCollectorDevice();
collector.setSensors(sensor1, sensor2, sensor3);
collector.init();
// collector.getConfig().putNode(new MetaBuilder("storage").putValue("path", "D:\\temp\\test"));
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/VacCollector.fxml"));
loader.load();
controller = loader.getController();
collector.connect(controller, Roles.VIEW_ROLE);
Scene scene = new Scene(loader.getRoot(), 800, 600);
stage.setTitle("Vacuum measurement test");
stage.setScene(scene);
stage.show();
} catch (Exception ex) {
throw new Error(ex);
}
}
@Override
public void stop() throws Exception {
if (controller != null) {
controller.getDevice().shutdown();
}
super.stop();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}

View File

@ -1,277 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package inr.numass.control.readvac;
import hep.dataforge.context.Context;
import hep.dataforge.control.connections.Roles;
import hep.dataforge.control.devices.Device;
import hep.dataforge.control.devices.Sensor;
import hep.dataforge.control.measurements.Measurement;
import hep.dataforge.control.measurements.MeasurementListener;
import hep.dataforge.exceptions.ControlException;
import hep.dataforge.exceptions.MeasurementException;
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.FXPlotFrame;
import hep.dataforge.plots.fx.PlotContainer;
import hep.dataforge.plots.jfreechart.JFreeChartFrame;
import hep.dataforge.tables.DataPoint;
import hep.dataforge.values.Value;
import inr.numass.control.DeviceViewConnection;
import javafx.application.Platform;
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.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.util.Duration;
import org.controlsfx.control.Notifications;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URL;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
/**
* A view controller for Vac collector
*
* @author <a href="mailto:altavir@gmail.com">Alexander Nozik</a>
*/
public class VacCollectorView extends DeviceViewConnection<VacCollectorDevice> implements Initializable, MeasurementListener {
public static VacCollectorView build(Context context) {
try {
FXMLLoader loader = new FXMLLoader(context.getClassLoader().getResource("fxml/VacCollector.fxml"));
loader.setClassLoader(context.getClassLoader());
loader.load();
return loader.getController();
} catch (IOException e) {
throw new Error(e);
}
}
private final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
private final String[] intervalNames = {"1 sec", "5 sec", "10 sec", "30 sec", "1 min"};
private final int[] intervals = {1000, 5000, 10000, 30000, 60000};
private final List<VacuumeterViewConnection> views = new ArrayList<>();
private TimePlottableGroup plottables;
@FXML
private BorderPane root;
@FXML
private AnchorPane plotHolder;
@FXML
private VBox vacBoxHolder;
@FXML
private Label timeLabel;
@FXML
private ChoiceBox<String> intervalSelector;
@FXML
private ToggleButton startStopButton;
@FXML
private ToggleButton storeButton;
@FXML
private ToggleButton logButton;
@Override
public Node getFXNode() {
return root;
}
/**
* Initializes the controller class.
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
intervalSelector.setItems(FXCollections.observableArrayList(intervalNames));
intervalSelector.getSelectionModel().select(1);
intervalSelector.getSelectionModel().selectedIndexProperty().addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
if (getDevice() != null) {
try {
getDevice().setDelay(intervals[newValue.intValue()]);
} catch (MeasurementException ex) {
evaluateDeviceException(getDevice(), "Failed to restart measurement", null);
}
}
});
LogFragment logFragment = new LogFragment();
new FragmentWindow(logFragment).bindTo(logButton);
logFragment.addRootLogHandler();
}
@Override
public void evaluateDeviceException(Device device, String message, Throwable exception) {
Notifications.create().darkStyle().hideAfter(Duration.seconds(2d)).text(message).showError();
}
@Override
public void open(VacCollectorDevice device) {
super.open(device);
device.getSensors().stream().map((sensor) -> {
VacuumeterViewConnection view;
if (sensor.hasState("power")) {
view = new PoweredVacuumeterViewConnection();
} else {
view = new VacuumeterViewConnection();
}
sensor.connect(view, Roles.VIEW_ROLE, Roles.DEVICE_LISTENER_ROLE);
return view;
}).forEach(views::add);
setupView();
}
@Override
public void notifyDeviceStateChanged(Device device, String name, Value state) {
}
@Override
public void onMeasurementFailed(Measurement measurement, Throwable exception) {
LoggerFactory.getLogger(getClass()).debug("Exception during measurement: {}", exception.getMessage());
}
@Override
public void onMeasurementResult(Measurement measurement, Object res, Instant time) {
if (plottables != null) {
plottables.put(DataPoint.class.cast(res));
}
Platform.runLater(() -> timeLabel.setText(TIME_FORMAT.format(LocalDateTime.ofInstant(time, ZoneOffset.UTC))));
}
private void setupView() {
vacBoxHolder.getChildren().clear();
plottables = new TimePlottableGroup();
views.forEach((view) -> {
vacBoxHolder.getChildren().add(view.getComponent());
TimePlottable plot = new TimePlottable(view.getTitle(),
view.getDevice().getName());
plot.configure(view.getDevice().meta());
plottables.add(plot);
});
plottables.setValue("thickness", 3);
plottables.setMaxAge(java.time.Duration.ofHours(3));
PlotContainer.anchorTo(plotHolder).setPlot(setupPlot(plottables));
}
private FXPlotFrame setupPlot(TimePlottableGroup plottables) {
Meta plotConfig = new MetaBuilder("plotFrame")
.setNode(new MetaBuilder("yAxis")
.setValue("type", "log")
.setValue("axisTitle", "pressure")
.setValue("axisUnits", "mbar")
)
.setValue("xAxis.type", "time");
JFreeChartFrame frame = new JFreeChartFrame(plotConfig);
frame.addAll(plottables);
return frame;
}
private void startMeasurement() throws ControlException {
getDevice().startMeasurement();
startStopButton.setSelected(true);
}
private void stopMeasurement() {
try {
getDevice().stopMeasurement(false);
for (Sensor sensor : getDevice().getSensors()) {
sensor.stopMeasurement(false);
}
} catch (ControlException ex) {
throw new RuntimeException(ex);
}
}
@FXML
private void onStartStopToggle(ActionEvent event) {
if (startStopButton.isSelected() != getDevice().isMeasuring()) {
//Starting measurement on non-UI thread
new Thread(() -> {
if (startStopButton.isSelected()) {
try {
startMeasurement();
} catch (ControlException ex) {
getDevice().getLogger().error("Failed to start measurement", ex);
startStopButton.setSelected(false);
}
} else {
stopMeasurement();
}
}).start();
}
}
@FXML
private void onStoreToggle(ActionEvent event) {
getDevice().setState("storing", storeButton.isSelected());
// if (storeButton.isSelected()) {
// //creating storage on UI thread
// if (!device.meta().hasMeta("storage")) {
// getLogger().info("Storage not defined. Starting storage selection dialog");
// DirectoryChooser chooser = new DirectoryChooser();
// File storageDir = chooser.showDialog(plotHolder.getScene().getWindow());
// if (storageDir == null) {
// storeButton.setSelected(false);
// throw new RuntimeException("User canceled directory selection");
// }
// device.getConfig().putNode(new MetaBuilder("storage")
// .putValue("path", storageDir.getAbsolutePath()));
// }
// Meta storageConfig = device.meta().getMeta("storage");
// Storage localStorage = StorageManager.buildFrom(device.getContext())
// .buildStorage(storageConfig);
// //Start storage creation on non-UI thread
// new Thread(() -> {
// try {
//
// PointLoader loader;
//
// if (loaderFactory != null) {
// loader = loaderFactory.apply(device, localStorage);
// } else {
// TableFormatBuilder format = new TableFormatBuilder().setType("timestamp", ValueType.TIME);
// device.getSensors().forEach((s) -> {
// format.setType(s.getName(), ValueType.NUMBER);
// });
//
// loader = LoaderFactory.buildPointLoder(localStorage, "vactms",
// device.meta().getString("storage.shelf", ""), "timestamp", format.build());
// }
// storageConnection = new LoaderConnection(loader);
// device.connect(storageConnection, Roles.STORAGE_ROLE);
// } catch (Exception ex) {
// getLogger().error("Failed to start data storing", ex);
// storeButton.setSelected(false);
// }
// }).start();
// } else if (storageConnection != null) {
// device.disconnect(storageConnection);
// }
}
}

View File

@ -0,0 +1,139 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package inr.numass.control.readvac
import hep.dataforge.control.Connection
import hep.dataforge.control.connections.Roles
import hep.dataforge.control.devices.Sensor
import hep.dataforge.control.measurements.Measurement
import hep.dataforge.control.measurements.MeasurementListener
import hep.dataforge.fx.fragments.FragmentWindow
import hep.dataforge.fx.fragments.LogFragment
import hep.dataforge.plots.data.TimePlottable
import hep.dataforge.plots.data.TimePlottableGroup
import hep.dataforge.values.Value
import inr.numass.control.DeviceViewConnection
import inr.numass.control.deviceStateToggle
import inr.numass.control.plot
import javafx.collections.FXCollections
import javafx.collections.MapChangeListener
import javafx.geometry.Orientation
import javafx.scene.control.ScrollPane
import javafx.scene.layout.Priority
import tornadofx.*
import java.time.Instant
/**
* A view controller for Vac collector
* @author [Alexander Nozik](mailto:altavir@gmail.com)
*/
class VacCollectorViewConnection : DeviceViewConnection<VacCollectorDevice>() {
private val table = FXCollections.observableHashMap<String, Double>()
private val sensorConnection = object : MeasurementListener, Connection{
override fun onMeasurementResult(measurement: Measurement<*>, result: Any, time: Instant?) {
if(result is Double){
table.put(measurement.device.name, result);
}
}
override fun onMeasurementFailed(measurement: Measurement<*>?, exception: Throwable?) {
}
}
private val viewList = FXCollections.observableArrayList<VacViewConnection>();
override fun buildView(device: VacCollectorDevice): View {
return VacCollectorView();
}
override fun open(obj: Any) {
super.open(obj)
device.sensors.forEach { sensor ->
val view = VacViewConnection()
sensor.connect(view, Roles.VIEW_ROLE, Roles.DEVICE_LISTENER_ROLE)
sensor.connect(sensorConnection, Roles.MEASUREMENT_LISTENER_ROLE);
viewList.add(view)
}
}
inner class VacCollectorView : View("Numass vacuum view") {
private val plottables = TimePlottableGroup().apply {
viewList.forEach {
val plot = TimePlottable(it.getTitle(), it.device.name)
plot.configure(it.device.meta())
add(plot)
}
setValue("thickness", 3)
}
private val logWindow = FragmentWindow(LogFragment().apply {
addLogHandler(device.logger)
})
override val root = borderpane {
top {
toolbar {
deviceStateToggle(this@VacCollectorViewConnection, Sensor.MEASURING_STATE, "Measure")
deviceStateToggle(this@VacCollectorViewConnection, "storing", "Store")
pane {
hgrow = Priority.ALWAYS
}
separator(Orientation.VERTICAL)
togglebutton("Log") {
isSelected = false
logWindow.bindTo(this)
}
}
}
plot(plottables) {
"xAxis.type" to "time"
node("yAxis") {
"type" to "log"
"axisTitle" to "presure"
"axisUnits" to "mbar"
}
}
right {
scrollpane {
hbarPolicy = ScrollPane.ScrollBarPolicy.NEVER
vbox {
viewList.forEach {
add(it.fxNode)
separator(Orientation.HORIZONTAL)
}
}
}
// listview(viewList) {
// cellFormat {
// graphic = it.fxNode
// }
// }
}
}
init {
table.addListener { change: MapChangeListener.Change<out String, out Double> ->
if (change.wasAdded()) {
val pl = plottables.get(change.key)
val value = change.valueAdded
if (pl != null) {
if (value > 0) {
pl.put(Value.of(value))
} else {
pl.put(Value.NULL)
}
}
}
}
}
}
}

View File

@ -0,0 +1,46 @@
package inr.numass.control.readvac
import hep.dataforge.context.Context
import hep.dataforge.control.devices.Device
import hep.dataforge.control.devices.Sensor
import hep.dataforge.control.virtual.VirtualDevice
import hep.dataforge.meta.Meta
import inr.numass.control.DeviceViewConnection
import inr.numass.control.DeviceViewFactory
import java.util.stream.Collectors
/**
* A factory for vacuum measurements collector
* Created by darksnake on 16-May-17.
*/
class VacDeviceFactory : DeviceViewFactory<VacCollectorDevice> {
override fun getType(): String {
return "numass:vac"
}
fun buildSensor(context: Context, sensorConfig: Meta): Sensor<Double> {
when (sensorConfig.getString("sensorType", "")) {
"mks" -> return MKSVacDevice(context, sensorConfig)
"CM32" -> return CM32Device(context, sensorConfig)
"meradat" -> return MeradatVacDevice(context, sensorConfig)
"baratron" -> return MKSBaratronDevice(context, sensorConfig)
VirtualDevice.VIRTUAL_SENSOR_TYPE -> return VirtualDevice.randomDoubleSensor(context, sensorConfig)
else -> throw RuntimeException("Unknown vacuum sensor type")
}
}
override fun build(context: Context, config: Meta): VacCollectorDevice {
val sensors = config.getMetaList("sensor").stream()
.map { sensorConfig ->
buildSensor(context, sensorConfig)
}.collect(Collectors.toList<Sensor<Double>>())
val collector = VacCollectorDevice(context, config)
collector.setSensors(sensors)
return collector
}
override fun buildView(device: Device): DeviceViewConnection<VacCollectorDevice> {
return VacCollectorViewConnection();
}
}

View File

@ -13,6 +13,7 @@ import hep.dataforge.control.measurements.MeasurementListener
import inr.numass.control.DeviceViewConnection
import inr.numass.control.switch
import javafx.application.Platform
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import javafx.geometry.Insets
import javafx.geometry.Orientation
@ -30,7 +31,7 @@ import java.time.format.DateTimeFormatter
/**
* @author [Alexander Nozik](mailto:altavir@gmail.com)
*/
open class VacuumeterViewConnection : DeviceViewConnection<Sensor<Double>>(), MeasurementListener {
open class VacViewConnection : DeviceViewConnection<Sensor<Double>>(), MeasurementListener {
val statusProperty = SimpleStringProperty("")
var status: String by statusProperty
@ -38,8 +39,11 @@ open class VacuumeterViewConnection : DeviceViewConnection<Sensor<Double>>(), Me
val valueProperty = SimpleStringProperty("---")
var value: String by valueProperty
val timeProperty = SimpleObjectProperty<Instant>()
var time: Instant by timeProperty
override fun buildView(): View {
override fun buildView(device: Sensor<Double>): View {
return VacView();
}
@ -64,17 +68,23 @@ open class VacuumeterViewConnection : DeviceViewConnection<Sensor<Double>>(), Me
}
override fun onMeasurementResult(measurement: Measurement<*>, res: Any, time: Instant) {
val result = Double::class.java.cast(res)
val result = Number::class.java.cast(res).toDouble()
val resString = FORMAT.format(result)
Platform.runLater {
value = resString
this.time = time
status = "OK: " + TIME_FORMAT.format(LocalDateTime.ofInstant(time, ZoneOffset.UTC));
}
}
inner class VacView : View("Numass vacuumeter ${device.meta().getString("title", device.name)}") {
fun getTitle(): String{
return device.meta().getString("title", device.name);
}
inner class VacView : View("Numass vacuumeter ${getTitle()}") {
override val root = borderpane {
minWidth = 90.0
style {
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<config>
<storage path="D:/temp/test"/>
<device type="numass:vac">
<sensor name="test1" color="red" sensorType="@test" mean="1e-6" sigma="5e-7"/>
<sensor name="test2" color="green" sensorType="@test" mean="2e-6" sigma="5e-7"/>
<sensor name="test3" color="blue" sensorType="@test" mean="3e-6" sigma="1e-6"/>
</device>
</config>

View File

@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import org.controlsfx.control.ToggleSwitch?>
<?import java.net.URL?>
<AnchorPane styleClass="vacBox" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
<stylesheets>
<URL value="@vacstyles.css"/>
</stylesheets>
<VBox layoutX="50.0" layoutY="6.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<AnchorPane styleClass="namePane">
<Label id="name" fx:id="deviceNameLabel" alignment="CENTER" prefHeight="40.0" text="device Name"
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="50.0"
AnchorPane.topAnchor="0.0"/>
<ToggleSwitch fx:id="disableButton" alignment="CENTER" layoutX="130.0" layoutY="10.0"
prefWidth="40.0" AnchorPane.bottomAnchor="10.0" AnchorPane.rightAnchor="10.0"
AnchorPane.topAnchor="10.0"/>
</AnchorPane>
<Separator/>
<BorderPane>
<left>
<Label id="pressure" fx:id="valueLabel" alignment="CENTER_RIGHT" minWidth="110.0"
prefHeight="60.0" text="---" BorderPane.alignment="CENTER">
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
</padding>
</Label>
</left>
<right>
<Label id="units" fx:id="unitLabel" prefHeight="60.0" prefWidth="75.0" text="mbar"
BorderPane.alignment="CENTER">
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
</padding>
</Label>
</right>
</BorderPane>
<Separator/>
<Pane minHeight="30.0" prefHeight="30.0" VBox.vgrow="ALWAYS">
<ToggleSwitch fx:id="powerSwitch" layoutX="58.0" layoutY="5.0" text="Power"/>
</Pane>
<Separator/>
<AnchorPane styleClass="statusPane">
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
</padding>
<Label fx:id="status" maxWidth="200.0" text="Initializing" wrapText="true"
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0"/>
</AnchorPane>
</VBox>
</AnchorPane>

View File

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Font?>
<?import java.net.URL?>
<BorderPane fx:id="root" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="inr.numass.control.readvac.VacCollectorView">
<stylesheets>
<URL value="@/fxml/vacstyles.css" />
</stylesheets>
<right>
<ScrollPane fitToHeight="true" fitToWidth="true" hbarPolicy="NEVER" minWidth="250.0" BorderPane.alignment="CENTER">
<content>
<VBox fx:id="vacBoxHolder" minWidth="200.0" spacing="2.0" />
</content>
</ScrollPane>
</right>
<center>
<VBox alignment="TOP_CENTER" BorderPane.alignment="CENTER">
<ToolBar>
<ToggleButton fx:id="startStopButton" mnemonicParsing="false" onAction="#onStartStopToggle" text="Measure" />
<ToggleButton fx:id="storeButton" mnemonicParsing="false" onAction="#onStoreToggle" text="Store" />
<Separator orientation="VERTICAL" />
<Label text="Interval: " />
<ChoiceBox fx:id="intervalSelector" prefWidth="150.0" />
<Separator orientation="VERTICAL" />
<Pane HBox.hgrow="ALWAYS" />
<Separator orientation="VERTICAL" />
<ToggleButton fx:id="logButton" mnemonicParsing="false" text="Console" />
</ToolBar>
<AnchorPane fx:id="plotHolder" VBox.vgrow="ALWAYS" />
<HBox styleClass="beveled">
<Label alignment="CENTER_RIGHT" text="Last update: ">
<font>
<Font size="24.0" />
</font>
</Label>
<Label fx:id="timeLabel" text="08.02.2016 15:57" HBox.hgrow="ALWAYS">
<font>
<Font size="24.0" />
</font>
</Label>
</HBox>
</VBox>
</center>
</BorderPane>

View File

@ -1,56 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import org.controlsfx.control.ToggleSwitch?>
<?import java.net.URL?>
<BorderPane fx:id="root" styleClass="vacBox" xmlns="http://javafx.com/javafx/8.0.40"
xmlns:fx="http://javafx.com/fxml/1">
<stylesheets>
<URL value="@vacstyles.css"/>
</stylesheets>
<center>
<VBox>
<AnchorPane styleClass="namePane">
<Label id="name" fx:id="deviceNameLabel" alignment="CENTER" prefHeight="40.0" text="device Name"
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="50.0"
AnchorPane.topAnchor="0.0"/>
<ToggleSwitch fx:id="disableButton" alignment="CENTER" layoutX="140.0" layoutY="20.0"
prefWidth="40.0" selected="true" AnchorPane.bottomAnchor="10.0"
AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0"/>
</AnchorPane>
<Separator/>
<BorderPane VBox.vgrow="ALWAYS">
<left>
<Label id="pressure" fx:id="valueLabel" alignment="CENTER_RIGHT" minWidth="110.0" prefHeight="60.0"
text="---" BorderPane.alignment="CENTER">
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
</padding>
</Label>
</left>
<right>
<Label id="units" fx:id="unitLabel" prefHeight="60.0" prefWidth="75.0" text="mbar"
BorderPane.alignment="CENTER">
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
</padding>
</Label>
</right>
</BorderPane>
<Separator/>
<AnchorPane styleClass="statusPane">
<VBox.margin>
<Insets/>
</VBox.margin>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
</padding>
<Label fx:id="status" maxWidth="200.0" text="Initializing" wrapText="true"
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0"/>
</AnchorPane>
</VBox>
</center>
</BorderPane>

View File

@ -1,30 +0,0 @@
/*
* Empty Stylesheet file.
*/
.vacBox {
-fx-border-color: blue;
}
.vacBox #pressure{
-fx-font-size: 24;
-fx-font-weight: bold
}
.vacBox #units{
-fx-font-size: 24;
}
.vacBox #name{
-fx-font-size: 18;
-fx-font-weight: bold
}
.vacBox .namePane{
-fx-background-color: lavender
}
.beveled{
-fx-border-color: white #676767 #676767 white;
-fx-border-style: solid outside line-join bevel;
}