Updating Device UI

This commit is contained in:
Alexander Nozik 2017-05-14 16:11:22 +03:00
parent 7749dd31de
commit dcf318b16c
12 changed files with 524 additions and 340 deletions

View File

@ -1,15 +1,7 @@
configurations {
compile.exclude module: 'groovy'
compile.exclude module: 'groovy-all'
compile.exclude module: 'shichimifx'
compile.exclude module: 'zt-zip'
}
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:dataforge-fx" //project(':dataforge-fx')
// https://mvnrepository.com/artifact/org.controlsfx/controlsfx // https://mvnrepository.com/artifact/org.controlsfx/controlsfx
compile group: 'org.controlsfx', name: 'controlsfx', version: '8.40.12' compile group: 'org.controlsfx', name: 'controlsfx', version: '8.40.12'

View File

@ -21,7 +21,7 @@ import hep.dataforge.io.MetaFileReader;
import hep.dataforge.meta.Meta; import hep.dataforge.meta.Meta;
import hep.dataforge.meta.MetaUtils; import hep.dataforge.meta.MetaUtils;
import hep.dataforge.storage.commons.StorageManager; import hep.dataforge.storage.commons.StorageManager;
import inr.numass.control.NumassConnections; import inr.numass.control.NumassControlUtils;
import javafx.application.Application; import javafx.application.Application;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
@ -75,7 +75,7 @@ public class PKT8App extends Application {
device = setupDevice(deviceName, config); device = setupDevice(deviceName, config);
// setting up storage connections // setting up storage connections
NumassConnections.connectStorage(device, config); NumassControlUtils.connectStorage(device, config);
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/PKT8Indicator.fxml")); FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/PKT8Indicator.fxml"));
PKT8Controller controller = new PKT8Controller(); PKT8Controller controller = new PKT8Controller();

View File

