diff --git a/numass-control/build.gradle b/numass-control/build.gradle index 0e32a0ff..6dc67921 100644 --- a/numass-control/build.gradle +++ b/numass-control/build.gradle @@ -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 { compile project(':numass-client') compile "hep.dataforge:plots-jfc" // project(':dataforge-plots:plots-jfc') compile "hep.dataforge:dataforge-control" //project(':dataforge-control') + compile "hep.dataforge:kodex" - // https://mvnrepository.com/artifact/org.controlsfx/controlsfx - compile group: 'org.controlsfx', name: 'controlsfx', version: '8.40.12' - + //graphics + compile 'org.controlsfx:controlsfx:8.40.12' + compile "no.tornado:tornadofx:1.7.4" } task installAll(type: Copy) { diff --git a/numass-control/control-room/build.gradle b/numass-control/control-room/build.gradle index 7b02c750..7a1e8326 100644 --- a/numass-control/control-room/build.gradle +++ b/numass-control/control-room/build.gradle @@ -1,9 +1,9 @@ plugins{ - id "org.jetbrains.kotlin.jvm" version '1.1.2-2' id "application" id 'com.github.johnrengelman.shadow' version '2.0.0' } + if (!hasProperty('mainClass')) { ext.mainClass = 'inr.numass.control.ServerApp'//"inr.numass.viewer.test.TestApp" } @@ -21,11 +21,6 @@ configurations{ } 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 compile project(':numass-control') compile project(':numass-server') diff --git a/numass-control/control-room/src/main/kotlin/inr/numass/control/BoardView.kt b/numass-control/control-room/src/main/kotlin/inr/numass/control/BoardView.kt index 61888fca..70e58f7b 100644 --- a/numass-control/control-room/src/main/kotlin/inr/numass/control/BoardView.kt +++ b/numass-control/control-room/src/main/kotlin/inr/numass/control/BoardView.kt @@ -1,12 +1,6 @@ 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 inr.numass.control.NumassControlUtils.getDFIcon import javafx.geometry.Orientation import javafx.geometry.Pos import javafx.scene.control.Hyperlink @@ -17,7 +11,7 @@ import tornadofx.* /** * 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(); override val root = borderpane { @@ -87,23 +81,11 @@ class BoardView : View("Numass control board", ImageView(getDFIcon())) { vbox { prefHeight = 40.0 bindChildren(controller.devices) { connection -> - titledpane(title = "Device: " + connection.device.name, collapsible = true) { - hbox { - alignment = Pos.CENTER_LEFT - vgrow = Priority.ALWAYS; - 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) - } - } - } + titledpane( + title = "Device: " + connection.device.name, + collapsible = true, + node = connection.getBoardView() + ) } } } diff --git a/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8App.java b/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8App.java index b41a4593..b14af643 100644 --- a/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8App.java +++ b/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8App.java @@ -29,7 +29,7 @@ import java.util.Objects; public class PKT8App extends NumassControlApplication { @Override protected DeviceViewConnection buildView(PKT8Device device) { - return PKT8View.build(device.getContext()); + return PKT8ViewConnection.build(device.getContext()); } @Override diff --git a/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8DeviceFactory.java b/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8DeviceFactory.java index d9330cc6..f01b63c0 100644 --- a/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8DeviceFactory.java +++ b/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8DeviceFactory.java @@ -22,6 +22,6 @@ public class PKT8DeviceFactory implements DeviceViewFactory { @Override public DeviceViewConnection buildView(Device device) { - return PKT8View.build(device.getContext()); + return PKT8ViewConnection.build(device.getContext()); } } diff --git a/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8PlotView.java b/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8PlotView.java index a7a5d863..37d652e2 100644 --- a/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8PlotView.java +++ b/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8PlotView.java @@ -125,9 +125,9 @@ public class PKT8PlotView extends DeviceViewConnection implements In PKT8Result res = PKT8Result.class.cast(result); //PENDING replace by connection? if (rawDataButton.isSelected()) { - plottables.put(res.channel, res.rawValue); + plottables.put(res.getChannel(), res.getRawValue()); } else { - plottables.put(res.channel, res.temperature); + plottables.put(res.getChannel(), res.getTemperature()); } } diff --git a/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8View.java b/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8View.java index 1ac8b140..d94cbabb 100644 --- a/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8View.java +++ b/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8View.java @@ -33,7 +33,7 @@ import java.util.ResourceBundle; */ public class PKT8View extends DeviceViewConnection implements Initializable, MeasurementListener { - public static PKT8View build(Context context) { + public static PKT8ViewConnection build(Context context) { try { FXMLLoader loader = new FXMLLoader(context.getClassLoader().getResource("fxml/PKT8Indicator.fxml")); loader.setClassLoader(context.getClassLoader()); @@ -104,9 +104,9 @@ public class PKT8View extends DeviceViewConnection implements Initia PKT8Result res = PKT8Result.class.cast(result); Platform.runLater(() -> { lastUpdateLabel.setText(time.toString()); - table.getItems().removeIf(it -> it.channel.equals(res.channel)); + table.getItems().removeIf(it -> it.getChannel().equals(res.getChannel())); table.getItems().add(res); - table.getItems().sort(Comparator.comparing(o -> o.channel)); + table.getItems().sort(Comparator.comparing(PKT8Result::getChannel)); }); } diff --git a/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8Result.java b/numass-control/cryotemp/src/main/kotlin/inr/numass/control/cryotemp/PKT8Result.kt similarity index 50% rename from numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8Result.java rename to numass-control/cryotemp/src/main/kotlin/inr/numass/control/cryotemp/PKT8Result.kt index fc1309b2..1fa427d5 100644 --- a/numass-control/cryotemp/src/main/java/inr/numass/control/cryotemp/PKT8Result.java +++ b/numass-control/cryotemp/src/main/kotlin/inr/numass/control/cryotemp/PKT8Result.kt @@ -14,32 +14,14 @@ * limitations under the License. */ -package inr.numass.control.cryotemp; +package inr.numass.control.cryotemp /** * 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; - public double rawValue; - public double temperature; + val rawString: String = String.format("%.2f", rawValue) - public PKT8Result(String channel, double rawValue, double 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); - } + val temperatureString: String = String.format("%.2f", temperature) } diff --git a/numass-control/cryotemp/src/main/kotlin/inr/numass/control/cryotemp/PKT8ViewConnection.kt b/numass-control/cryotemp/src/main/kotlin/inr/numass/control/cryotemp/PKT8ViewConnection.kt new file mode 100644 index 00000000..ba38a3a5 --- /dev/null +++ b/numass-control/cryotemp/src/main/kotlin/inr/numass/control/cryotemp/PKT8ViewConnection.kt @@ -0,0 +1,182 @@ +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.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.property.SimpleObjectProperty +import javafx.collections.FXCollections +import javafx.collections.ListChangeListener +import javafx.collections.transformation.SortedList +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(), MeasurementListener { + private val view by lazy { CryoView() } + internal val table = SortedList(FXCollections.observableArrayList()) { r1, r2 -> + r1.channel.compareTo(r2.channel) + } + + val lastUpdateProperty = SimpleObjectProperty("NEVER") + + + override fun getBoardView(): Parent { + return VBox().apply { + this += super.getBoardView() + } + } + + override fun getFXNode(): Node { + return view.root; + } + + override fun onMeasurementFailed(measurement: Measurement<*>, exception: Throwable) { + throw exception; + } + + override fun onMeasurementResult(measurement: Measurement<*>, result: Any, time: Instant) { + if (result is PKT8Result) { + Platform.runLater { + lastUpdateProperty.set(time.toString()) + val item = table.find { it.channel == result.channel }; + if (item == null) { + table.add(result); + } else { + table[table.indexOf(item)] = result + } + } + } + } + + inner class CryoView() : View() { + override val root = borderpane { + top { + toolbar { + togglebutton("Measure") { + bindBooleanToState(Sensor.MEASURING_STATE, selectedProperty()) + } + togglebutton("Store") { + bindBooleanToState("storing", selectedProperty()) + } + separator(Orientation.VERTICAL) + pane { + hgrow = Priority.ALWAYS + } + separator(Orientation.VERTICAL) + togglebutton("Plot") { + FragmentWindow(CryoPlotView().root).bindTo(this) + } + togglebutton("Log") { + FragmentWindow(LogFragment().apply { + addLogHandler(device.logger) + }).bindTo(this) + } + } + } + center { + tableview(table) { + column("Sensor", PKT8Result::channel); + column("Resistance", PKT8Result::rawValue).cellFormat { + text = String.format("%.2f", it) + } + column("Resistance", PKT8Result::temperature).cellFormat { + text = String.format("%.2f", it) + } + } + } + bottom { + toolbar { + label("Last update: ") + label(lastUpdateProperty) { + font = Font.font("System Bold") + } + } + } + } + } + + inner class CryoPlotView : View() { + 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"))) + table.addListener(ListChangeListener { change -> + while (change.next()) { + change.addedSubList.forEach { + if (rawDataButton.isSelected()) { + plottables.put(it.channel, it.rawValue) + } else { + plottables.put(it.channel, it.temperature) + } + } + } + }) + } + } + + override val root: Parent = borderpane { + PlotContainer.centerIn(this).plot = plotFrame + bottom { + rawDataButton = togglebutton("Raw data") { + action { + plottables.forEach { + it.clear() + } + } + } + } + } + + 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) + } + } + } +} + diff --git a/numass-control/cryotemp/src/main/resources/fxml/PKT8Indicator.fxml b/numass-control/cryotemp/src/main/resources/fxml/PKT8Indicator.fxml index 03f91350..a6f87fe7 100644 --- a/numass-control/cryotemp/src/main/resources/fxml/PKT8Indicator.fxml +++ b/numass-control/cryotemp/src/main/resources/fxml/PKT8Indicator.fxml @@ -4,7 +4,7 @@ + xmlns="http://javafx.com/javafx/8.0.111" fx:controller="inr.numass.control.cryotemp.PKT8ViewConnection">
@@ -31,14 +31,12 @@ - - + diff --git a/numass-control/cryotemp/src/main/resources/fxml/PKT8Plot.fxml b/numass-control/cryotemp/src/main/resources/fxml/PKT8Plot.fxml index cba1b0d6..61b541a1 100644 --- a/numass-control/cryotemp/src/main/resources/fxml/PKT8Plot.fxml +++ b/numass-control/cryotemp/src/main/resources/fxml/PKT8Plot.fxml @@ -27,12 +27,10 @@ limitations under the License.
- - - - - - + + + +
diff --git a/numass-control/magnet/src/main/java/inr/numass/control/magnet/fx/MagnetControllerApp.java b/numass-control/magnet/src/main/java/inr/numass/control/magnet/fx/MagnetControllerApp.java index 2ecb0a4c..227141e6 100644 --- a/numass-control/magnet/src/main/java/inr/numass/control/magnet/fx/MagnetControllerApp.java +++ b/numass-control/magnet/src/main/java/inr/numass/control/magnet/fx/MagnetControllerApp.java @@ -49,7 +49,7 @@ public class MagnetControllerApp extends Application { List controllers = new ArrayList<>(); @Override - public void start(Stage primaryStage) throws IOException, ControlException { + public void start(Stage stage) throws IOException, ControlException { 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); @@ -108,10 +108,10 @@ public class MagnetControllerApp extends Application { Scene scene = new Scene(vbox, width, height); - primaryStage.setTitle("Numass magnet view"); - primaryStage.setScene(scene); - primaryStage.setResizable(false); - primaryStage.show(); + stage.setTitle("Numass magnet view"); + stage.setScene(scene); + stage.setResizable(false); + stage.show(); } @Override diff --git a/numass-control/src/main/java/inr/numass/control/DeviceFragment.java b/numass-control/src/main/java/inr/numass/control/DeviceFragment.java deleted file mode 100644 index 16c7d86e..00000000 --- a/numass-control/src/main/java/inr/numass/control/DeviceFragment.java +++ /dev/null @@ -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 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 - } -} diff --git a/numass-control/src/main/java/inr/numass/control/DeviceViewConnection.java b/numass-control/src/main/java/inr/numass/control/DeviceViewConnection.java deleted file mode 100644 index f78817b0..00000000 --- a/numass-control/src/main/java/inr/numass/control/DeviceViewConnection.java +++ /dev/null @@ -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 extends DeviceConnection implements DeviceListener, FXObject { - private Map> bindings = new HashMap<>(); - - /** - * Get binding for a given device state - * - * @param state - * @return - */ - public ObjectBinding getStateBinding(String state) { - return bindings.computeIfAbsent(state, stateName -> - new ObjectBinding() { - @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 getBoardView(); -} diff --git a/numass-control/src/main/java/inr/numass/control/DeviceViewFactory.java b/numass-control/src/main/java/inr/numass/control/DeviceViewFactory.java deleted file mode 100644 index 9f218f83..00000000 --- a/numass-control/src/main/java/inr/numass/control/DeviceViewFactory.java +++ /dev/null @@ -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); -} diff --git a/numass-control/src/main/java/inr/numass/control/NumassControlApplication.java b/numass-control/src/main/java/inr/numass/control/NumassControlApplication.java deleted file mode 100644 index ba0b319c..00000000 --- a/numass-control/src/main/java/inr/numass/control/NumassControlApplication.java +++ /dev/null @@ -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 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 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 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(); - } - } - - -} diff --git a/numass-control/src/main/java/inr/numass/control/NumassControlUtils.java b/numass-control/src/main/java/inr/numass/control/NumassControlUtils.java deleted file mode 100644 index 0a9ff859..00000000 --- a/numass-control/src/main/java/inr/numass/control/NumassControlUtils.java +++ /dev/null @@ -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 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 findDeviceMeta(Meta config, Predicate 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")); - } - -} diff --git a/numass-control/src/main/java/inr/numass/control/StorageHelper.java b/numass-control/src/main/java/inr/numass/control/StorageHelper.java deleted file mode 100644 index 23fb2be6..00000000 --- a/numass-control/src/main/java/inr/numass/control/StorageHelper.java +++ /dev/null @@ -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 loaderMap = new HashMap<>(); - private final Function loaderFactory; - - public StorageHelper(AbstractDevice device, Function 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); - } - }); - } -} diff --git a/numass-control/control-room/src/main/kotlin/inr/numass/control/ControlFXExtensions.kt b/numass-control/src/main/kotlin/inr/numass/control/ControlFXExtensions.kt similarity index 100% rename from numass-control/control-room/src/main/kotlin/inr/numass/control/ControlFXExtensions.kt rename to numass-control/src/main/kotlin/inr/numass/control/ControlFXExtensions.kt diff --git a/numass-control/src/main/kotlin/inr/numass/control/DeviceViewConnection.kt b/numass-control/src/main/kotlin/inr/numass/control/DeviceViewConnection.kt new file mode 100644 index 00000000..f5a93a3b --- /dev/null +++ b/numass-control/src/main/kotlin/inr/numass/control/DeviceViewConnection.kt @@ -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 : DeviceConnection(), DeviceListener, FXObject { + private val bindings = HashMap>() + + /** + * Get binding for a given device state + + * @param state + * * + * @return + */ + fun getStateBinding(state: String): ObjectBinding { + return bindings.computeIfAbsent(state) { stateName -> + object : ObjectBinding() { + 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) + } + } + } +} diff --git a/numass-control/src/main/kotlin/inr/numass/control/DeviceViewFactory.kt b/numass-control/src/main/kotlin/inr/numass/control/DeviceViewFactory.kt new file mode 100644 index 00000000..e05b48d3 --- /dev/null +++ b/numass-control/src/main/kotlin/inr/numass/control/DeviceViewFactory.kt @@ -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<*> +} diff --git a/numass-control/src/main/kotlin/inr/numass/control/NumassControlApplication.kt b/numass-control/src/main/kotlin/inr/numass/control/NumassControlApplication.kt new file mode 100644 index 00000000..698bd4e5 --- /dev/null +++ b/numass-control/src/main/kotlin/inr/numass/control/NumassControlApplication.kt @@ -0,0 +1,89 @@ +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 : 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 + + /** + * 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 { this.acceptDevice(it) }) + .orElseThrow { RuntimeException("Device configuration not found") } + + + try { + + 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() { + super.stop() + device.shutdown() + device.context.close() + } + + +} diff --git a/numass-control/src/main/kotlin/inr/numass/control/NumassControlUtils.kt b/numass-control/src/main/kotlin/inr/numass/control/NumassControlUtils.kt new file mode 100644 index 00000000..06b35874 --- /dev/null +++ b/numass-control/src/main/kotlin/inr/numass/control/NumassControlUtils.kt @@ -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 { + 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() + } +} + + +fun findDeviceMeta(config: Meta, criterion: Predicate): Optional { + 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) +} + + diff --git a/numass-control/src/main/kotlin/inr/numass/control/StorageHelper.kt b/numass-control/src/main/kotlin/inr/numass/control/StorageHelper.kt new file mode 100644 index 00000000..6493e52d --- /dev/null +++ b/numass-control/src/main/kotlin/inr/numass/control/StorageHelper.kt @@ -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) : AutoCloseable { + private val loaderMap = HashMap() + + 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) + } + } + } +} diff --git a/numass-control/vac/src/main/java/inr/numass/control/readvac/fx/TestVac.java b/numass-control/vac/src/main/java/inr/numass/control/readvac/fx/TestVac.java index 9e8d8006..970ed9fd 100644 --- a/numass-control/vac/src/main/java/inr/numass/control/readvac/fx/TestVac.java +++ b/numass-control/vac/src/main/java/inr/numass/control/readvac/fx/TestVac.java @@ -25,7 +25,7 @@ public class TestVac extends Application { VacCollectorView controller; @Override - public void start(Stage primaryStage) { + public void start(Stage stage) { try { Sensor sensor1 = Virtual.randomDoubleSensor("vac1", Duration.ofMillis(200), 1e-5, 2e-6); Sensor 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); - primaryStage.setTitle("Vacuum measurement test"); - primaryStage.setScene(scene); - primaryStage.show(); + stage.setTitle("Vacuum measurement test"); + stage.setScene(scene); + stage.show(); } catch (Exception ex) { throw new Error(ex); } diff --git a/numass-main/src/main/java/inr/numass/models/sterile/TestModels.java b/numass-main/src/main/java/inr/numass/models/sterile/TestModels.java index 9802784d..32c3de9a 100644 --- a/numass-main/src/main/java/inr/numass/models/sterile/TestModels.java +++ b/numass-main/src/main/java/inr/numass/models/sterile/TestModels.java @@ -67,7 +67,7 @@ public class TestModels { double A = meta.getDouble("resolution", meta.getDouble("resolution.width", 8.3e-5));//8.3e-5 double from = meta.getDouble("from", 13900d); 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; if (meta.hasValue("resolution.tailAlpha")) { resolutionTail = ResolutionFunction.getAngledTail(meta.getDouble("resolution.tailAlpha"), meta.getDouble("resolution.tailBeta", 0)); @@ -78,7 +78,7 @@ public class TestModels { RangedNamedSetSpectrum beta = new BetaSpectrum(); ModularSpectrum sp = new ModularSpectrum(beta, new ResolutionFunction(A, resolutionTail), from, to); if (meta.getBoolean("caching", false)) { - context.getLog().report("Caching turned on"); + context.getChronicle().report("Caching turned on"); sp.setCaching(true); } //Adding trapping energy dependence diff --git a/numass-viewer/src/main/java/inr/numass/viewer/TestDirectoryViewer.java b/numass-viewer/src/main/java/inr/numass/viewer/TestDirectoryViewer.java index 9c4b1dda..79e94c85 100644 --- a/numass-viewer/src/main/java/inr/numass/viewer/TestDirectoryViewer.java +++ b/numass-viewer/src/main/java/inr/numass/viewer/TestDirectoryViewer.java @@ -31,7 +31,7 @@ import java.io.IOException; public class TestDirectoryViewer extends Application { @Override - public void start(Stage primaryStage) throws IOException { + public void start(Stage stage) throws IOException { new StorageManager().startGlobal(); 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); - primaryStage.setTitle("Detector Visualisation test"); - primaryStage.setScene(scene); - primaryStage.setMinHeight(600); - primaryStage.setMinWidth(800); + stage.setTitle("Detector Visualisation test"); + stage.setScene(scene); + stage.setMinHeight(600); + stage.setMinWidth(800); // primaryStage.setResizable(false); - primaryStage.show(); + stage.show(); } /**