Update Value and State annotations
This commit is contained in:
commit
900e249d5c
@ -1,11 +1,35 @@
|
|||||||
|
buildscript {
|
||||||
|
ext.kotlin_version = '1.1.2-2'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects{
|
||||||
|
apply plugin: "kotlin"
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
javaParameters = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':numass-client')
|
compile project(':numass-client')
|
||||||
compile "hep.dataforge:plots-jfc" // project(':dataforge-plots:plots-jfc')
|
compile "hep.dataforge:plots-jfc" // project(':dataforge-plots:plots-jfc')
|
||||||
compile "hep.dataforge:dataforge-control" //project(':dataforge-control')
|
compile "hep.dataforge:dataforge-control" //project(':dataforge-control')
|
||||||
|
compile "hep.dataforge:kodex"
|
||||||
|
|
||||||
// https://mvnrepository.com/artifact/org.controlsfx/controlsfx
|
//graphics
|
||||||
compile group: 'org.controlsfx', name: 'controlsfx', version: '8.40.12'
|
compile 'org.controlsfx:controlsfx:8.40.12'
|
||||||
|
compile "no.tornado:tornadofx:1.7.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
task installAll(type: Copy) {
|
task installAll(type: Copy) {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
plugins{
|
plugins{
|
||||||
id "org.jetbrains.kotlin.jvm" version '1.1.2-2'
|
|
||||||
id "application"
|
id "application"
|
||||||
id 'com.github.johnrengelman.shadow' version '2.0.0'
|
id 'com.github.johnrengelman.shadow' version '2.0.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!hasProperty('mainClass')) {
|
if (!hasProperty('mainClass')) {
|
||||||
ext.mainClass = 'inr.numass.control.ServerApp'//"inr.numass.viewer.test.TestApp"
|
ext.mainClass = 'inr.numass.control.ServerApp'//"inr.numass.viewer.test.TestApp"
|
||||||
}
|
}
|
||||||
@ -21,11 +21,6 @@ configurations{
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
//graphics
|
|
||||||
compile 'org.controlsfx:controlsfx:8.40.12'
|
|
||||||
compile "no.tornado:tornadofx:1.7.4"
|
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8"
|
|
||||||
|
|
||||||
//DataForge dependencies
|
//DataForge dependencies
|
||||||
compile project(':numass-control')
|
compile project(':numass-control')
|
||||||
compile project(':numass-server')
|
compile project(':numass-server')
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
package inr.numass.control
|
package inr.numass.control
|
||||||
|
|
||||||
import hep.dataforge.control.devices.Device
|
|
||||||
import hep.dataforge.control.devices.PortSensor
|
|
||||||
import hep.dataforge.control.devices.Sensor
|
|
||||||
import hep.dataforge.fx.fragments.FXFragment
|
|
||||||
import hep.dataforge.fx.fragments.FragmentWindow
|
|
||||||
import hep.dataforge.storage.filestorage.FileStorage
|
import hep.dataforge.storage.filestorage.FileStorage
|
||||||
import inr.numass.control.NumassControlUtils.getDFIcon
|
|
||||||
import javafx.geometry.Orientation
|
import javafx.geometry.Orientation
|
||||||
import javafx.geometry.Pos
|
import javafx.geometry.Pos
|
||||||
import javafx.scene.control.Hyperlink
|
import javafx.scene.control.Hyperlink
|
||||||
@ -17,7 +11,7 @@ import tornadofx.*
|
|||||||
/**
|
/**
|
||||||
* Created by darksnake on 11-May-17.
|
* Created by darksnake on 11-May-17.
|
||||||
*/
|
*/
|
||||||
class BoardView : View("Numass control board", ImageView(getDFIcon())) {
|
class BoardView : View("Numass control board", ImageView(dfIcon)) {
|
||||||
private val controller: BoardController by inject();
|
private val controller: BoardController by inject();
|
||||||
|
|
||||||
override val root = borderpane {
|
override val root = borderpane {
|
||||||
@ -87,23 +81,11 @@ class BoardView : View("Numass control board", ImageView(getDFIcon())) {
|
|||||||
vbox {
|
vbox {
|
||||||
prefHeight = 40.0
|
prefHeight = 40.0
|
||||||
bindChildren(controller.devices) { connection ->
|
bindChildren(controller.devices) { connection ->
|
||||||
titledpane(title = "Device: " + connection.device.name, collapsible = true) {
|
titledpane(
|
||||||
hbox {
|
title = "Device: " + connection.device.name,
|
||||||
alignment = Pos.CENTER_LEFT
|
collapsible = true,
|
||||||
vgrow = Priority.ALWAYS;
|
node = connection.getBoardView()
|
||||||
deviceStateIndicator(connection, Device.INITIALIZED_STATE)
|
)
|
||||||
deviceStateIndicator(connection, PortSensor.CONNECTED_STATE)
|
|
||||||
deviceStateIndicator(connection, Sensor.MEASURING_STATE)
|
|
||||||
deviceStateIndicator(connection, "storing")
|
|
||||||
pane {
|
|
||||||
hgrow = Priority.ALWAYS
|
|
||||||
}
|
|
||||||
togglebutton("View") {
|
|
||||||
isSelected = false
|
|
||||||
FragmentWindow(FXFragment.buildFromNode(connection.device.name) { connection.fxNode }).bindTo(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ dependencies {
|
|||||||
|
|
||||||
task debug(dependsOn: classes, type: JavaExec) {
|
task debug(dependsOn: classes, type: JavaExec) {
|
||||||
main mainClass
|
main mainClass
|
||||||
args "--config.resource=/config-debug/devices.xml"
|
args = ["--config.resource=/config-debug/devices.xml"]
|
||||||
classpath = sourceSets.main.runtimeClasspath
|
classpath = sourceSets.main.runtimeClasspath
|
||||||
description "Start application in debug mode with default virtual port"
|
description "Start application in debug mode with default virtual port"
|
||||||
group "debug"
|
group "debug"
|
||||||
@ -24,7 +24,7 @@ task debug(dependsOn: classes, type: JavaExec) {
|
|||||||
|
|
||||||
task testRun(dependsOn: classes, type: JavaExec) {
|
task testRun(dependsOn: classes, type: JavaExec) {
|
||||||
main mainClass
|
main mainClass
|
||||||
args(["--config=D:/temp/test/numass-devices.xml", "--device=thermo-1"])
|
args = ["--config=D:/temp/test/numass-devices.xml", "--device=thermo-1"]
|
||||||
classpath = sourceSets.main.runtimeClasspath
|
classpath = sourceSets.main.runtimeClasspath
|
||||||
description "Start application using real device"
|
description "Start application using real device"
|
||||||
group "debug"
|
group "debug"
|
||||||
|
@ -1,53 +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.cryotemp;
|
|
||||||
|
|
||||||
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 darksnake
|
|
||||||
*/
|
|
||||||
public class PKT8App extends NumassControlApplication<PKT8Device> {
|
|
||||||
@Override
|
|
||||||
protected DeviceViewConnection<PKT8Device> buildView(PKT8Device device) {
|
|
||||||
return PKT8View.build(device.getContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected DeviceFactory getDeviceFactory() {
|
|
||||||
return new PKT8DeviceFactory();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setupStage(Stage stage, PKT8Device device) {
|
|
||||||
stage.setTitle("Numass temperature view " + device.getName());
|
|
||||||
stage.setMinHeight(400);
|
|
||||||
stage.setMinWidth(400);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean acceptDevice(Meta meta) {
|
|
||||||
return Objects.equals(meta.getString("type"), "PKT8");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -22,6 +22,6 @@ public class PKT8DeviceFactory implements DeviceViewFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DeviceViewConnection buildView(Device device) {
|
public DeviceViewConnection buildView(Device device) {
|
||||||
return PKT8View.build(device.getContext());
|
return new PKT8ViewConnection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
package inr.numass.control.cryotemp;
|
|
||||||
|
|
||||||
import hep.dataforge.control.connections.Roles;
|
|
||||||
import hep.dataforge.fx.fragments.FXFragment;
|
|
||||||
import javafx.fxml.FXMLLoader;
|
|
||||||
import javafx.scene.Parent;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by darksnake on 07-Oct-16.
|
|
||||||
*/
|
|
||||||
public class PKT8PlotFragment extends FXFragment {
|
|
||||||
private PKT8PlotView plotController;
|
|
||||||
|
|
||||||
public PKT8PlotFragment(PKT8Device device) {
|
|
||||||
super("PKT8 cryogenic temperature viewer", 600, 400);
|
|
||||||
|
|
||||||
try {
|
|
||||||
FXMLLoader loader = new FXMLLoader(device.getContext().getClassLoader().getResource("fxml/PKT8Plot.fxml"));
|
|
||||||
loader.setClassLoader(device.getContext().getClassLoader());
|
|
||||||
loader.load();
|
|
||||||
plotController = loader.getController();
|
|
||||||
device.connect(plotController, Roles.VIEW_ROLE);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Parent buildRoot() {
|
|
||||||
return plotController.getPane();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,144 +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.cryotemp;
|
|
||||||
|
|
||||||
import hep.dataforge.control.measurements.Measurement;
|
|
||||||
import hep.dataforge.control.measurements.MeasurementListener;
|
|
||||||
import hep.dataforge.meta.Meta;
|
|
||||||
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 inr.numass.control.DeviceViewConnection;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.fxml.Initializable;
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.ToggleButton;
|
|
||||||
import javafx.scene.layout.AnchorPane;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FXML Controller class
|
|
||||||
*
|
|
||||||
* @author darksnake
|
|
||||||
*/
|
|
||||||
public class PKT8PlotView extends DeviceViewConnection<PKT8Device> implements Initializable, MeasurementListener {
|
|
||||||
|
|
||||||
private FXPlotFrame plotFrame;
|
|
||||||
private TimePlottableGroup plottables;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private BorderPane root;
|
|
||||||
@FXML
|
|
||||||
private ToggleButton rawDataButton;
|
|
||||||
@FXML
|
|
||||||
private AnchorPane plotArea;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the controller class.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void initialize(URL url, ResourceBundle rb) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open(PKT8Device device) throws Exception {
|
|
||||||
super.open(device);
|
|
||||||
rawDataButton.selectedProperty().addListener(observable -> {
|
|
||||||
if (plotFrame != null) {
|
|
||||||
setupPlotFrame(plotFrame.getConfig());
|
|
||||||
if (device != null) {
|
|
||||||
setupChannels(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setupPlotFrame(device.meta().getMetaOrEmpty("plot.frame"));
|
|
||||||
setupChannels(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws Exception {
|
|
||||||
super.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set o reset plot area
|
|
||||||
*/
|
|
||||||
private synchronized void setupPlotFrame(Meta plotFrameMeta) {
|
|
||||||
plottables = new TimePlottableGroup();
|
|
||||||
plottables.setMaxAge(Duration.parse(plotFrameMeta.getString("maxAge", "PT2H")));
|
|
||||||
plotArea.getChildren().clear();
|
|
||||||
plotFrame = new JFreeChartFrame(plotFrameMeta);
|
|
||||||
PlotUtils.setXAxis(plotFrame, "timestamp", null, "time");
|
|
||||||
PlotContainer container = PlotContainer.anchorTo(plotArea);
|
|
||||||
container.setPlot(plotFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupChannels(PKT8Device device) {
|
|
||||||
Collection<PKT8Channel> channels = device.getChanels();
|
|
||||||
|
|
||||||
//plot config from device configuration
|
|
||||||
//Do not use view config here, it is applyed separately
|
|
||||||
channels.stream()
|
|
||||||
.filter(channel -> !plottables.has(channel.getName()))
|
|
||||||
.forEachOrdered(channel -> {
|
|
||||||
//plot config from device configuration
|
|
||||||
TimePlottable plottable = new TimePlottable(channel.getName());
|
|
||||||
plottable.configure(channel.meta());
|
|
||||||
plottables.add(plottable);
|
|
||||||
plotFrame.add(plottable);
|
|
||||||
});
|
|
||||||
if (device.meta().hasMeta("plotConfig")) {
|
|
||||||
plottables.applyConfig(device.meta().getMeta("plotConfig"));
|
|
||||||
plottables.setMaxItems(1000);
|
|
||||||
plottables.setPrefItems(400);
|
|
||||||
}
|
|
||||||
// getPlottables.applyConfig(plotFrame.getConfig());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void onMeasurementResult(Measurement measurement, Object result, Instant time) {
|
|
||||||
PKT8Result res = PKT8Result.class.cast(result);
|
|
||||||
//PENDING replace by connection?
|
|
||||||
if (rawDataButton.isSelected()) {
|
|
||||||
plottables.put(res.channel, res.rawValue);
|
|
||||||
} else {
|
|
||||||
plottables.put(res.channel, res.temperature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMeasurementFailed(Measurement measurement, Throwable exception) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Node getFXNode() {
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,150 +0,0 @@
|
|||||||
package inr.numass.control.cryotemp;
|
|
||||||
|
|
||||||
import hep.dataforge.context.Context;
|
|
||||||
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 inr.numass.control.DeviceViewConnection;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.event.ActionEvent;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.fxml.FXMLLoader;
|
|
||||||
import javafx.fxml.Initializable;
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.TableColumn;
|
|
||||||
import javafx.scene.control.TableView;
|
|
||||||
import javafx.scene.control.ToggleButton;
|
|
||||||
import javafx.scene.control.cell.PropertyValueFactory;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by darksnake on 07-Oct-16.
|
|
||||||
*/
|
|
||||||
public class PKT8View extends DeviceViewConnection<PKT8Device> implements Initializable, MeasurementListener {
|
|
||||||
|
|
||||||
public static PKT8View build(Context context) {
|
|
||||||
try {
|
|
||||||
FXMLLoader loader = new FXMLLoader(context.getClassLoader().getResource("fxml/PKT8Indicator.fxml"));
|
|
||||||
loader.setClassLoader(context.getClassLoader());
|
|
||||||
loader.load();
|
|
||||||
return loader.getController();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new Error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private LogFragment logFragment;
|
|
||||||
private PKT8PlotFragment plotFragment;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private BorderPane root;
|
|
||||||
@FXML
|
|
||||||
private ToggleButton startStopButton;
|
|
||||||
@FXML
|
|
||||||
private ToggleButton storeButton;
|
|
||||||
@FXML
|
|
||||||
private ToggleButton consoleButton;
|
|
||||||
@FXML
|
|
||||||
private ToggleButton plotButton;
|
|
||||||
@FXML
|
|
||||||
private Label lastUpdateLabel;
|
|
||||||
@FXML
|
|
||||||
private TableView<PKT8Result> table;
|
|
||||||
@FXML
|
|
||||||
private TableColumn<TableView<PKT8Result>, String> sensorColumn;
|
|
||||||
@FXML
|
|
||||||
private TableColumn<TableView<PKT8Result>, Double> resColumn;
|
|
||||||
@FXML
|
|
||||||
private TableColumn<TableView<PKT8Result>, String> tempColumn;
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
|
||||||
sensorColumn.setCellValueFactory(new PropertyValueFactory<>("channel"));
|
|
||||||
resColumn.setCellValueFactory(new PropertyValueFactory<>("rawString"));
|
|
||||||
tempColumn.setCellValueFactory(new PropertyValueFactory<>("temperatureString"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open(@NotNull PKT8Device device) throws Exception {
|
|
||||||
super.open(device);
|
|
||||||
|
|
||||||
this.logFragment = new LogFragment();
|
|
||||||
logFragment.addRootLogHandler();
|
|
||||||
new FragmentWindow(logFragment).bindTo(consoleButton);
|
|
||||||
|
|
||||||
|
|
||||||
plotFragment = new PKT8PlotFragment(device);
|
|
||||||
startStopButton.selectedProperty().setValue(getDevice().isMeasuring());
|
|
||||||
|
|
||||||
new FragmentWindow(plotFragment).bindTo(plotButton);
|
|
||||||
bindBooleanToState("storing", storeButton.selectedProperty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws Exception {
|
|
||||||
super.close();
|
|
||||||
logFragment = null;
|
|
||||||
plotFragment = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMeasurementResult(Measurement<?> measurement, Object result, Instant time) {
|
|
||||||
PKT8Result res = PKT8Result.class.cast(result);
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
lastUpdateLabel.setText(time.toString());
|
|
||||||
table.getItems().removeIf(it -> it.channel.equals(res.channel));
|
|
||||||
table.getItems().add(res);
|
|
||||||
table.getItems().sort(Comparator.comparing(o -> o.channel));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMeasurementFailed(Measurement measurement, Throwable exception) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void startMeasurement() throws MeasurementException {
|
|
||||||
getDevice().startMeasurement();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopMeasurement() throws MeasurementException {
|
|
||||||
if (getDevice().isMeasuring()) {
|
|
||||||
getDevice().stopMeasurement(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private void onStartStopClick(ActionEvent event) {
|
|
||||||
if (getDevice() != null) {
|
|
||||||
try {
|
|
||||||
if (startStopButton.isSelected()) {
|
|
||||||
startMeasurement();
|
|
||||||
} else {
|
|
||||||
//in case device started
|
|
||||||
stopMeasurement();
|
|
||||||
}
|
|
||||||
} catch (ControlException ex) {
|
|
||||||
getDevice().getLogger().error("Failed to start or stop device", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Node getFXNode() {
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
}
|
|
@ -27,7 +27,7 @@ public class PKT8VirtualPort extends VirtualPort implements Metoid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void evaluateRequest(String request) {
|
protected synchronized void evaluateRequest(String request) {
|
||||||
switch (request) {
|
switch (request) {
|
||||||
case "s":
|
case "s":
|
||||||
String[] letters = {"a", "b", "c", "d", "e", "f", "g", "h"};
|
String[] letters = {"a", "b", "c", "d", "e", "f", "g", "h"};
|
||||||
@ -48,16 +48,15 @@ public class PKT8VirtualPort extends VirtualPort implements Metoid {
|
|||||||
() -> {
|
() -> {
|
||||||
double res = average + generator.nextGaussian() * sigma;
|
double res = average + generator.nextGaussian() * sigma;
|
||||||
//TODO convert double value to formatted string
|
//TODO convert double value to formatted string
|
||||||
return letter + "000120000\n";
|
return String.format("%s000%d", letter, (int) (res * 100));
|
||||||
},
|
},
|
||||||
Duration.ZERO, Duration.ofMillis(200), letter, "measurement"
|
Duration.ZERO, Duration.ofMillis(500), letter, "measurement"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case "p":
|
case "p":
|
||||||
cancelByTag("measurement");
|
cancelByTag("measurement");
|
||||||
this.recievePhrase("stopped\n\r");
|
this.receivePhrase("Stopped");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.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
|
||||||
|
import javafx.stage.Stage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author darksnake
|
||||||
|
*/
|
||||||
|
class PKT8App : NumassControlApplication<PKT8Device>() {
|
||||||
|
override fun buildView(device: PKT8Device): DeviceViewConnection<PKT8Device> {
|
||||||
|
return PKT8ViewConnection().apply {
|
||||||
|
device.connect(this, Roles.VIEW_ROLE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val deviceFactory: DeviceFactory = PKT8DeviceFactory()
|
||||||
|
|
||||||
|
override fun setupStage(stage: Stage, device: PKT8Device) {
|
||||||
|
stage.title = "Numass temperature view " + device.name
|
||||||
|
stage.minHeight = 400.0
|
||||||
|
stage.minWidth = 400.0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun acceptDevice(meta: Meta): Boolean {
|
||||||
|
return meta.getString("type") == "PKT8"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -14,32 +14,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package inr.numass.control.cryotemp;
|
package inr.numass.control.cryotemp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by darksnake on 28-Sep-16.
|
* Created by darksnake on 28-Sep-16.
|
||||||
*/
|
*/
|
||||||
public class PKT8Result {
|
data class PKT8Result(var channel: String, var rawValue: Double, var temperature: Double) {
|
||||||
|
|
||||||
public String channel;
|
val rawString: String = String.format("%.2f", rawValue)
|
||||||
public double rawValue;
|
|
||||||
public double temperature;
|
|
||||||
|
|
||||||
public PKT8Result(String channel, double rawValue, double temperature) {
|
val temperatureString: String = String.format("%.2f", temperature)
|
||||||
this.channel = channel;
|
|
||||||
this.rawValue = rawValue;
|
|
||||||
this.temperature = temperature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getChannel() {
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRawString() {
|
|
||||||
return String.format("%.2f", rawValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTemperatureString() {
|
|
||||||
return String.format("%.2f", temperature);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,224 @@
|
|||||||
|
package inr.numass.control.cryotemp
|
||||||
|
|
||||||
|
import hep.dataforge.control.devices.Sensor
|
||||||
|
import hep.dataforge.control.measurements.Measurement
|
||||||
|
import hep.dataforge.control.measurements.MeasurementListener
|
||||||
|
import hep.dataforge.fx.fragments.FXFragment
|
||||||
|
import hep.dataforge.fx.fragments.FragmentWindow
|
||||||
|
import hep.dataforge.fx.fragments.LogFragment
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
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 inr.numass.control.DeviceViewConnection
|
||||||
|
import javafx.application.Platform
|
||||||
|
import javafx.beans.binding.ListBinding
|
||||||
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
|
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
|
||||||
|
import javafx.scene.layout.VBox
|
||||||
|
import javafx.scene.text.Font
|
||||||
|
import tornadofx.*
|
||||||
|
import java.time.Duration
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by darksnake on 30-May-17.
|
||||||
|
*/
|
||||||
|
class PKT8ViewConnection : DeviceViewConnection<PKT8Device>(), MeasurementListener {
|
||||||
|
private val cryoView by lazy { CryoView() }
|
||||||
|
private val plotView by lazy { CryoPlotView() }
|
||||||
|
|
||||||
|
internal val table = FXCollections.observableHashMap<String, PKT8Result>()
|
||||||
|
|
||||||
|
|
||||||
|
val lastUpdateProperty = SimpleObjectProperty<String>("NEVER")
|
||||||
|
|
||||||
|
|
||||||
|
override fun getBoardView(): Parent {
|
||||||
|
return VBox().apply {
|
||||||
|
this += super.getBoardView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFXNode(): Node {
|
||||||
|
if (!isOpen) {
|
||||||
|
throw RuntimeException("Not connected!")
|
||||||
|
}
|
||||||
|
return cryoView.root;
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMeasurementFailed(measurement: Measurement<*>, exception: Throwable) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMeasurementResult(measurement: Measurement<*>, result: Any, time: Instant) {
|
||||||
|
if (result is PKT8Result) {
|
||||||
|
Platform.runLater {
|
||||||
|
lastUpdateProperty.set(time.toString())
|
||||||
|
table.put(result.channel, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class CryoView() : View() {
|
||||||
|
private var plotButton: ToggleButton by singleAssign()
|
||||||
|
private var logButton: ToggleButton by singleAssign()
|
||||||
|
|
||||||
|
private val logWindow = FragmentWindow(LogFragment().apply {
|
||||||
|
addLogHandler(device.logger)
|
||||||
|
})
|
||||||
|
|
||||||
|
// need those to have strong references to listeners
|
||||||
|
private val plotView = CryoPlotView();
|
||||||
|
private val plotWindow = FragmentWindow(FXFragment.buildFromNode(plotView.title) { plotView.root })
|
||||||
|
|
||||||
|
override val root = borderpane {
|
||||||
|
top {
|
||||||
|
toolbar {
|
||||||
|
togglebutton("Measure") {
|
||||||
|
isSelected = false
|
||||||
|
bindBooleanToState(Sensor.MEASURING_STATE, selectedProperty())
|
||||||
|
}
|
||||||
|
togglebutton("Store") {
|
||||||
|
isSelected = false
|
||||||
|
bindBooleanToState("storing", selectedProperty())
|
||||||
|
}
|
||||||
|
separator(Orientation.VERTICAL)
|
||||||
|
pane {
|
||||||
|
hgrow = Priority.ALWAYS
|
||||||
|
}
|
||||||
|
separator(Orientation.VERTICAL)
|
||||||
|
|
||||||
|
plotButton = togglebutton("Plot") {
|
||||||
|
isSelected = false
|
||||||
|
plotWindow.bindTo(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
logButton = togglebutton("Log") {
|
||||||
|
isSelected = false
|
||||||
|
logWindow.bindTo(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
center {
|
||||||
|
tableview<PKT8Result> {
|
||||||
|
items = object : ListBinding<PKT8Result>() {
|
||||||
|
init {
|
||||||
|
bind(table)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun computeValue(): ObservableList<PKT8Result> {
|
||||||
|
return FXCollections.observableArrayList(table.values).apply {
|
||||||
|
sortBy { it.channel }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
column("Sensor", PKT8Result::channel);
|
||||||
|
column("Resistance", PKT8Result::rawValue).cellFormat {
|
||||||
|
text = String.format("%.2f", it)
|
||||||
|
}
|
||||||
|
column("Temperature", PKT8Result::temperature).cellFormat {
|
||||||
|
text = String.format("%.2f", it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bottom {
|
||||||
|
toolbar {
|
||||||
|
label("Last update: ")
|
||||||
|
label(lastUpdateProperty) {
|
||||||
|
font = Font.font("System Bold")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class CryoPlotView : View("PKT8 temperature plot") {
|
||||||
|
val plotFrameMeta: Meta = device.meta.getMetaOrEmpty("plotConfig")
|
||||||
|
|
||||||
|
val plotFrame: FXPlotFrame by lazy {
|
||||||
|
JFreeChartFrame(plotFrameMeta).apply {
|
||||||
|
PlotUtils.setXAxis(this, "timestamp", null, "time")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var rawDataButton: ToggleButton by singleAssign()
|
||||||
|
|
||||||
|
val plottables: TimePlottableGroup by lazy {
|
||||||
|
TimePlottableGroup().apply {
|
||||||
|
setMaxAge(Duration.parse(plotFrameMeta.getString("maxAge", "PT2H")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val root: Parent = borderpane {
|
||||||
|
prefWidth = 800.0
|
||||||
|
prefHeight = 600.0
|
||||||
|
PlotContainer.centerIn(this).plot = plotFrame
|
||||||
|
top {
|
||||||
|
toolbar {
|
||||||
|
rawDataButton = togglebutton("Raw data") {
|
||||||
|
isSelected = false
|
||||||
|
action {
|
||||||
|
clearPlot()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button("Reset") {
|
||||||
|
action {
|
||||||
|
clearPlot()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
val channels = device.chanels
|
||||||
|
|
||||||
|
//plot config from device configuration
|
||||||
|
//Do not use view config here, it is applyed separately
|
||||||
|
channels.stream()
|
||||||
|
.filter { channel -> !plottables.has(channel.name) }
|
||||||
|
.forEachOrdered { channel ->
|
||||||
|
//plot config from device configuration
|
||||||
|
val plottable = TimePlottable(channel.name)
|
||||||
|
plottable.configure(channel.meta())
|
||||||
|
plottables.add(plottable)
|
||||||
|
plotFrame.add(plottable)
|
||||||
|
}
|
||||||
|
if (device.meta().hasMeta("plotConfig")) {
|
||||||
|
plottables.applyConfig(device.meta().getMeta("plotConfig"))
|
||||||
|
plottables.setMaxItems(1000)
|
||||||
|
plottables.setPrefItems(400)
|
||||||
|
}
|
||||||
|
table.addListener(MapChangeListener { change ->
|
||||||
|
if (change.wasAdded()) {
|
||||||
|
change.valueAdded.apply {
|
||||||
|
if (rawDataButton.isSelected()) {
|
||||||
|
plottables.put(this.channel, this.rawValue)
|
||||||
|
} else {
|
||||||
|
plottables.put(this.channel, this.temperature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearPlot() {
|
||||||
|
plottables.forEach {
|
||||||
|
it.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@
|
|||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
<BorderPane fx:id="root" xmlns:fx="http://javafx.com/fxml/1" prefHeight="400.0" prefWidth="400.0"
|
<BorderPane fx:id="root" xmlns:fx="http://javafx.com/fxml/1" prefHeight="400.0" prefWidth="400.0"
|
||||||
xmlns="http://javafx.com/javafx/8.0.111" fx:controller="inr.numass.control.cryotemp.PKT8View">
|
xmlns="http://javafx.com/javafx/8.0.111" fx:controller="inr.numass.control.cryotemp.PKT8ViewConnection">
|
||||||
<center>
|
<center>
|
||||||
<TableView fx:id="table" BorderPane.alignment="CENTER">
|
<TableView fx:id="table" BorderPane.alignment="CENTER">
|
||||||
<columns>
|
<columns>
|
||||||
@ -31,14 +31,12 @@
|
|||||||
</top>
|
</top>
|
||||||
<bottom>
|
<bottom>
|
||||||
<ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
|
<ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
|
||||||
<items>
|
<Label text="Last update: "/>
|
||||||
<Label text="Last update: "/>
|
<Label fx:id="lastUpdateLabel" text="NONE">
|
||||||
<Label fx:id="lastUpdateLabel" text="NONE">
|
<font>
|
||||||
<font>
|
<Font name="System Bold" size="12.0"/>
|
||||||
<Font name="System Bold" size="12.0"/>
|
</font>
|
||||||
</font>
|
</Label>
|
||||||
</Label>
|
|
||||||
</items>
|
|
||||||
</ToolBar>
|
</ToolBar>
|
||||||
</bottom>
|
</bottom>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
|
@ -27,12 +27,10 @@ limitations under the License.
|
|||||||
</center>
|
</center>
|
||||||
<top>
|
<top>
|
||||||
<ToolBar BorderPane.alignment="CENTER">
|
<ToolBar BorderPane.alignment="CENTER">
|
||||||
<items>
|
<ToggleButton fx:id="rawDataButton" mnemonicParsing="false" text="Raw data"/>
|
||||||
<ToggleButton fx:id="rawDataButton" mnemonicParsing="false" text="Raw data"/>
|
<Separator orientation="VERTICAL"/>
|
||||||
<Separator orientation="VERTICAL"/>
|
<Pane HBox.hgrow="ALWAYS"/>
|
||||||
<Pane HBox.hgrow="ALWAYS"/>
|
<Separator orientation="VERTICAL"/>
|
||||||
<Separator orientation="VERTICAL"/>
|
|
||||||
</items>
|
|
||||||
</ToolBar>
|
</ToolBar>
|
||||||
</top>
|
</top>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
|
@ -18,10 +18,11 @@ package inr.numass.control.magnet;
|
|||||||
import hep.dataforge.control.ports.VirtualPort;
|
import hep.dataforge.control.ports.VirtualPort;
|
||||||
import hep.dataforge.exceptions.PortException;
|
import hep.dataforge.exceptions.PortException;
|
||||||
import hep.dataforge.meta.Meta;
|
import hep.dataforge.meta.Meta;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -69,7 +70,7 @@ public class VirtualLambdaPort extends VirtualPort {
|
|||||||
evaluateRequest(comand.trim(), value.trim());
|
evaluateRequest(comand.trim(), value.trim());
|
||||||
} catch (RuntimeException ex) {
|
} catch (RuntimeException ex) {
|
||||||
|
|
||||||
recievePhrase("FAIL");//TODO какая команда правильная?
|
receivePhrase("FAIL");//TODO какая команда правильная?
|
||||||
LoggerFactory.getLogger(getClass()).error("Request evaluation failure", ex);
|
LoggerFactory.getLogger(getClass()).error("Request evaluation failure", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ public class MagnetControllerApp extends Application {
|
|||||||
List<SafeMagnetController> controllers = new ArrayList<>();
|
List<SafeMagnetController> controllers = new ArrayList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage primaryStage) throws IOException, ControlException {
|
public void start(Stage stage) throws IOException, ControlException {
|
||||||
Locale.setDefault(Locale.US);// чтобы отделение десятичных знаков было точкой
|
Locale.setDefault(Locale.US);// чтобы отделение десятичных знаков было точкой
|
||||||
ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
|
ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
|
||||||
|
|
||||||
@ -108,10 +108,10 @@ public class MagnetControllerApp extends Application {
|
|||||||
Scene scene = new Scene(vbox, width, height);
|
Scene scene = new Scene(vbox, width, height);
|
||||||
|
|
||||||
|
|
||||||
primaryStage.setTitle("Numass magnet view");
|
stage.setTitle("Numass magnet view");
|
||||||
primaryStage.setScene(scene);
|
stage.setScene(scene);
|
||||||
primaryStage.setResizable(false);
|
stage.setResizable(false);
|
||||||
primaryStage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
package inr.numass.control;
|
|
||||||
|
|
||||||
import hep.dataforge.control.devices.Device;
|
|
||||||
import hep.dataforge.control.devices.DeviceListener;
|
|
||||||
import hep.dataforge.fx.fragments.FXFragment;
|
|
||||||
import javafx.scene.Parent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by darksnake on 20-Oct-16.
|
|
||||||
*/
|
|
||||||
public abstract class DeviceFragment<T extends Device> extends FXFragment implements DeviceListener {
|
|
||||||
|
|
||||||
private final T device;
|
|
||||||
|
|
||||||
protected DeviceFragment(T device) {
|
|
||||||
this.device = device;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Parent buildRoot() {
|
|
||||||
return buildRoot(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract Parent buildRoot(T device);
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void evaluateDeviceException(Device device, String message, Throwable exception) {
|
|
||||||
//do something pretty
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
package inr.numass.control;
|
|
||||||
|
|
||||||
import hep.dataforge.control.connections.DeviceConnection;
|
|
||||||
import hep.dataforge.control.devices.Device;
|
|
||||||
import hep.dataforge.control.devices.DeviceListener;
|
|
||||||
import hep.dataforge.fx.FXObject;
|
|
||||||
import hep.dataforge.values.Value;
|
|
||||||
import javafx.beans.binding.ObjectBinding;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by darksnake on 14-May-17.
|
|
||||||
*/
|
|
||||||
public abstract class DeviceViewConnection<D extends Device> extends DeviceConnection<D> implements DeviceListener, FXObject {
|
|
||||||
private Map<String, ObjectBinding<Value>> bindings = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get binding for a given device state
|
|
||||||
*
|
|
||||||
* @param state
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public ObjectBinding<Value> getStateBinding(String state) {
|
|
||||||
return bindings.computeIfAbsent(state, stateName ->
|
|
||||||
new ObjectBinding<Value>() {
|
|
||||||
@Override
|
|
||||||
protected Value computeValue() {
|
|
||||||
if(isOpen()) {
|
|
||||||
return getDevice().getState(stateName);
|
|
||||||
} else {
|
|
||||||
return Value.NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind existing boolean property to writable device state
|
|
||||||
*
|
|
||||||
* @param state
|
|
||||||
* @param property
|
|
||||||
*/
|
|
||||||
protected void bindBooleanToState(String state, BooleanProperty property) {
|
|
||||||
getStateBinding(state).addListener((observable, oldValue, newValue) -> {
|
|
||||||
if (isOpen() && oldValue != newValue) {
|
|
||||||
property.setValue(newValue.booleanValue());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
property.addListener((observable, oldValue, newValue) -> {
|
|
||||||
if (isOpen() && oldValue != newValue) {
|
|
||||||
getDevice().setState(state, newValue);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void notifyDeviceStateChanged(Device device, String name, Value state) {
|
|
||||||
if (bindings.containsKey(name)) {
|
|
||||||
bindings.get(name).invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * The small view for
|
|
||||||
// * @return
|
|
||||||
// */
|
|
||||||
// public abstract Optional<Parent> getBoardView();
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package inr.numass.control;
|
|
||||||
|
|
||||||
import hep.dataforge.control.devices.Device;
|
|
||||||
import hep.dataforge.control.devices.DeviceFactory;
|
|
||||||
|
|
||||||
public interface DeviceViewFactory extends DeviceFactory {
|
|
||||||
/**
|
|
||||||
* Create but do not connect view connection for the device
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
DeviceViewConnection buildView(Device device);
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
package inr.numass.control;
|
|
||||||
|
|
||||||
import ch.qos.logback.classic.Level;
|
|
||||||
import hep.dataforge.context.Context;
|
|
||||||
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 javafx.application.Application;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by darksnake on 14-May-17.
|
|
||||||
*/
|
|
||||||
public abstract class NumassControlApplication<D extends Device> extends Application {
|
|
||||||
private D device;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start(Stage primaryStage) throws Exception {
|
|
||||||
Locale.setDefault(Locale.US);// чтобы отделение десятичных знаков было точкой
|
|
||||||
ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
|
|
||||||
rootLogger.setLevel(Level.INFO);
|
|
||||||
|
|
||||||
device = setupDevice();
|
|
||||||
DeviceViewConnection<D> controller = buildView(device);
|
|
||||||
|
|
||||||
Scene scene = new Scene(controller.getPane());
|
|
||||||
primaryStage.setScene(scene);
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
device.connect(controller, Roles.VIEW_ROLE, Roles.DEVICE_LISTENER_ROLE);
|
|
||||||
});
|
|
||||||
|
|
||||||
primaryStage.show();
|
|
||||||
|
|
||||||
|
|
||||||
setupStage(primaryStage, device);
|
|
||||||
NumassControlUtils.setDFStageIcon(primaryStage);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a view connection
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected abstract DeviceViewConnection<D> buildView(D device);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a device factory for given device
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected abstract DeviceFactory getDeviceFactory();
|
|
||||||
|
|
||||||
protected abstract void setupStage(Stage stage, D device);
|
|
||||||
|
|
||||||
protected abstract boolean acceptDevice(Meta meta);
|
|
||||||
|
|
||||||
private D setupDevice() {
|
|
||||||
Meta config = NumassControlUtils.getConfig(this)
|
|
||||||
.orElseGet(() -> NumassControlUtils.readResourceMeta("/config/devices.xml"));
|
|
||||||
|
|
||||||
Context ctx = NumassControlUtils.setupContext(config);
|
|
||||||
Meta deviceConfig = NumassControlUtils.findDeviceMeta(config, this::acceptDevice)
|
|
||||||
.orElseThrow(() -> new RuntimeException("Device configuration not found"));
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
@SuppressWarnings("unchecked") D d = (D) getDeviceFactory().build(ctx, deviceConfig);
|
|
||||||
d.init();
|
|
||||||
NumassControlUtils.connectStorage(d, config);
|
|
||||||
|
|
||||||
return d;
|
|
||||||
} catch (ControlException e) {
|
|
||||||
throw new RuntimeException("Failed to build device", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stop() throws Exception {
|
|
||||||
super.stop();
|
|
||||||
if (device != null) {
|
|
||||||
device.shutdown();
|
|
||||||
device.getContext().close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,115 +0,0 @@
|
|||||||
package inr.numass.control;
|
|
||||||
|
|
||||||
import hep.dataforge.context.Context;
|
|
||||||
import hep.dataforge.context.Global;
|
|
||||||
import hep.dataforge.control.connections.Roles;
|
|
||||||
import hep.dataforge.control.connections.StorageConnection;
|
|
||||||
import hep.dataforge.control.devices.Device;
|
|
||||||
import hep.dataforge.exceptions.StorageException;
|
|
||||||
import hep.dataforge.io.MetaFileReader;
|
|
||||||
import hep.dataforge.io.XMLMetaReader;
|
|
||||||
import hep.dataforge.meta.Meta;
|
|
||||||
import hep.dataforge.storage.api.Storage;
|
|
||||||
import hep.dataforge.storage.commons.StorageFactory;
|
|
||||||
import hep.dataforge.storage.commons.StorageManager;
|
|
||||||
import inr.numass.client.ClientUtils;
|
|
||||||
import javafx.application.Application;
|
|
||||||
import javafx.scene.image.Image;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by darksnake on 08-May-17.
|
|
||||||
*/
|
|
||||||
public class NumassControlUtils {
|
|
||||||
public static final String DEFAULT_CONFIG_LOCATION = "./numass-control.xml";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a single or multiple storage connections for a device
|
|
||||||
*
|
|
||||||
* @param device
|
|
||||||
* @param config
|
|
||||||
*/
|
|
||||||
public static void connectStorage(Device device, Meta config) {
|
|
||||||
//TODO add on reset listener
|
|
||||||
if (config.hasMeta("storage") && device.acceptsRole(Roles.STORAGE_ROLE)) {
|
|
||||||
String numassRun = ClientUtils.getRunName(config);
|
|
||||||
config.getMetaList("storage").forEach(node -> {
|
|
||||||
device.getContext().getLogger().info("Creating storage for device with meta: {}", node);
|
|
||||||
//building storage in a separate thread
|
|
||||||
new Thread(() -> {
|
|
||||||
Storage storage = StorageFactory.buildStorage(device.getContext(), node);
|
|
||||||
if (!numassRun.isEmpty()) {
|
|
||||||
try {
|
|
||||||
storage = storage.buildShelf(numassRun, Meta.empty());
|
|
||||||
} catch (StorageException e) {
|
|
||||||
device.getContext().getLogger().error("Failed to build shelf", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
device.connect(new StorageConnection(storage), Roles.STORAGE_ROLE);
|
|
||||||
}).start();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Meta readResourceMeta(String path) {
|
|
||||||
try {
|
|
||||||
return new XMLMetaReader().read(NumassControlUtils.class.getResourceAsStream(path));
|
|
||||||
} catch (IOException | ParseException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Optional<Meta> getConfig(Application app) {
|
|
||||||
String debugConfig = app.getParameters().getNamed().get("config.resource");
|
|
||||||
if (debugConfig != null) {
|
|
||||||
return Optional.ofNullable(readResourceMeta(debugConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
String configFileName = app.getParameters().getNamed().get("config");
|
|
||||||
Logger logger = LoggerFactory.getLogger(app.getClass());
|
|
||||||
if (configFileName == null) {
|
|
||||||
logger.info("Configuration path not defined. Loading configuration from {}", DEFAULT_CONFIG_LOCATION);
|
|
||||||
configFileName = DEFAULT_CONFIG_LOCATION;
|
|
||||||
}
|
|
||||||
File configFile = new File(configFileName);
|
|
||||||
|
|
||||||
if (configFile.exists()) {
|
|
||||||
try {
|
|
||||||
Meta config = MetaFileReader.read(configFile).build();
|
|
||||||
return Optional.of(config);
|
|
||||||
} catch (IOException | ParseException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.warn("Configuration file not found");
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static Optional<Meta> findDeviceMeta(Meta config, Predicate<Meta> criterion) {
|
|
||||||
return config.getMetaList("device").stream().filter(criterion).findFirst().map(it -> it);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Context setupContext(Meta meta) {
|
|
||||||
Context ctx = Global.getContext("NUMASS-CONTROL");
|
|
||||||
ctx.pluginManager().getOrLoad(StorageManager.class);
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setDFStageIcon(Stage stage) {
|
|
||||||
stage.getIcons().add(getDFIcon());
|
|
||||||
}
|
|
||||||
public static Image getDFIcon(){
|
|
||||||
return new Image(NumassControlUtils.class.getResourceAsStream("/img/df.png"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
package inr.numass.control;
|
|
||||||
|
|
||||||
import hep.dataforge.control.connections.StorageConnection;
|
|
||||||
import hep.dataforge.control.devices.AbstractDevice;
|
|
||||||
import hep.dataforge.exceptions.StorageException;
|
|
||||||
import hep.dataforge.storage.api.PointLoader;
|
|
||||||
import hep.dataforge.tables.DataPoint;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A helper to store points in multiple loaders
|
|
||||||
* Created by darksnake on 16-May-17.
|
|
||||||
*/
|
|
||||||
public class StorageHelper implements AutoCloseable {
|
|
||||||
private final AbstractDevice device;
|
|
||||||
private final Map<StorageConnection, PointLoader> loaderMap = new HashMap<>();
|
|
||||||
private final Function<StorageConnection, PointLoader> loaderFactory;
|
|
||||||
|
|
||||||
public StorageHelper(AbstractDevice device, Function<StorageConnection, PointLoader> loaderFactory) {
|
|
||||||
this.device = device;
|
|
||||||
this.loaderFactory = loaderFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void push(DataPoint point) {
|
|
||||||
if (!device.hasState("storing") || device.getState("storing").booleanValue()) {
|
|
||||||
device.forEachConnection("storage", StorageConnection.class, connection -> {
|
|
||||||
PointLoader pl = loaderMap.computeIfAbsent(connection, loaderFactory);
|
|
||||||
try {
|
|
||||||
pl.push(point);
|
|
||||||
} catch (StorageException ex) {
|
|
||||||
device.getLogger().error("Push to loader failed", ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
loaderMap.values().forEach(it -> {
|
|
||||||
try {
|
|
||||||
it.close();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
device.getLogger().error("Failed to close Loader", ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,89 @@
|
|||||||
|
package inr.numass.control
|
||||||
|
|
||||||
|
import hep.dataforge.control.connections.DeviceConnection
|
||||||
|
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.fx.FXObject
|
||||||
|
import hep.dataforge.fx.fragments.FXFragment
|
||||||
|
import hep.dataforge.fx.fragments.FragmentWindow
|
||||||
|
import hep.dataforge.values.Value
|
||||||
|
import javafx.beans.binding.ObjectBinding
|
||||||
|
import javafx.beans.property.BooleanProperty
|
||||||
|
import javafx.geometry.Pos
|
||||||
|
import javafx.scene.Parent
|
||||||
|
import javafx.scene.layout.HBox
|
||||||
|
import javafx.scene.layout.Priority
|
||||||
|
import tornadofx.*
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by darksnake on 14-May-17.
|
||||||
|
*/
|
||||||
|
abstract class DeviceViewConnection<D : Device> : DeviceConnection<D>(), DeviceListener, FXObject {
|
||||||
|
private val bindings = HashMap<String, ObjectBinding<Value>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get binding for a given device state
|
||||||
|
|
||||||
|
* @param state
|
||||||
|
* *
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun getStateBinding(state: String): ObjectBinding<Value> {
|
||||||
|
return bindings.computeIfAbsent(state) { stateName ->
|
||||||
|
object : ObjectBinding<Value>() {
|
||||||
|
override fun computeValue(): Value {
|
||||||
|
if (isOpen) {
|
||||||
|
return device.getState(stateName)
|
||||||
|
} else {
|
||||||
|
return Value.NULL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind existing boolean property to writable device state
|
||||||
|
|
||||||
|
* @param state
|
||||||
|
* *
|
||||||
|
* @param property
|
||||||
|
*/
|
||||||
|
protected fun bindBooleanToState(state: String, property: BooleanProperty) {
|
||||||
|
getStateBinding(state).addListener { observable, oldValue, newValue ->
|
||||||
|
if (isOpen && oldValue !== newValue) {
|
||||||
|
property.value = newValue.booleanValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
property.addListener { observable, oldValue, newValue ->
|
||||||
|
if (isOpen && oldValue != newValue) {
|
||||||
|
device.setState(state, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun notifyDeviceStateChanged(device: Device, name: String, state: Value) {
|
||||||
|
bindings[name]?.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun getBoardView(): Parent {
|
||||||
|
return HBox().apply {
|
||||||
|
alignment = Pos.CENTER_LEFT
|
||||||
|
vgrow = Priority.ALWAYS;
|
||||||
|
deviceStateIndicator(this@DeviceViewConnection, Device.INITIALIZED_STATE)
|
||||||
|
deviceStateIndicator(this@DeviceViewConnection, PortSensor.CONNECTED_STATE)
|
||||||
|
deviceStateIndicator(this@DeviceViewConnection, Sensor.MEASURING_STATE)
|
||||||
|
deviceStateIndicator(this@DeviceViewConnection, "storing")
|
||||||
|
pane {
|
||||||
|
hgrow = Priority.ALWAYS
|
||||||
|
}
|
||||||
|
togglebutton("View") {
|
||||||
|
isSelected = false
|
||||||
|
FragmentWindow(FXFragment.buildFromNode(device.name) { fxNode }).bindTo(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package inr.numass.control
|
||||||
|
|
||||||
|
import hep.dataforge.control.devices.Device
|
||||||
|
import hep.dataforge.control.devices.DeviceFactory
|
||||||
|
|
||||||
|
interface DeviceViewFactory : DeviceFactory {
|
||||||
|
/**
|
||||||
|
* Create but do not connect view connection for the device
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun buildView(device: Device): DeviceViewConnection<*>
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
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 javafx.application.Platform
|
||||||
|
import javafx.scene.Scene
|
||||||
|
import javafx.stage.Stage
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import tornadofx.*
|
||||||
|
import java.util.*
|
||||||
|
import java.util.function.Predicate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by darksnake on 14-May-17.
|
||||||
|
*/
|
||||||
|
abstract class NumassControlApplication<D : Device> : App() {
|
||||||
|
private var device: D by singleAssign()
|
||||||
|
|
||||||
|
override fun start(stage: Stage) {
|
||||||
|
Locale.setDefault(Locale.US)// чтобы отделение десятичных знаков было точкой
|
||||||
|
val rootLogger = LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME) as ch.qos.logback.classic.Logger
|
||||||
|
rootLogger.level = Level.INFO
|
||||||
|
|
||||||
|
device = setupDevice()
|
||||||
|
val controller = buildView(device)
|
||||||
|
|
||||||
|
val scene = Scene(controller.pane)
|
||||||
|
stage.scene = scene
|
||||||
|
Platform.runLater { device.connect(controller, Roles.VIEW_ROLE, Roles.DEVICE_LISTENER_ROLE) }
|
||||||
|
|
||||||
|
stage.show()
|
||||||
|
|
||||||
|
|
||||||
|
setupStage(stage, device)
|
||||||
|
setDFStageIcon(stage)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a view connection
|
||||||
|
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected abstract fun buildView(device: D): DeviceViewConnection<D>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a device factory for given device
|
||||||
|
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected abstract val deviceFactory: DeviceFactory
|
||||||
|
|
||||||
|
protected abstract fun setupStage(stage: Stage, device: D)
|
||||||
|
|
||||||
|
protected abstract fun acceptDevice(meta: Meta): Boolean
|
||||||
|
|
||||||
|
private fun setupDevice(): D {
|
||||||
|
val config = getConfig(this)
|
||||||
|
.orElseGet { readResourceMeta("/config/devices.xml") }
|
||||||
|
|
||||||
|
val ctx = setupContext(config)
|
||||||
|
val deviceConfig = findDeviceMeta(config, Predicate<Meta> { this.acceptDevice(it) })
|
||||||
|
.orElseThrow { RuntimeException("Device configuration not found") }
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val d = deviceFactory.build(ctx, deviceConfig) as D
|
||||||
|
d.init()
|
||||||
|
connectStorage(d, config)
|
||||||
|
|
||||||
|
return d
|
||||||
|
} catch (e: ControlException) {
|
||||||
|
throw RuntimeException("Failed to build device", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stop() {
|
||||||
|
try {
|
||||||
|
device.shutdown()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
LoggerFactory.getLogger(javaClass).error("Failed to shutdown application", ex);
|
||||||
|
} finally {
|
||||||
|
device.context.close()
|
||||||
|
super.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
package inr.numass.control
|
||||||
|
|
||||||
|
import hep.dataforge.context.Context
|
||||||
|
import hep.dataforge.context.Global
|
||||||
|
import hep.dataforge.control.connections.Roles
|
||||||
|
import hep.dataforge.control.connections.StorageConnection
|
||||||
|
import hep.dataforge.control.devices.Device
|
||||||
|
import hep.dataforge.exceptions.StorageException
|
||||||
|
import hep.dataforge.io.MetaFileReader
|
||||||
|
import hep.dataforge.io.XMLMetaReader
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.storage.commons.StorageFactory
|
||||||
|
import hep.dataforge.storage.commons.StorageManager
|
||||||
|
import inr.numass.client.ClientUtils
|
||||||
|
import javafx.application.Application
|
||||||
|
import javafx.scene.image.Image
|
||||||
|
import javafx.stage.Stage
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.text.ParseException
|
||||||
|
import java.util.*
|
||||||
|
import java.util.function.Predicate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by darksnake on 08-May-17.
|
||||||
|
*/
|
||||||
|
val DEFAULT_CONFIG_LOCATION = "./numass-control.xml"
|
||||||
|
val dfIcon: Image = Image(Global::class.java.getResourceAsStream("/img/df.png"))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a single or multiple storage connections for a device
|
||||||
|
|
||||||
|
* @param device
|
||||||
|
* *
|
||||||
|
* @param config
|
||||||
|
*/
|
||||||
|
fun connectStorage(device: Device, config: Meta) {
|
||||||
|
//TODO add on reset listener
|
||||||
|
if (config.hasMeta("storage") && device.acceptsRole(Roles.STORAGE_ROLE)) {
|
||||||
|
val numassRun = ClientUtils.getRunName(config)
|
||||||
|
config.getMetaList("storage").forEach { node ->
|
||||||
|
device.context.logger.info("Creating storage for device with meta: {}", node)
|
||||||
|
//building storage in a separate thread
|
||||||
|
Thread {
|
||||||
|
var storage = StorageFactory.buildStorage(device.context, node)
|
||||||
|
if (!numassRun.isEmpty()) {
|
||||||
|
try {
|
||||||
|
storage = storage.buildShelf(numassRun, Meta.empty())
|
||||||
|
} catch (e: StorageException) {
|
||||||
|
device.context.logger.error("Failed to build shelf", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
device.connect(StorageConnection(storage), Roles.STORAGE_ROLE)
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readResourceMeta(path: String): Meta {
|
||||||
|
try {
|
||||||
|
return XMLMetaReader().read(Global::class.java.getResourceAsStream(path))
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw RuntimeException(e)
|
||||||
|
} catch (e: ParseException) {
|
||||||
|
throw RuntimeException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getConfig(app: Application): Optional<Meta> {
|
||||||
|
val debugConfig = app.parameters.named["config.resource"]
|
||||||
|
if (debugConfig != null) {
|
||||||
|
return Optional.ofNullable(readResourceMeta(debugConfig))
|
||||||
|
}
|
||||||
|
|
||||||
|
var configFileName: String? = app.parameters.named["config"]
|
||||||
|
val logger = LoggerFactory.getLogger(app.javaClass)
|
||||||
|
if (configFileName == null) {
|
||||||
|
logger.info("Configuration path not defined. Loading configuration from {}", DEFAULT_CONFIG_LOCATION)
|
||||||
|
configFileName = DEFAULT_CONFIG_LOCATION
|
||||||
|
}
|
||||||
|
val configFile = File(configFileName)
|
||||||
|
|
||||||
|
if (configFile.exists()) {
|
||||||
|
try {
|
||||||
|
val config = MetaFileReader.read(configFile).build()
|
||||||
|
return Optional.of(config)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw RuntimeException(e)
|
||||||
|
} catch (e: ParseException) {
|
||||||
|
throw RuntimeException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
logger.warn("Configuration file not found")
|
||||||
|
return Optional.empty<Meta>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun findDeviceMeta(config: Meta, criterion: Predicate<Meta>): Optional<Meta> {
|
||||||
|
return config.getMetaList("device").stream().filter(criterion).findFirst().map { it -> it }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setupContext(meta: Meta): Context {
|
||||||
|
val ctx = Global.getContext("NUMASS-CONTROL")
|
||||||
|
ctx.pluginManager().getOrLoad(StorageManager::class.java)
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDFStageIcon(stage: Stage) {
|
||||||
|
stage.icons.add(dfIcon)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
|||||||
|
package inr.numass.control
|
||||||
|
|
||||||
|
import hep.dataforge.control.connections.StorageConnection
|
||||||
|
import hep.dataforge.control.devices.AbstractDevice
|
||||||
|
import hep.dataforge.exceptions.StorageException
|
||||||
|
import hep.dataforge.storage.api.PointLoader
|
||||||
|
import hep.dataforge.tables.DataPoint
|
||||||
|
import java.util.*
|
||||||
|
import java.util.function.Function
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper to store points in multiple loaders
|
||||||
|
* Created by darksnake on 16-May-17.
|
||||||
|
*/
|
||||||
|
class StorageHelper(private val device: AbstractDevice, private val loaderFactory: Function<StorageConnection, PointLoader>) : AutoCloseable {
|
||||||
|
private val loaderMap = HashMap<StorageConnection, PointLoader>()
|
||||||
|
|
||||||
|
fun push(point: DataPoint) {
|
||||||
|
if (!device.hasState("storing") || device.getState("storing").booleanValue()) {
|
||||||
|
device.forEachConnection("storage", StorageConnection::class.java) { connection ->
|
||||||
|
val pl = loaderMap.computeIfAbsent(connection, loaderFactory)
|
||||||
|
try {
|
||||||
|
pl.push(point)
|
||||||
|
} catch (ex: StorageException) {
|
||||||
|
device.logger.error("Push to loader failed", ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
loaderMap.values.forEach { it ->
|
||||||
|
try {
|
||||||
|
it.close()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
device.logger.error("Failed to close Loader", ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -25,7 +25,7 @@ public class TestVac extends Application {
|
|||||||
VacCollectorView controller;
|
VacCollectorView controller;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage primaryStage) {
|
public void start(Stage stage) {
|
||||||
try {
|
try {
|
||||||
Sensor<Double> sensor1 = Virtual.randomDoubleSensor("vac1", Duration.ofMillis(200), 1e-5, 2e-6);
|
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> sensor2 = Virtual.randomDoubleSensor("vac2", Duration.ofMillis(200), 2e-5, 2e-6);
|
||||||
@ -65,9 +65,9 @@ public class TestVac extends Application {
|
|||||||
|
|
||||||
Scene scene = new Scene(loader.getRoot(), 800, 600);
|
Scene scene = new Scene(loader.getRoot(), 800, 600);
|
||||||
|
|
||||||
primaryStage.setTitle("Vacuum measurement test");
|
stage.setTitle("Vacuum measurement test");
|
||||||
primaryStage.setScene(scene);
|
stage.setScene(scene);
|
||||||
primaryStage.show();
|
stage.show();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new Error(ex);
|
throw new Error(ex);
|
||||||
}
|
}
|
||||||
|
@ -28,15 +28,17 @@ import inr.numass.debunch.FrameAnalizer;
|
|||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
import static hep.dataforge.values.ValueType.NUMBER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Darksnake
|
* @author Darksnake
|
||||||
*/
|
*/
|
||||||
@TypedActionDef(name = "debunch", inputType = RawNMFile.class, outputType = RawNMFile.class)
|
@TypedActionDef(name = "debunch", inputType = RawNMFile.class, outputType = RawNMFile.class)
|
||||||
@ValueDef(name = "upperchanel", type = "NUMBER", def = "4095", info = "An upper chanel for debuncing")
|
@ValueDef(name = "upperchanel", type = {NUMBER}, def = "4095", info = "An upper chanel for debuncing")
|
||||||
@ValueDef(name = "lowerchanel", type = "NUMBER", def = "0", info = "A lower chanel for debuncing")
|
@ValueDef(name = "lowerchanel", type = {NUMBER}, def = "0", info = "A lower chanel for debuncing")
|
||||||
@ValueDef(name = "rejectprob", type = "NUMBER", def = "1e-5", info = "Rejection probability")
|
@ValueDef(name = "rejectprob", type = {NUMBER}, def = "1e-5", info = "Rejection probability")
|
||||||
@ValueDef(name = "framelength", type = "NUMBER", def = "5", info = "Frame length in seconds")
|
@ValueDef(name = "framelength", type = {NUMBER}, def = "5", info = "Frame length in seconds")
|
||||||
@ValueDef(name = "maxcr", type = "NUMBER", def = "100", info = "Maximum count rate for debunching")
|
@ValueDef(name = "maxcr", type = {NUMBER}, def = "100", info = "Maximum count rate for debunching")
|
||||||
public class DebunchAction extends OneToOneAction<RawNMFile, RawNMFile> {
|
public class DebunchAction extends OneToOneAction<RawNMFile, RawNMFile> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -37,11 +37,13 @@ import java.util.Map.Entry;
|
|||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
import static hep.dataforge.values.ValueType.NUMBER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Darksnake
|
* @author Darksnake
|
||||||
*/
|
*/
|
||||||
@TypedActionDef(name = "monitor", inputType = Table.class, outputType = Table.class)
|
@TypedActionDef(name = "monitor", inputType = Table.class, outputType = Table.class)
|
||||||
@ValueDef(name = "monitorPoint", type = "NUMBER", required = true, info = "The Uset for monitor point")
|
@ValueDef(name = "monitorPoint", type = {NUMBER}, required = true, info = "The Uset for monitor point")
|
||||||
@ValueDef(name = "monitorFile", info = "The outputfile for monitor points", def = "monitor.onComplete")
|
@ValueDef(name = "monitorFile", info = "The outputfile for monitor points", def = "monitor.onComplete")
|
||||||
@ValueDef(name = "calculateRelative", info = "Calculate count rate relative to average monitor point", def = "false")
|
@ValueDef(name = "calculateRelative", info = "Calculate count rate relative to average monitor point", def = "false")
|
||||||
public class MonitorCorrectAction extends OneToOneAction<Table, Table> {
|
public class MonitorCorrectAction extends OneToOneAction<Table, Table> {
|
||||||
|
@ -44,16 +44,18 @@ import java.util.Map;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static hep.dataforge.values.ValueType.NUMBER;
|
||||||
|
import static hep.dataforge.values.ValueType.STRING;
|
||||||
import static inr.numass.utils.TritiumUtils.pointExpression;
|
import static inr.numass.utils.TritiumUtils.pointExpression;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Darksnake
|
* @author Darksnake
|
||||||
*/
|
*/
|
||||||
@TypedActionDef(name = "prepareData", inputType = NumassData.class, outputType = Table.class)
|
@TypedActionDef(name = "prepareData", inputType = NumassData.class, outputType = Table.class)
|
||||||
@ValueDef(name = "lowerWindow", type = "NUMBER", def = "0", info = "Base for the window lowerWindow bound")
|
@ValueDef(name = "lowerWindow", type = {NUMBER}, def = "0", info = "Base for the window lowerWindow bound")
|
||||||
@ValueDef(name = "lowerWindowSlope", type = "NUMBER", def = "0", info = "Slope for the window lowerWindow bound")
|
@ValueDef(name = "lowerWindowSlope", type = {NUMBER}, def = "0", info = "Slope for the window lowerWindow bound")
|
||||||
@ValueDef(name = "upperWindow", type = "NUMBER", info = "Upper bound for window")
|
@ValueDef(name = "upperWindow", type = {NUMBER}, info = "Upper bound for window")
|
||||||
@ValueDef(name = "deadTime", type = "[NUMBER, STRING]", info = "Dead time in s. Could be an expression.")
|
@ValueDef(name = "deadTime", type = {NUMBER, STRING}, info = "Dead time in s. Could be an expression.")
|
||||||
@ValueDef(name = "correction",
|
@ValueDef(name = "correction",
|
||||||
info = "An expression to correct count number depending on potential `U`, point length `T` and point itself as `point`")
|
info = "An expression to correct count number depending on potential `U`, point length `T` and point itself as `point`")
|
||||||
@ValueDef(name = "utransform", info = "Expression for voltage transformation. Uses U as input")
|
@ValueDef(name = "utransform", info = "Expression for voltage transformation. Uses U as input")
|
||||||
@ -103,9 +105,9 @@ public class PrepareDataAction extends OneToOneAction<NumassData, Table> {
|
|||||||
utransform = Function.identity();
|
utransform = Function.identity();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(meta.hasMeta("debunch")){
|
if (meta.hasMeta("debunch")) {
|
||||||
if(dataFile instanceof NumassDataLoader){
|
if (dataFile instanceof NumassDataLoader) {
|
||||||
dataFile = ((NumassDataLoader) dataFile).applyRawTransformation(raw->debunch(context,raw,meta.getMeta("debunch")));
|
dataFile = ((NumassDataLoader) dataFile).applyRawTransformation(raw -> debunch(context, raw, meta.getMeta("debunch")));
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("Debunch not available");
|
throw new RuntimeException("Debunch not available");
|
||||||
}
|
}
|
||||||
@ -173,8 +175,8 @@ public class PrepareDataAction extends OneToOneAction<NumassData, Table> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ValueDef(name = "value", type = "[NUMBER, STRING]", info = "Value or function to multiply count rate")
|
@ValueDef(name = "value", type = {NUMBER, STRING}, info = "Value or function to multiply count rate")
|
||||||
@ValueDef(name = "err", type = "[NUMBER, STRING]", info = "error of the value")
|
@ValueDef(name = "err", type = {NUMBER, STRING}, info = "error of the value")
|
||||||
private Correction makeCorrection(Meta corrMeta) {
|
private Correction makeCorrection(Meta corrMeta) {
|
||||||
final String expr = corrMeta.getString("value");
|
final String expr = corrMeta.getString("value");
|
||||||
final String errExpr = corrMeta.getString("err", "");
|
final String errExpr = corrMeta.getString("err", "");
|
||||||
|
@ -26,6 +26,7 @@ import hep.dataforge.meta.Laminate;
|
|||||||
import inr.numass.data.NMFile;
|
import inr.numass.data.NMFile;
|
||||||
import inr.numass.data.RawNMFile;
|
import inr.numass.data.RawNMFile;
|
||||||
|
|
||||||
|
import static hep.dataforge.values.ValueType.NUMBER;
|
||||||
import static inr.numass.NumassIO.getNumassData;
|
import static inr.numass.NumassIO.getNumassData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,7 +37,7 @@ import static inr.numass.NumassIO.getNumassData;
|
|||||||
inputType = Binary.class, outputType = NMFile.class, info = "Read binary numass data file")
|
inputType = Binary.class, outputType = NMFile.class, info = "Read binary numass data file")
|
||||||
@ValueDef(name = "fileName", info = "The name of the file. By default equals file name.")
|
@ValueDef(name = "fileName", info = "The name of the file. By default equals file name.")
|
||||||
@ValueDef(name = "HVdev", info = "Divider for HV measurements. Should be set to 1.0 for numass data 2014",
|
@ValueDef(name = "HVdev", info = "Divider for HV measurements. Should be set to 1.0 for numass data 2014",
|
||||||
def = "2.468555393226049", type = "NUMBER")
|
def = "2.468555393226049", type = {NUMBER})
|
||||||
@ValueDef(name = "noUset", info = "If 'true', then Uset = Uread")
|
@ValueDef(name = "noUset", info = "If 'true', then Uset = Uread")
|
||||||
@NodeDef(name = "debunch", target = "class::inr.numass.actions.DebunchAction", info = "If given, governs debunching")
|
@NodeDef(name = "debunch", target = "class::inr.numass.actions.DebunchAction", info = "If given, governs debunching")
|
||||||
public class ReadLegacyDataAction extends OneToOneAction<Binary, NMFile> {
|
public class ReadLegacyDataAction extends OneToOneAction<Binary, NMFile> {
|
||||||
|
@ -24,6 +24,8 @@ import java.io.FileInputStream;
|
|||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static hep.dataforge.values.ValueType.BOOLEAN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compact all-in-one model for sterile neutrino spectrum
|
* Compact all-in-one model for sterile neutrino spectrum
|
||||||
*
|
*
|
||||||
@ -32,7 +34,7 @@ import java.io.InputStream;
|
|||||||
@NodeDef(name = "resolution")
|
@NodeDef(name = "resolution")
|
||||||
@NodeDef(name = "transmission")
|
@NodeDef(name = "transmission")
|
||||||
@ValueDef(name = "fssFile", info = "The name for external FSS file. By default internal FSS file is used")
|
@ValueDef(name = "fssFile", info = "The name for external FSS file. By default internal FSS file is used")
|
||||||
@ValueDef(name = "useFSS", type = "BOOLEAN")
|
@ValueDef(name = "useFSS", type = {BOOLEAN})
|
||||||
public class SterileNeutrinoSpectrum extends AbstractParametricFunction {
|
public class SterileNeutrinoSpectrum extends AbstractParametricFunction {
|
||||||
|
|
||||||
private static final String[] list = {"X", "trap", "E0", "mnu2", "msterile2", "U2"};
|
private static final String[] list = {"X", "trap", "E0", "mnu2", "msterile2", "U2"};
|
||||||
|
@ -67,7 +67,7 @@ public class TestModels {
|
|||||||
double A = meta.getDouble("resolution", meta.getDouble("resolution.width", 8.3e-5));//8.3e-5
|
double A = meta.getDouble("resolution", meta.getDouble("resolution.width", 8.3e-5));//8.3e-5
|
||||||
double from = meta.getDouble("from", 13900d);
|
double from = meta.getDouble("from", 13900d);
|
||||||
double to = meta.getDouble("to", 18700d);
|
double to = meta.getDouble("to", 18700d);
|
||||||
context.getLog().report("Setting up tritium model with real transmission function");
|
context.getChronicle().report("Setting up tritium model with real transmission function");
|
||||||
BivariateFunction resolutionTail;
|
BivariateFunction resolutionTail;
|
||||||
if (meta.hasValue("resolution.tailAlpha")) {
|
if (meta.hasValue("resolution.tailAlpha")) {
|
||||||
resolutionTail = ResolutionFunction.getAngledTail(meta.getDouble("resolution.tailAlpha"), meta.getDouble("resolution.tailBeta", 0));
|
resolutionTail = ResolutionFunction.getAngledTail(meta.getDouble("resolution.tailAlpha"), meta.getDouble("resolution.tailBeta", 0));
|
||||||
@ -78,7 +78,7 @@ public class TestModels {
|
|||||||
RangedNamedSetSpectrum beta = new BetaSpectrum();
|
RangedNamedSetSpectrum beta = new BetaSpectrum();
|
||||||
ModularSpectrum sp = new ModularSpectrum(beta, new ResolutionFunction(A, resolutionTail), from, to);
|
ModularSpectrum sp = new ModularSpectrum(beta, new ResolutionFunction(A, resolutionTail), from, to);
|
||||||
if (meta.getBoolean("caching", false)) {
|
if (meta.getBoolean("caching", false)) {
|
||||||
context.getLog().report("Caching turned on");
|
context.getChronicle().report("Caching turned on");
|
||||||
sp.setCaching(true);
|
sp.setCaching(true);
|
||||||
}
|
}
|
||||||
//Adding trapping energy dependence
|
//Adding trapping energy dependence
|
||||||
|
@ -31,7 +31,7 @@ import java.io.IOException;
|
|||||||
public class TestDirectoryViewer extends Application {
|
public class TestDirectoryViewer extends Application {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage primaryStage) throws IOException {
|
public void start(Stage stage) throws IOException {
|
||||||
new StorageManager().startGlobal();
|
new StorageManager().startGlobal();
|
||||||
|
|
||||||
NumassDataLoader reader = NumassDataLoader.fromLocalDir(null, new File("C:\\Users\\darksnake\\Dropbox\\PlayGround\\data-test\\20150703143643_1\\"));
|
NumassDataLoader reader = NumassDataLoader.fromLocalDir(null, new File("C:\\Users\\darksnake\\Dropbox\\PlayGround\\data-test\\20150703143643_1\\"));
|
||||||
@ -49,13 +49,13 @@ public class TestDirectoryViewer extends Application {
|
|||||||
|
|
||||||
Scene scene = new Scene(comp.getRoot(), 800, 600);
|
Scene scene = new Scene(comp.getRoot(), 800, 600);
|
||||||
|
|
||||||
primaryStage.setTitle("Detector Visualisation test");
|
stage.setTitle("Detector Visualisation test");
|
||||||
primaryStage.setScene(scene);
|
stage.setScene(scene);
|
||||||
primaryStage.setMinHeight(600);
|
stage.setMinHeight(600);
|
||||||
primaryStage.setMinWidth(800);
|
stage.setMinWidth(800);
|
||||||
// primaryStage.setResizable(false);
|
// primaryStage.setResizable(false);
|
||||||
|
|
||||||
primaryStage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +31,7 @@ class JFCTest : View("My View") {
|
|||||||
override val root = borderpane {
|
override val root = borderpane {
|
||||||
center {
|
center {
|
||||||
container.plot = plot
|
container.plot = plot
|
||||||
add(container.root)
|
add(container.pane)
|
||||||
}
|
}
|
||||||
bottom {
|
bottom {
|
||||||
add(button)
|
add(button)
|
||||||
|
Loading…
Reference in New Issue
Block a user