@ -1,6 +1,6 @@
apply plugin: 'application' apply plugin: 'application'
version = "0.3.0" version = "0.4.0"
if (!hasProperty('mainClass')) { if (!hasProperty('mainClass')) {
ext.mainClass = 'inr.numass.control.msp.fx.MspApp' ext.mainClass = 'inr.numass.control.msp.fx.MspApp'

View File

@ -20,6 +20,7 @@ import hep.dataforge.control.connections.Roles;
import hep.dataforge.control.connections.StorageConnection; import hep.dataforge.control.connections.StorageConnection;
import hep.dataforge.control.devices.SingleMeasurementDevice; import hep.dataforge.control.devices.SingleMeasurementDevice;
import hep.dataforge.control.devices.annotations.RoleDef; import hep.dataforge.control.devices.annotations.RoleDef;
import hep.dataforge.control.devices.annotations.StateDef;
import hep.dataforge.control.measurements.AbstractMeasurement; import hep.dataforge.control.measurements.AbstractMeasurement;
import hep.dataforge.control.measurements.Measurement; import hep.dataforge.control.measurements.Measurement;
import hep.dataforge.control.ports.PortHandler; import hep.dataforge.control.ports.PortHandler;
@ -37,6 +38,7 @@ import hep.dataforge.tables.MapPoint;
import hep.dataforge.tables.TableFormat; import hep.dataforge.tables.TableFormat;
import hep.dataforge.tables.TableFormatBuilder; import hep.dataforge.tables.TableFormatBuilder;
import hep.dataforge.utils.DateTimeUtils; import hep.dataforge.utils.DateTimeUtils;
import hep.dataforge.values.Value;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.*;
@ -44,23 +46,28 @@ import java.util.concurrent.ConcurrentSkipListMap;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
*
* @author Alexander Nozik * @author Alexander Nozik
*/ */
@RoleDef(name = Roles.STORAGE_ROLE, objectType = StorageConnection.class) @RoleDef(name = Roles.STORAGE_ROLE, objectType = StorageConnection.class)
@RoleDef(name = Roles.VIEW_ROLE)
@StateDef(name = "connected", writable = true, info = "Connection with the device itself")
@StateDef(name = "storing", writable = true, info = "Define if this device is currently writes to storage")
@StateDef(name = "filamentOn", writable = true, info = "Mass-spectrometer filament on")
@StateDef(name = "filamentStatus", info = "Filament status")
public class MspDevice extends SingleMeasurementDevice implements PortHandler.PortController { public class MspDevice extends SingleMeasurementDevice implements PortHandler.PortController {
public static final String MSP_DEVICE_TYPE = "msp"; public static final String MSP_DEVICE_TYPE = "msp";
// private static final String PEAK_SET_PATH = "peakJump.peak"; // private static final String PEAK_SET_PATH = "peakJump.peak";
private static final int TIMEOUT = 200; private static final int TIMEOUT = 200;
boolean connected = false; // private boolean connected = false;
boolean selected = false; // private boolean selected = false;
boolean controlled = false; // private boolean controlled = false;
// private boolean storing = false;
private TcpPortHandler handler; private TcpPortHandler handler;
//listener //listener
private MspListener mspListener; private MspListener mspListener;
private Consumer<MspResponse> responseDelegate; private Consumer<MspResponse> responseDelegate;
private Consumer<Throwable> errorDelegate;
public MspDevice() { public MspDevice() {
} }
@ -81,17 +88,16 @@ public class MspDevice extends SingleMeasurementDevice implements PortHandler.Po
getLogger().info("Connection to MKS mass-spectrometer on {}:{}...", ip, port); getLogger().info("Connection to MKS mass-spectrometer on {}:{}...", ip, port);
handler = new TcpPortHandler(ip, port); handler = new TcpPortHandler(ip, port);
handler.setDelimeter("\r\r"); handler.setDelimeter("\r\r");
handler.holdBy(this);
setConnected(true);
} }
@Override @Override
public void shutdown() throws ControlException { public void shutdown() throws ControlException {
super.shutdown(); super.shutdown();
super.stopMeasurement(true); super.stopMeasurement(true);
setFileamentOn(false); if(isConnected()) {
setConnected(false); setFileamentOn(false);
getHandler().unholdBy(this); setConnected(false);
}
getHandler().close(); getHandler().close();
} }
@ -118,10 +124,14 @@ public class MspDevice extends SingleMeasurementDevice implements PortHandler.Po
@Override @Override
protected Object computeState(String stateName) throws ControlException { protected Object computeState(String stateName) throws ControlException {
switch (stateName) { switch (stateName) {
case "connected":
return false;
case "filamentOn": case "filamentOn":
return false;//Always return false on first request return false;//Always return false on first request
case "filamentStatus": case "filamentStatus":
return "UNKNOWN"; return "UNKNOWN";
case "storing":
return false;
default: default:
throw new ControlException("State not defined"); throw new ControlException("State not defined");
} }
@ -132,17 +142,17 @@ public class MspDevice extends SingleMeasurementDevice implements PortHandler.Po
return "MKS E-Vision"; return "MKS E-Vision";
} }
// @Override @Override
// public void command(String commandName, Value argument) throws ControlException { protected void requestStateChange(String stateName, Value value) throws ControlException {
// switch (commandName) { switch (stateName) {
// case "connect": case "connected":
// setConnected(argument.booleanValue()); setConnected(value.booleanValue());
// case "setFilamentOn": case "filamentOn":
// setFileamentOn(argument.booleanValue()); setFileamentOn(value.booleanValue());
// default: default:
// super.command(commandName, argument); super.requestStateChange(stateName, value);
// } }
// } }
/** /**
* Startup MSP: get available sensors, select sensor and control. * Startup MSP: get available sensors, select sensor and control.
@ -151,10 +161,11 @@ public class MspDevice extends SingleMeasurementDevice implements PortHandler.Po
* @return * @return
* @throws hep.dataforge.exceptions.ControlException * @throws hep.dataforge.exceptions.ControlException
*/ */
public boolean setConnected(boolean connected) throws ControlException { private boolean setConnected(boolean connected) throws ControlException {
String sensorName; String sensorName;
if (isConnected() != connected) { if (isConnected() != connected) {
if (connected) { if (connected) {
handler.holdBy(this);
MspResponse response = sendAndWait("Sensors"); MspResponse response = sendAndWait("Sensors");
if (response.isOK()) { if (response.isOK()) {
sensorName = response.get(2, 1); sensorName = response.get(2, 1);
@ -166,8 +177,8 @@ public class MspDevice extends SingleMeasurementDevice implements PortHandler.Po
response = sendAndWait("Select", sensorName); response = sendAndWait("Select", sensorName);
if (response.isOK()) { if (response.isOK()) {
selected = true; updateState("selected", true);
// updateState("selected", true); // selected = true;
} else { } else {
error(response.errorDescription(), null); error(response.errorDescription(), null);
return false; return false;
@ -175,17 +186,18 @@ public class MspDevice extends SingleMeasurementDevice implements PortHandler.Po
response = sendAndWait("Control", "inr.numass.msp", "1.0"); response = sendAndWait("Control", "inr.numass.msp", "1.0");
if (response.isOK()) { if (response.isOK()) {
controlled = true; // controlled = true;
// invalidateState("controlled"); // invalidateState("controlled");
// updateState("controlled", true); updateState("controlled", true);
} else { } else {
error(response.errorDescription(), null); error(response.errorDescription(), null);
return false; return false;
} }
connected = true; // connected = true;
// updateState("connected", true); updateState("connected", true);
return true; return true;
} else { } else {
handler.unholdBy(this);
return !sendAndWait("Release").isOK(); return !sendAndWait("Release").isOK();
} }
@ -238,7 +250,7 @@ public class MspDevice extends SingleMeasurementDevice implements PortHandler.Po
* @return * @return
* @throws PortException * @throws PortException
*/ */
public MspResponse sendAndWait(String commandName, Object... paremeters) throws PortException { private MspResponse sendAndWait(String commandName, Object... paremeters) throws PortException {
String request = buildCommand(commandName, paremeters); String request = buildCommand(commandName, paremeters);
if (mspListener != null) { if (mspListener != null) {
@ -254,18 +266,15 @@ public class MspDevice extends SingleMeasurementDevice implements PortHandler.Po
} }
public boolean isConnected() { public boolean isConnected() {
return connected; return getState("connected").booleanValue();
//return getState("connected") != null && getState("connected").booleanValue();
} }
public boolean isSelected() { public boolean isSelected() {
return selected; return getState("selected").booleanValue();
//return getState("selected") != null && getState("selected").booleanValue();
} }
public boolean isControlled() { public boolean isControlled() {
return controlled; return getState("controlled").booleanValue();
//return getState("controlled") != null && getState("controlled").booleanValue();
} }
public boolean isFilamentOn() { public boolean isFilamentOn() {
@ -279,9 +288,8 @@ public class MspDevice extends SingleMeasurementDevice implements PortHandler.Po
/** /**
* Turn filament on or off * Turn filament on or off
* *
* @return
*
* @param filamentOn * @param filamentOn
* @return
* @throws hep.dataforge.exceptions.PortException * @throws hep.dataforge.exceptions.PortException
*/ */
public boolean setFileamentOn(boolean filamentOn) throws PortException { public boolean setFileamentOn(boolean filamentOn) throws PortException {
@ -336,7 +344,7 @@ public class MspDevice extends SingleMeasurementDevice implements PortHandler.Po
} }
private TcpPortHandler getHandler() { private TcpPortHandler getHandler() {
if(handler == null){ if (handler == null) {
throw new RuntimeException("Device not initialized"); throw new RuntimeException("Device not initialized");
} }
return handler; return handler;
@ -397,7 +405,7 @@ public class MspDevice extends SingleMeasurementDevice implements PortHandler.Po
private final Map<Integer, Double> measurement = new ConcurrentSkipListMap<>(); private final Map<Integer, Double> measurement = new ConcurrentSkipListMap<>();
private final Map<StorageConnection, PointLoader> loaderMap = new HashMap<>(); private final Map<StorageConnection, PointLoader> loaderMap = new HashMap<>();
// private List<PointLoader> loaders = new ArrayList<>(); // private List<PointLoader> loaders = new ArrayList<>();
private final Meta meta; private final Meta meta;
private Map<Integer, String> peakMap; private Map<Integer, String> peakMap;
private double zero = 0; private double zero = 0;
@ -416,16 +424,13 @@ public class MspDevice extends SingleMeasurementDevice implements PortHandler.Po
} }
TableFormatBuilder builder = new TableFormatBuilder().addTime("timestamp"); TableFormatBuilder builder = new TableFormatBuilder().addTime("timestamp");
this.peakMap.values().stream().forEach((peakName) -> { this.peakMap.values().forEach(builder::addNumber);
builder.addNumber(peakName);
});
TableFormat format = builder.build(); TableFormat format = builder.build();
String suffix = Integer.toString((int) DateTimeUtils.now().toEpochMilli()); String suffix = DateTimeUtils.now().toString();
PointLoader loader = LoaderFactory return LoaderFactory
.buildPointLoder(storage, "msp" + suffix, "", "timestamp", format); .buildPointLoder(storage, "msp_" + suffix, "", "timestamp", format);
return loader;
} catch (StorageException ex) { } catch (StorageException ex) {
getLogger().error("Failed to create Loader", ex); getLogger().error("Failed to create Loader", ex);
return null; return null;
@ -505,30 +510,34 @@ public class MspDevice extends SingleMeasurementDevice implements PortHandler.Po
case "StartingScan": case "StartingScan":
if (mspListener != null && !measurement.isEmpty()) { if (mspListener != null && !measurement.isEmpty()) {
if (peakMap == null) { if (peakMap == null) {
throw new IllegalStateException("Peal map is not initialized"); throw new IllegalStateException("Peak map is not initialized");
} }
Instant time = DateTimeUtils.now();
MapPoint.Builder point = new MapPoint.Builder();
point.putValue("timestamp", time);
measurement.entrySet().stream().forEach((entry) -> {
double val = entry.getValue();
point.putValue(peakMap.get(entry.getKey()), val);
});
if (isFilamentOn()) { if (isFilamentOn()) {
Instant time = DateTimeUtils.now();
MapPoint.Builder point = new MapPoint.Builder();
point.putValue("timestamp", time);
measurement.entrySet().forEach((entry) -> {
double val = entry.getValue();
point.putValue(peakMap.get(entry.getKey()), val);
});
mspListener.acceptScan(measurement); mspListener.acceptScan(measurement);
forEachConnection(Roles.STORAGE_ROLE, StorageConnection.class, (StorageConnection connection) -> { if (getState("storing").booleanValue()) {
PointLoader pl = loaderMap.computeIfAbsent(connection, con -> makeLoader(con)); forEachConnection(Roles.STORAGE_ROLE, StorageConnection.class, (StorageConnection connection) -> {
try { PointLoader pl = loaderMap.computeIfAbsent(connection, this::makeLoader);
pl.push(point.build()); try {
} catch (StorageException ex) { pl.push(point.build());
getLogger().error("Push to loader failed", ex); } catch (StorageException ex) {
} getLogger().error("Push to loader failed", ex);
}); }
});
}
} }
measurement.clear(); measurement.clear();

View File

@ -15,115 +15,95 @@
*/ */
package inr.numass.control.msp.fx; package inr.numass.control.msp.fx;
import ch.qos.logback.classic.Level; import hep.dataforge.control.devices.DeviceFactory;
import hep.dataforge.context.Global; import inr.numass.control.DeviceViewConnection;
import hep.dataforge.control.connections.Roles; import inr.numass.control.NumassControlApplication;
import hep.dataforge.exceptions.ControlException;
import hep.dataforge.io.MetaFileReader;
import hep.dataforge.io.XMLMetaReader;
import hep.dataforge.meta.Meta;
import hep.dataforge.storage.commons.StorageManager;
import inr.numass.control.msp.MspDevice; import inr.numass.control.msp.MspDevice;
import inr.numass.control.msp.MspDeviceFactory; import inr.numass.control.msp.MspDeviceFactory;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage; import javafx.stage.Stage;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.Locale;
import static inr.numass.control.msp.MspDevice.MSP_DEVICE_TYPE;
/** /**
* @author darksnake * @author darksnake
*/ */
public class MspApp extends Application { public class MspApp extends NumassControlApplication<MspDevice> {
public static final String DEFAULT_CONFIG_LOCATION = "msp-config.xml";
private MspDevice device; @Override
protected DeviceViewConnection<MspDevice> buildView() {
return MspViewController.build();
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
} }
@Override @Override
public void start(Stage primaryStage) throws Exception { protected DeviceFactory<MspDevice> getDeviceFactory() {
Locale.setDefault(Locale.US);// чтобы отделение десятичных знаков было точкой return new MspDeviceFactory();
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);
new StorageManager().startGlobal();
String configFileName = getParameters().getNamed().get("config");
if (configFileName == null) {
configFileName = DEFAULT_CONFIG_LOCATION;
}
File configFile = new File(configFileName);
Meta config;
if (configFile.exists()) {
config = MetaFileReader.read(configFile).build();
} else {
config = new XMLMetaReader().read(getClass().getResourceAsStream("/config/msp-config.xml"));
}
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/MspView.fxml"));
Parent parent = loader.load();
MspViewController controller = loader.getController();
Scene scene = new Scene(parent, 800, 600);
primaryStage.setTitle("Numass mass-spectrometer view");
primaryStage.setScene(scene);
primaryStage.setMinHeight(400);
primaryStage.setMinWidth(600);
Platform.runLater(()->{
try {
device = new MspDeviceFactory().build(Global.instance(), getMspConfig(config));
device.init();
device.connect(controller, Roles.VIEW_ROLE);
} catch (ControlException e) {
throw new RuntimeException("Failed to build device", e);
}
});
primaryStage.show();
} }
private Meta getMspConfig(Meta config) {
Meta mspConfig = null;
if (config.hasMeta("device")) {
for (Meta d : config.getMetaList("device")) {
if (d.getString("type", "unknown").equals(MSP_DEVICE_TYPE)) {
mspConfig = d;
}
}
} else if (config.hasMeta("peakJump")) {
mspConfig = config;
}
return mspConfig;
}
// showError(String.format("Can't connect to %s:%d. The port is either busy or not the MKS mass-spectrometer port",
// device.meta().getString("connection.ip", "127.0.0.1"),
// device.meta().getInt("connection.port", 10014)));
// throw new RuntimeException("Can't connect to device");
@Override @Override
public void stop() throws Exception { protected void setupStage(Stage stage) {
super.stop(); stage.setTitle("Numass mass-spectrometer view");
if (device != null) { stage.setMinHeight(400);
device.shutdown(); stage.setMinWidth(600);
}
} }
// private Device device;
//
//
// /**
// * @param args the command line arguments
// */
// public static void main(String[] args) {
// launch(args);
// }
//
// @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);
//
// FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/MspView.fxml"));
//
// Parent parent = loader.load();
// MspViewController controller = loader.getController();
//
// Scene scene = new Scene(parent, 800, 600);
//
// primaryStage.setTitle("Numass mass-spectrometer view");
// primaryStage.setScene(scene);
// primaryStage.setMinHeight(400);
// primaryStage.setMinWidth(600);
//
// primaryStage.show();
//
// setupDevice(controller);
// }
//
// private void setupDevice(MspViewController controller){
// Meta config = NumassControlUtils.getConfig(this)
// .orElseGet(() -> NumassControlUtils.readResourceMeta("/config/msp-config.xml"));
//
// Context ctx = NumassControlUtils.setupContext(config);
// Meta mspConfig = NumassControlUtils.findDeviceMeta(config,it-> Objects.equals(it.getString("name"), "msp"))
// .orElseThrow(()-> new RuntimeException("Msp configuration not found"));
//
//
// Platform.runLater(() -> {
// try {
// device = new MspDeviceFactory().build(ctx, mspConfig);
// device.init();
// device.connect(controller, Roles.VIEW_ROLE);
// NumassControlUtils.connectStorage(device,config);
// } catch (ControlException e) {
// throw new RuntimeException("Failed to build device", e);
// }
// });
// }
//
// @Override
// public void stop() throws Exception {
// super.stop();
// if (device != null) {
// device.shutdown();
// }
// }
} }

View File

@ -15,28 +15,21 @@
*/ */
package inr.numass.control.msp.fx; package inr.numass.control.msp.fx;
import hep.dataforge.control.connections.DeviceConnection;
import hep.dataforge.control.connections.Roles;
import hep.dataforge.control.connections.StorageConnection;
import hep.dataforge.control.devices.Device; import hep.dataforge.control.devices.Device;
import hep.dataforge.control.devices.DeviceListener; import hep.dataforge.control.devices.DeviceListener;
import hep.dataforge.exceptions.ControlException; import hep.dataforge.exceptions.ControlException;
import hep.dataforge.exceptions.PortException; import hep.dataforge.exceptions.PortException;
import hep.dataforge.exceptions.StorageException;
import hep.dataforge.fx.fragments.FragmentWindow; import hep.dataforge.fx.fragments.FragmentWindow;
import hep.dataforge.fx.fragments.LogFragment; import hep.dataforge.fx.fragments.LogFragment;
import hep.dataforge.meta.ConfigChangeListener; import hep.dataforge.meta.ConfigChangeListener;
import hep.dataforge.meta.Configuration;
import hep.dataforge.meta.Meta; import hep.dataforge.meta.Meta;
import hep.dataforge.meta.MetaBuilder; import hep.dataforge.meta.MetaBuilder;
import hep.dataforge.plots.data.PlottableGroup; import hep.dataforge.plots.data.PlottableGroup;
import hep.dataforge.plots.data.TimePlottable; import hep.dataforge.plots.data.TimePlottable;
import hep.dataforge.plots.fx.PlotContainer; import hep.dataforge.plots.fx.PlotContainer;
import hep.dataforge.plots.jfreechart.JFreeChartFrame; import hep.dataforge.plots.jfreechart.JFreeChartFrame;
import hep.dataforge.storage.api.Storage;
import hep.dataforge.storage.commons.StorageManager;
import hep.dataforge.values.Value; import hep.dataforge.values.Value;
import inr.numass.client.NumassClient; import inr.numass.control.DeviceViewConnection;
import inr.numass.control.msp.MspDevice; import inr.numass.control.msp.MspDevice;
import inr.numass.control.msp.MspListener; import inr.numass.control.msp.MspListener;
import javafx.application.Platform; import javafx.application.Platform;
@ -44,21 +37,19 @@ import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
import javafx.scene.control.ComboBox; import javafx.scene.control.ComboBox;
import javafx.scene.control.Slider;
import javafx.scene.control.ToggleButton; import javafx.scene.control.ToggleButton;
import javafx.scene.input.DragEvent; import javafx.scene.layout.BorderPane;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Paint; import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle; import javafx.scene.shape.Circle;
import javafx.stage.DirectoryChooser;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import org.controlsfx.control.ToggleSwitch; import org.controlsfx.control.ToggleSwitch;
import org.slf4j.LoggerFactory;
import java.io.File; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -69,24 +60,23 @@ import java.util.ResourceBundle;
* *
* @author darksnake * @author darksnake
*/ */
public class MspViewController extends DeviceConnection<MspDevice> implements DeviceListener, Initializable, MspListener { public class MspViewController extends DeviceViewConnection<MspDevice> implements DeviceListener, Initializable, MspListener {
public static MspViewController build() {
try {
FXMLLoader loader = new FXMLLoader(MspViewController.class.getResource("/fxml/MspView.fxml"));
loader.load();
return loader.getController();
} catch (IOException e) {
throw new Error(e);
}
}
private final PlottableGroup<TimePlottable> plottables = new PlottableGroup<>(); private final PlottableGroup<TimePlottable> plottables = new PlottableGroup<>();
private Configuration viewConfig; // private Configuration viewConfig;
private JFreeChartFrame plot; private JFreeChartFrame plot;
private LogFragment logArea; private LogFragment logArea;
private StorageConnection connection;
@FXML
private Slider autoRangeSlider;
@FXML
private ToggleSwitch fillamentButton;
@FXML
private Circle fillamentIndicator;
@FXML
private ToggleButton plotButton;
@FXML
private AnchorPane plotPane;
private final ConfigChangeListener viewConfigObserver = new ConfigChangeListener() { private final ConfigChangeListener viewConfigObserver = new ConfigChangeListener() {
@Override @Override
@ -100,6 +90,19 @@ public class MspViewController extends DeviceConnection<MspDevice> implements De
} }
}; };
@FXML
private BorderPane root;
@FXML
private ToggleSwitch fillamentButton;
@FXML
private Circle fillamentIndicator;
@FXML
private ToggleButton plotButton;
@FXML
private BorderPane plotPane;
@FXML
public ToggleButton connectButton;
@FXML @FXML
private ToggleButton consoleButton; private ToggleButton consoleButton;
@FXML @FXML
@ -139,28 +142,25 @@ public class MspViewController extends DeviceConnection<MspDevice> implements De
getDevice().getLogger().error("Failed to toggle filaments"); getDevice().getLogger().error("Failed to toggle filaments");
} }
}); });
} }
public Configuration getViewConfig() {
if (viewConfig == null) {
viewConfig = new Configuration(getDevice().meta().getMeta("peakJump")); public Meta getViewConfig() {
viewConfig.addObserver(viewConfigObserver); return getDevice().meta().getMeta("plot",getDevice().getMeta());
LoggerFactory.getLogger(getClass()).warn("Could not find view configuration. Using default view configuration instead.");
}
return viewConfig;
} }
public void setViewConfig(Meta viewConfig) {
this.viewConfig = new Configuration(viewConfig);
this.viewConfig.addObserver(viewConfigObserver);
}
@Override @Override
public void open(MspDevice device) throws Exception { public void open(MspDevice device) throws Exception {
super.open(device); super.open(device);
getDevice().setMspListener(this); getDevice().setMspListener(this);
device.getConfig().optMeta("plot").ifPresent(this::setViewConfig);
updatePlot(); updatePlot();
//FIXME
getStateBinding("connected").addListener((observable, oldValue, newValue) -> connectButton.setSelected(newValue.booleanValue()));
bindStateTo("connected", connectButton.selectedProperty());
} }
// public void setDeviceConfig(Context context, File cfgFile) { // public void setDeviceConfig(Context context, File cfgFile) {
@ -172,7 +172,7 @@ public class MspViewController extends DeviceConnection<MspDevice> implements De
// } // }
// } // }
public void initPlot() { private void initPlot() {
Meta plotConfig = new MetaBuilder("plotFrame") Meta plotConfig = new MetaBuilder("plotFrame")
.setNode(new MetaBuilder("yAxis") .setNode(new MetaBuilder("yAxis")
.setValue("type", "log") .setValue("type", "log")
@ -182,9 +182,9 @@ public class MspViewController extends DeviceConnection<MspDevice> implements De
.setValue("xAxis.type", "time"); .setValue("xAxis.type", "time");
this.plot = new JFreeChartFrame(plotConfig); this.plot = new JFreeChartFrame(plotConfig);
PlotContainer container = PlotContainer.anchorTo(plotPane); PlotContainer container = PlotContainer.centerIn(plotPane);
container.setPlot(plot); container.setPlot(plot);
updatePlot(); // updatePlot();
// this.plot = DynamicPlot.attachToFX(plotPane, new AnnotationBuilder("plot-config").putValue("logY", true).build()); // this.plot = DynamicPlot.attachToFX(plotPane, new AnnotationBuilder("plot-config").putValue("logY", true).build());
// plot.setAutoRange(30 * 60); // plot.setAutoRange(30 * 60);
} }
@ -197,8 +197,8 @@ public class MspViewController extends DeviceConnection<MspDevice> implements De
if (config.hasMeta("plotFrame")) { if (config.hasMeta("plotFrame")) {
this.plot.configure(config.getMeta("plotFrame")); this.plot.configure(config.getMeta("plotFrame"));
} }
if (config.hasMeta("peakJump.line")) { if (config.hasMeta("peakJump.peak")) {
for (Meta an : config.getMetaList("peakJump.line")) { for (Meta an : config.getMetaList("peakJump.peak")) {
String mass = an.getString("mass"); String mass = an.getString("mass");
if (!this.plottables.has(mass)) { if (!this.plottables.has(mass)) {
@ -254,11 +254,6 @@ public class MspViewController extends DeviceConnection<MspDevice> implements De
} }
@FXML
private void onAutoRangeChange(DragEvent event) {
plottables.setValue(TimePlottable.MAX_AGE_KEY, this.autoRangeSlider.getValue() * 60000);
}
@FXML @FXML
private void onPlotToggle(ActionEvent event) throws ControlException { private void onPlotToggle(ActionEvent event) throws ControlException {
if (plotButton.isSelected()) { if (plotButton.isSelected()) {
@ -302,50 +297,51 @@ public class MspViewController extends DeviceConnection<MspDevice> implements De
@FXML @FXML
private void onStoreButtonClick(ActionEvent event) { private void onStoreButtonClick(ActionEvent event) {
if (storeButton.isSelected()) { getDevice().setState("storing", storeButton.isSelected());
// if (storeButton.isSelected()) {
if (!getDevice().meta().hasMeta("storage")) { //
getDevice().getLogger().info("Storage not defined. Starting storage selection dialog"); // if (!getDevice().meta().hasMeta("storage")) {
DirectoryChooser chooser = new DirectoryChooser(); // getDevice().getLogger().info("Storage not defined. Starting storage selection dialog");
File storageDir = chooser.showDialog(this.plotPane.getScene().getWindow()); // DirectoryChooser chooser = new DirectoryChooser();
if (storageDir == null) { // File storageDir = chooser.showDialog(this.plotPane.getScene().getWindow());
storeButton.setSelected(false); // if (storageDir == null) {
throw new RuntimeException("User canceled directory selection"); // storeButton.setSelected(false);
} // throw new RuntimeException("User canceled directory selection");
getDevice().getConfig().putNode(new MetaBuilder("storage") // }
.putValue("path", storageDir.getAbsolutePath())); // getDevice().getConfig().putNode(new MetaBuilder("storage")
} // .putValue("path", storageDir.getAbsolutePath()));
Meta storageConfig = getDevice().meta().getMeta("storage"); // }
Storage localStorage = StorageManager.buildFrom(getDevice().getContext()) // Meta storageConfig = getDevice().meta().getMeta("storage");
.buildStorage(storageConfig); // Storage localStorage = StorageManager.buildFrom(getDevice().getContext())
// .buildStorage(storageConfig);
String runName = getDevice().meta().getString("numass.run", ""); //
Meta meta = getDevice().meta(); // String runName = getDevice().meta().getString("numass.run", "");
if (meta.hasMeta("numass")) { // Meta meta = getDevice().meta();
try { // if (meta.hasMeta("numass")) {
getDevice().getLogger().info("Obtaining run information from cetral server..."); // try {
NumassClient client = new NumassClient(meta.getString("numass.ip", "192.168.111.1"), // getDevice().getLogger().info("Obtaining run information from cetral server...");
meta.getInt("numass.port", 8335)); // NumassClient client = new NumassClient(meta.getString("numass.ip", "192.168.111.1"),
runName = client.getCurrentRun().getString("path", ""); // meta.getInt("numass.port", 8335));
getDevice().getLogger().info("Run name is '{}'", runName); // runName = client.getCurrentRun().getString("path", "");
} catch (Exception ex) { // getDevice().getLogger().info("Run name is '{}'", runName);
getDevice().getLogger().warn("Failed to download current run information", ex); // } catch (Exception ex) {
} // getDevice().getLogger().warn("Failed to download current run information", ex);
} // }
// }
if (!runName.isEmpty()) { //
try { // if (!runName.isEmpty()) {
localStorage = localStorage.buildShelf(runName, null); // try {
} catch (StorageException ex) { // localStorage = localStorage.buildShelf(runName, null);
getDevice().getLogger().error("Failed to create storage shelf. Using root storage instead"); // } catch (StorageException ex) {
} // getDevice().getLogger().error("Failed to create storage shelf. Using root storage instead");
} // }
// }
connection = new StorageConnection(localStorage); //
getDevice().connect(connection, Roles.STORAGE_ROLE); // connection = new StorageConnection(localStorage);
} else if (connection != null) { // getDevice().connect(connection, Roles.STORAGE_ROLE);
getDevice().disconnect(connection); // } else if (connection != null) {
} // getDevice().disconnect(connection);
// }
} }
@Override @Override
@ -362,4 +358,9 @@ public class MspViewController extends DeviceConnection<MspDevice> implements De
public void evaluateDeviceException(Device device, String message, Throwable exception) { public void evaluateDeviceException(Device device, String message, Throwable exception) {
} }
@Override
public Node getFXNode() {
return root;
}
} }

View File

@ -32,10 +32,10 @@ limitations under the License.
</peakJump> </peakJump>
<plot> <plot>
<peakJump> <peakJump>
<line mass="2" title="hydrogen" color="black" thickness="6"/> <peak mass="2" title="hydrogen" color="black" thickness="6"/>
<line mass="6" title="tritium" thickness="4"/> <peak mass="6" title="tritium" thickness="4"/>
<line mass="18" title="water" /> <peak mass="18" title="water" />
<line mass="32" title="oxygen"/> <peak mass="32" title="oxygen"/>
</peakJump> </peakJump>
</plot> </plot>
</device> </device>

View File

@ -17,40 +17,32 @@ limitations under the License.
--> -->
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.ComboBox?> <?import javafx.scene.control.*?>
<?import javafx.scene.control.Label?> <?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.control.ToggleButton?>
<?import javafx.scene.control.ToolBar?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?> <?import javafx.scene.layout.Pane?>
<?import javafx.scene.shape.Circle?> <?import javafx.scene.shape.Circle?>
<?import org.controlsfx.control.ToggleSwitch?> <?import org.controlsfx.control.ToggleSwitch?>
<BorderPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="400.0" minWidth="600.0" prefHeight="480.0" prefWidth="640.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="inr.numass.control.msp.fx.MspViewController">
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="400.0" minWidth="600.0" prefHeight="480.0" prefWidth="640.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="inr.numass.control.msp.fx.MspViewController"> <top>
<children> <ToolBar prefHeight="50.0" prefWidth="200.0">
<ToolBar prefHeight="50.0" prefWidth="200.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <ToggleButton fx:id="connectButton" mnemonicParsing="false" text="Connect" />
<items> <ComboBox fx:id="fillamentSelector" promptText="Fillament 1" visibleRowCount="2" />
<ComboBox fx:id="fillamentSelector" promptText="Fillament 1" visibleRowCount="2" /> <ToggleSwitch fx:id="fillamentButton" prefHeight="40.0" prefWidth="35.0">
<ToggleSwitch fx:id="fillamentButton" prefHeight="40.0" prefWidth="35.0"> <padding>
<padding> <Insets top="11.0" />
<Insets top="11.0" /> </padding>
</padding> </ToggleSwitch>
</ToggleSwitch> <Circle fx:id="fillamentIndicator" fill="GRAY" radius="10.0" stroke="BLACK" strokeType="INSIDE" />
<Circle fx:id="fillamentIndicator" fill="GRAY" radius="10.0" stroke="BLACK" strokeType="INSIDE" /> <Separator orientation="VERTICAL" prefHeight="20.0" />
<Separator orientation="VERTICAL" prefHeight="20.0" /> <ToggleButton fx:id="plotButton" mnemonicParsing="false" onAction="#onPlotToggle" text="Measure" />
<ToggleButton fx:id="plotButton" mnemonicParsing="false" onAction="#onPlotToggle" text="Measure" /> <ToggleButton fx:id="storeButton" mnemonicParsing="false" onAction="#onStoreButtonClick" text="Store" />
<ToggleButton fx:id="storeButton" mnemonicParsing="false" onAction="#onStoreButtonClick" text="Store" /> <Separator orientation="VERTICAL" prefHeight="20.0" />
<Separator orientation="VERTICAL" prefHeight="20.0" /> <Pane HBox.hgrow="ALWAYS" />
<Label text="Autorange (min):" /> <ToggleButton fx:id="consoleButton" mnemonicParsing="false" text="Console" />
<Slider fx:id="autoRangeSlider" max="210.0" min="10.0" onDragDone="#onAutoRangeChange" showTickLabels="true" showTickMarks="true" snapToTicks="true" value="30.0" />
<Separator orientation="VERTICAL" prefHeight="20.0" />
<Pane HBox.hgrow="ALWAYS" />
<ToggleButton fx:id="consoleButton" mnemonicParsing="false" text="Console" />
</items>
</ToolBar> </ToolBar>
<AnchorPane fx:id="plotPane" minHeight="400.0" minWidth="600.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="50.0" /> </top>
</children> <center>
</AnchorPane> <BorderPane fx:id="plotPane" minHeight="400.0" minWidth="600.0" prefHeight="200.0" prefWidth="200.0" />
</center>
</BorderPane>

View File

@ -0,0 +1,65 @@
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.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
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
*/
protected ObjectBinding<Value> getStateBinding(String state) {
return bindings.computeIfAbsent(state, stateName ->
new ObjectBinding<Value>() {
@Override
protected Value computeValue() {
return getDevice().getState(stateName);
}
}
);
}
protected BooleanBinding getStateBooleanBinding(String state) {
ObjectBinding<Value> b = getStateBinding(state);
return Bindings.createBooleanBinding(() -> b.get().booleanValue(), b);
}
/**
* Bind writable state change to given observable value
*
* @param state
* @param observable
*/
protected void bindStateTo(String state, ObservableValue<?> observable) {
observable.addListener((ChangeListener<Object>) (observable1, oldValue, newValue) -> {
if (oldValue != newValue) {
getDevice().setState(state, newValue);
}
});
}
@Override
public void notifyDeviceStateChanged(Device device, String name, Value state) {
if (bindings.containsKey(name)) {
bindings.get(name).invalidate();
}
}
}

View File

@ -1,40 +0,0 @@
package inr.numass.control;
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.meta.Meta;
import hep.dataforge.storage.api.Storage;
import hep.dataforge.storage.commons.StorageFactory;
import inr.numass.client.ClientUtils;
/**
* Created by darksnake on 08-May-17.
*/
public class NumassConnections {
/**
* 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")) {
String numassRun = ClientUtils.getRunName(config);
config.getMetaList("storage").forEach(node -> {
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);
});
}
}
}

View File

@ -0,0 +1,93 @@
package inr.numass.control;
import ch.qos.logback.classic.Level;
import hep.dataforge.context.Context;
import hep.dataforge.control.connections.DeviceConnection;
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.scene.layout.BorderPane;
import javafx.stage.Stage;
import org.slf4j.LoggerFactory;
import java.util.Locale;
import java.util.Objects;
/**
* 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);
DeviceViewConnection<D> controller = buildView();
BorderPane pane = new BorderPane();
pane.setCenter(controller.getFXNode());
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
setupDevice(controller);
}
/**
* Build a view connection
*
* @return
*/
protected abstract DeviceViewConnection<D> buildView();
/**
* Get a device factory for given device
*
* @return
*/
protected abstract DeviceFactory<D> getDeviceFactory();
protected abstract void setupStage(Stage stage);
private void setupDevice(DeviceConnection<D> controller) {
Meta config = NumassControlUtils.getConfig(this)
.orElseGet(() -> NumassControlUtils.readResourceMeta("/config/msp-config.xml"));
Context ctx = NumassControlUtils.setupContext(config);
Meta mspConfig = NumassControlUtils.findDeviceMeta(config, it -> Objects.equals(it.getString("name"), "msp"))
.orElseThrow(() -> new RuntimeException("Msp configuration not found"));
Platform.runLater(() -> {
try {
device = getDeviceFactory().build(ctx, mspConfig);
device.init();
device.connect(controller, Roles.VIEW_ROLE, Roles.DEVICE_LISTENER_ROLE);
NumassControlUtils.connectStorage(device, config);
} 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();
}
}
}

View File

@ -0,0 +1,92 @@
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 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("storge")) {
String numassRun = ClientUtils.getRunName(config);
config.getMetaList("storage").forEach(node -> {
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);
});
}
}
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 configFileName = app.getParameters().getNamed().get("config");
if (configFileName == null) {
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 {
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;
}
}