diff --git a/numass-control/cryotemp/build.gradle b/numass-control/cryotemp/build.gradle new file mode 100644 index 00000000..6a97c860 --- /dev/null +++ b/numass-control/cryotemp/build.gradle @@ -0,0 +1,16 @@ +apply plugin: 'application' + +if (!hasProperty('mainClass')) { + ext.mainClass = 'inr.numass.cryotemp.PKT8App' +} +mainClassName = mainClass + +//mainClassName = "inr.numass.readvac.Main" + +dependencies { + compile 'commons-cli:commons-cli:1.3' + compile 'de.jensd:shichimifx:1.0.5' + compile project(':dataforge-control') + compile project(':dataforge-storage') + compile project(':dataforge-plots') +} \ No newline at end of file diff --git a/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8.java b/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8.java new file mode 100644 index 00000000..b1f32b68 --- /dev/null +++ b/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8.java @@ -0,0 +1,114 @@ +package inr.numass.cryotemp; + +import hep.dataforge.control.devices.PortSensor; +import hep.dataforge.control.measurements.AbstractMeasurement; +import hep.dataforge.control.measurements.Measurement; +import hep.dataforge.exceptions.ControlException; +import hep.dataforge.exceptions.MeasurementException; +import hep.dataforge.tables.DataPoint; + +/** + * Created by darksnake on 28-Sep-16. + */ +public class PKT8 extends PortSensor { + + public static final String PGA = "pga"; + public static final String SPS = "sps"; + public static final String ABUF = "abuf"; + + @Override + protected Measurement createMeasurement() throws MeasurementException { + return null; + } + + @Override + protected Object computeState(String stateName) throws ControlException { + return super.computeState(stateName); + } + + /** + * '0' : 2,5 SPS '1' : 5 SPS '2' : 10 SPS '3' : 25 SPS '4' : 50 SPS '5' : + * 100 SPS '6' : 500 SPS '7' : 1 kSPS '8' : 3,75 kSPS + * + * @param sps + * @return + */ + private String spsToStr(int sps) { + switch (sps) { + case 0: + return "2,5 SPS"; + case 1: + return "5 SPS"; + case 2: + return "10 SPS"; + case 3: + return "25 SPS"; + case 4: + return "50 SPS"; + case 5: + return "100 SPS"; + case 6: + return "500 SPS"; + case 7: + return "1 kSPS"; + case 8: + return "3.75 kSPS"; + default: + return "unknown value"; + } + } + + /** + * '0' : ± 5 В '1' : ± 2,5 В '2' : ± 1,25 В '3' : ± 0,625 В '4' : ± 312.5 мВ + * '5' : ± 156,25 мВ '6' : ± 78,125 мВ + * + * @param sps + * @return + */ + private String pgaToStr(int sps) { + switch (sps) { + case 0: + return "± 5 V"; + case 1: + return "± 2,5 V"; + case 2: + return "± 1,25 V"; + case 3: + return "± 0,625 V"; + case 4: + return "± 312.5 mV"; + case 5: + return "± 156,25 mV"; + case 6: + return "± 78,125 mV"; + default: + return "unknown value"; + } + } + + public String getSPS() { + return spsToStr(sps); + } + + public String getPGA() { + return pgaToStr(pga); + } + + public String getABUF() { + return Integer.toString(abuf); + } + + + private class PKT8Measurement extends AbstractMeasurement { + + @Override + public void start() { + + } + + @Override + public boolean stop(boolean force) throws MeasurementException { + return false; + } + } +} diff --git a/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8App.java b/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8App.java new file mode 100644 index 00000000..3c6dd857 --- /dev/null +++ b/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8App.java @@ -0,0 +1,83 @@ +/* + * 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.cryotemp; + +import ch.qos.logback.classic.Level; +import hep.dataforge.exceptions.ControlException; +import hep.dataforge.storage.commons.StorageManager; +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Locale; + +/** + * @author darksnake + */ +public class PKT8App extends Application { + + PKT8MainViewController controller; + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + launch(args); + } + + @Override + public void start(Stage primaryStage) 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); + rootLogger.setLevel(Level.INFO); + new StorageManager().startGlobal(); + + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/PKT8MainView.fxml")); + + Parent parent = loader.load(); + controller = loader.getController(); + +// Meta deviceMeta = XMLMetaConverter.fromStream(getClass().getResourceAsStream("/defaultConfig.xml")); + +// controller.setupDevice(deviceMeta); + + Scene scene = new Scene(parent, 600, 400); + + + primaryStage.setTitle("PKT8 cryogenic temperature viewer"); + primaryStage.setScene(scene); + primaryStage.setMinHeight(400); + primaryStage.setMinWidth(600); +// primaryStage.setResizable(false); + + primaryStage.show(); + } + + @Override + public void stop() throws Exception { + super.stop(); + if (controller != null) { + controller.close(); + controller = null; + } +// System.exit(0); + } + +} diff --git a/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8Device.java b/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8Device.java new file mode 100644 index 00000000..2074d4d6 --- /dev/null +++ b/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8Device.java @@ -0,0 +1,427 @@ +/* + * 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.cryotemp; + +import hep.dataforge.context.Context; +import hep.dataforge.control.collectors.RegularPointCollector; +import hep.dataforge.control.devices.PortSensor; +import hep.dataforge.control.measurements.AbstractMeasurement; +import hep.dataforge.control.ports.PortHandler; +import hep.dataforge.control.ports.TcpPortHandler; +import hep.dataforge.exceptions.ControlException; +import hep.dataforge.exceptions.MeasurementException; +import hep.dataforge.exceptions.PortException; +import hep.dataforge.exceptions.StorageException; +import hep.dataforge.meta.Annotated; +import hep.dataforge.meta.Meta; +import hep.dataforge.meta.MetaBuilder; +import hep.dataforge.names.Named; +import hep.dataforge.storage.api.PointLoader; +import hep.dataforge.storage.api.Storage; +import hep.dataforge.storage.commons.LoaderFactory; +import hep.dataforge.tables.DataPoint; +import hep.dataforge.tables.TableTableFormatBuilder; +import hep.dataforge.values.Value; + +import java.time.Duration; +import java.time.Instant; +import java.util.*; +import java.util.function.Function; + +/** + * A device controller for Dubna PKT 8 cryogenic thermometry device + * + * @author Alexander Nozik + */ +public class PKT8Device extends PortSensor implements PortHandler.PortController { + + private static final String[] CHANNEL_DESIGNATIONS = {"a", "b", "c", "d", "e", "f", "g", "h"}; + /** + * The key is the letter (a,b,c,d...) as in measurements + */ + private final Map channels = new HashMap<>(); + private PointLoader pointLoader; + private RegularPointCollector collector; + private boolean isStarted = false; + private PortHandler handler; + private int sps = -1; + private int pga = -1; + private int abuf = -1; + + public PKT8Device(String name, Context context, Meta annotation) { + super(name, context, annotation); + } + + /** + * Start measurement + * + * @throws ControlException + */ + @Override + public void doStart(Meta measurement) throws ControlException { + +// Meta meta = new MetaChain("device", measurement.meta(),meta()); + if (!isStarted) { + //setup storage + + try { + Storage storage = getPrimaryStorage(measurement); + String suffix = Integer.toString((int) Instant.now().toEpochMilli()); + + // Building data format + TableTableFormatBuilder TableFormatBuilder = new TableTableFormatBuilder() + .addTime("timestamp"); + List names = new ArrayList<>(); + + for (Channel channel : this.channels.values()) { + TableFormatBuilder.addNumber(channel.getName()); + names.add(channel.getName()); + } + + this.pointLoader = LoaderFactory.buildPointLoder(storage, "cryotemp_" + suffix, "", "timestamp", TableFormatBuilder.build()); + + Duration duration = Duration.parse(meta().getString("averagingDuration", "PT30S")); + + collector = new RegularPointCollector((dp) -> { + if (pointLoader != null) { + try { + getLogger().debug("Point measurement complete. Pushing..."); + pointLoader.push(dp); + } catch (StorageException ex) { + getLogger().error("Error while pushing point to loader", ex); + } + } + }, duration, names); + } catch (StorageException ex) { + getLogger().error("Can't setup storage", ex); + } + + handler.send("s"); + isStarted = true; + } + } + + @Override + public void init() throws ControlException { + + //read channel configuration + if (meta().hasNode("channel")) { + for (Meta node : meta().getNodes("channel")) { + String designation = node.getString("designation", "default"); + this.channels.put(designation, new Channel(node)); + } + } else { + //set default channel configuration + for (String designation : CHANNEL_DESIGNATIONS) { + channels.put(designation, new Channel(designation)); + } + getLogger().warn("No channels defined in configuration"); + } + + //setup connection + if (meta().hasNode("debug")) { + handler = new PKT8VirtualPort("PKT8", meta().getNode("debug")); + } else { + String ip = this.meta().getString("connection.ip", "127.0.0.1"); + int port = this.meta().getInt("connection.port", 4001); + handler = new TcpPortHandler(ip, port, getName()); + } + handler.setDelimeter("\n"); + handler.holdBy(this); + handler.open(); + handler.send("p"); + handler.sendAndWait("p", null, 1000); + + //update parameters from meta + if (meta().hasValue("pga")) { + getLogger().info("Setting dynamic range to " + meta().getInt("pga")); + String response = handler.sendAndWait("g" + meta().getInt("pga"), null, 400).trim(); + if (response.contains("=")) { + this.pga = Integer.parseInt(response.substring(4)); + } else { + getLogger().error("Setting pga failsed with message: " + response); + } + } + + setSPS(meta().getInt("sps", 0)); + setBUF(meta().getInt("abuf", 100)); + + super.init(); + } + + @Override + public void shutdown() throws ControlException { + stop(); + try { + this.handler.unholdBy(this); + this.handler.close(); + + } catch (Exception ex) { + throw new ControlException(ex); + } + super.shutdown(); + } + + public Collection getChanels() { + return this.channels.values(); + } + + private void setBUF(int buf) throws PortException { + getLogger().info("Setting avaraging buffer size to " + buf); + String response = handler.sendAndWait("b" + buf, null, 400).trim(); + if (response.contains("=")) { + this.abuf = Integer.parseInt(response.substring(14)); + getLogger().info("successfully set buffer size to {}", this.abuf); + } else { + getLogger().error("Setting averaging buffer failsed with message: " + response); + } + } + + @Override + public void doStop() throws ControlException { + handler.send("p"); + isStarted = false; + if (collector != null) { + collector.cancel(); + } + } + + public void changeParameters(int sps, int abuf) { + this.executor.submit(() -> { + try { + stop(); + //setting sps + setSPS(sps); + //setting buffer + setBUF(abuf); + start(); + } catch (ControlException ex) { + getLogger().error("Control error", ex); + } + }); + } + + @Override + public void accept(String message) { + String trimmed = message.trim(); + + if (isStarted) { + if (trimmed.equals("stopped")) { + isStarted = false; + getLogger().info("Measurement paused"); + } else { + String designation = trimmed.substring(0, 1); + double rawValue = Double.parseDouble(trimmed.substring(1)) / 100; + + if (channels.containsKey(designation)) { + Channel channel = channels.get(designation); + notifyMeasurementComplete(channel.getName(), rawValue, channel.getTemperature(rawValue)); + collector.put(channel.getName(), channel.getTemperature(rawValue)); + } else { + notifyMeasurementComplete(designation, rawValue, -1); + } + + } + } + } + + private void notifyMeasurementComplete(String channel, double rawValue, double temperature) { + measurementResult(null, new Data(channel, rawValue, temperature)); + } + + @Override + public void error(String errorMessage, Throwable error) { + + } + + /** + * '0' : 2,5 SPS '1' : 5 SPS '2' : 10 SPS '3' : 25 SPS '4' : 50 SPS '5' : + * 100 SPS '6' : 500 SPS '7' : 1 kSPS '8' : 3,75 kSPS + * + * @param sps + * @return + */ + private String spsToStr(int sps) { + switch (sps) { + case 0: + return "2,5 SPS"; + case 1: + return "5 SPS"; + case 2: + return "10 SPS"; + case 3: + return "25 SPS"; + case 4: + return "50 SPS"; + case 5: + return "100 SPS"; + case 6: + return "500 SPS"; + case 7: + return "1 kSPS"; + case 8: + return "3.75 kSPS"; + default: + return "unknown value"; + } + } + + /** + * '0' : ± 5 В '1' : ± 2,5 В '2' : ± 1,25 В '3' : ± 0,625 В '4' : ± 312.5 мВ + * '5' : ± 156,25 мВ '6' : ± 78,125 мВ + * + * @param sps + * @return + */ + private String pgaToStr(int sps) { + switch (sps) { + case 0: + return "± 5 V"; + case 1: + return "± 2,5 V"; + case 2: + return "± 1,25 V"; + case 3: + return "± 0,625 V"; + case 4: + return "± 312.5 mV"; + case 5: + return "± 156,25 mV"; + case 6: + return "± 78,125 mV"; + default: + return "unknown value"; + } + } + + public String getSPS() { + return spsToStr(sps); + } + + private void setSPS(int sps) throws PortException { + getLogger().info("Setting sampling rate to " + spsToStr(sps)); + String response = handler.sendAndWait("v" + sps, null, 400).trim(); + if (response.contains("=")) { + this.sps = Integer.parseInt(response.substring(4)); + getLogger().info("successfully sampling rate to {}", spsToStr(this.sps)); + } else { + getLogger().error("Setting sps failsed with message: " + response); + } + } + + public String getPGA() { + return pgaToStr(pga); + } + + public String getABUF() { + return Integer.toString(abuf); + } + + public static class Data { + + public String channel; + public double rawValue; + public double temperature; + + public Data(String channel, double rawValue, double temperature) { + this.channel = channel; + this.rawValue = rawValue; + this.temperature = temperature; + } + + } + + public class Channel implements Named, Annotated { + + private final Meta meta; + private final Function transformation; + + public Channel(String name) { + this.meta = new MetaBuilder("channel") + .putValue("name", name); + transformation = (d) -> d; + } + + public Channel(Meta meta) { + this.meta = meta; + + String transformationType = meta.getString("transformationType", "default"); + if (meta.hasValue("coefs")) { + switch (transformationType) { + case "default": + case "hyperbolic": + List coefs = meta.getValue("coefs").listValue(); + double r0 = meta.getDouble("r0", 1000); + transformation = (r) -> { + if (coefs == null) { + return -1d; + } else { + double res = 0; + for (int i = 0; i < coefs.size(); i++) { + res += coefs.get(i).doubleValue() * Math.pow(r0 / r, i); + } + return res; + } + }; + break; + default: + throw new RuntimeException("Unknown transformation type"); + } + } else { + //identity transformation + transformation = (d) -> d; + + } + + } + + @Override + public String getName() { + return meta().getString("name"); + } + + @Override + public Meta meta() { + return meta; + } + + public String description() { + return meta().getString("description", ""); + } + + /** + * @param r negative if temperature transformation not defined + * @return + */ + public double getTemperature(double r) { + return transformation.apply(r); + } + + } + + public class PKT8Measurement extends AbstractMeasurement { + + @Override + public void start() { + + } + + @Override + public boolean stop(boolean force) throws MeasurementException { + return false; + } + } +} diff --git a/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8MainViewController.java b/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8MainViewController.java new file mode 100644 index 00000000..9c618cc0 --- /dev/null +++ b/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8MainViewController.java @@ -0,0 +1,277 @@ +/* + * 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.cryotemp; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.AppenderBase; +import de.jensd.shichimifx.utils.SplitPaneDividerSlider; +import hep.dataforge.context.GlobalContext; +import hep.dataforge.control.devices.Device; +import hep.dataforge.control.devices.DeviceListener; +import hep.dataforge.control.measurements.MeasurementDevice; +import hep.dataforge.control.measurements.MeasurementListener; +import hep.dataforge.exceptions.ControlException; +import hep.dataforge.io.MetaFileReader; +import hep.dataforge.meta.Meta; +import hep.dataforge.meta.MetaBuilder; +import hep.dataforge.meta.MetaUtils; +import hep.dataforge.plots.XYPlotFrame; +import hep.dataforge.plots.data.DynamicPlottable; +import hep.dataforge.plots.data.DynamicPlottableSet; +import hep.dataforge.plots.jfreechart.JFreeChartFrame; +import hep.dataforge.values.Value; +import inr.numass.cryotemp.PKT8Device.Data; +import javafx.beans.value.ObservableValue; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.control.SplitPane; +import javafx.scene.control.TextArea; +import javafx.scene.control.ToggleButton; +import javafx.scene.layout.AnchorPane; +import javafx.stage.FileChooser; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.text.ParseException; +import java.time.Instant; +import java.util.Collection; +import java.util.ResourceBundle; + +/** + * FXML Controller class + * + * @author darksnake + */ +public class PKT8MainViewController implements Initializable, DeviceListener, MeasurementListener, AutoCloseable { + + public static final String DEFAULT_CONFIG_LOCATION = "devices.xml"; + private PKT8Device device; + private XYPlotFrame plotFrame; + private DynamicPlottableSet plottables; + private Meta currentPlotConfig; + + @FXML + private Button loadConfigButton; + @FXML + private SplitPane consoleSplitPane; + @FXML + private TextArea logArea; + @FXML + private ToggleButton startStopButton; + @FXML + private AnchorPane plotArea; + + @FXML + private ToggleButton consoleButton; + + @Override + public void close() throws Exception { + if (device != null) { + device.stop(); + device.shutdown(); + } + } + + /** + * Initializes the controller class. + */ + @Override + public void initialize(URL url, ResourceBundle rb) { + setupPlotFrame(null); + SplitPaneDividerSlider slider = new SplitPaneDividerSlider(consoleSplitPane, 0, SplitPaneDividerSlider.Direction.DOWN); + consoleButton.selectedProperty().addListener((ObservableValue observable, Boolean oldValue, Boolean newValue) -> { + slider.setAimContentVisible(newValue); + }); + slider.setAimContentVisible(false); + } + + @FXML + private void onLoadConfigClick(ActionEvent event) throws IOException, ParseException, ControlException { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Open configuration file"); + fileChooser.setInitialFileName(DEFAULT_CONFIG_LOCATION); + fileChooser.setInitialDirectory(GlobalContext.instance().io().getRootDirectory()); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("xml", "*.xml", "*.XML")); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("json", "*.json", "*.JSON")); +// fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("all", "*.*")); + File cfgFile = fileChooser.showOpenDialog(loadConfigButton.getScene().getWindow()); + + if (cfgFile != null) { + setConfig(MetaFileReader.read(cfgFile)); + } + } + + private void loadTestConfig() throws ControlException { + try { + Meta testConfig = MetaFileReader + .read(new File(getClass().getResource("/config/defaultConfig.xml").toURI())); + setConfig(testConfig); + } catch (URISyntaxException | IOException | ParseException ex) { + throw new Error(ex); + } + } + + public String getDeviceName() { + return "PKT8"; + } + + public void setConfig(Meta config) throws ControlException { + if (config.hasNode("plotConfig")) { + Meta plotConfig = MetaUtils.findNodeByValue(config, "plotConfig", "device", getDeviceName()); + if (plotConfig == null) { + plotConfig = config.getNode("plotConfig"); + } + + setupPlotFrame(plotConfig.getNode("plotFrame", null)); + currentPlotConfig = plotConfig; + } + + if (config.hasNode("device")) { + Meta deviceMeta = MetaUtils.findNodeByValue(config, "device", "name", Value.of(getDeviceName())); + setupDevice(deviceMeta); + } else { + setupDevice(config); + } + + } + + /** + * Set o reset plot area + */ + private void setupPlotFrame(Meta plotFrameMeta) { + plottables = new DynamicPlottableSet(); + plotArea.getChildren().clear(); + Meta plotConfig; + if (plotFrameMeta != null) { + plotConfig = new MetaBuilder(plotFrameMeta) + .setValue("xAxis.timeAxis", true); + } else { + plotConfig = new MetaBuilder("plotFrame") + .setValue("xAxis.timeAxis", true); + } + plotFrame = new JFreeChartFrame("plot", plotConfig).display(plotArea); + } + + public void setupDevice(Meta deviceMeta) throws ControlException { + if (device != null) { + device.stop(); + device.shutdown(); + } + + this.device = new PKT8Device("PKT8", GlobalContext.instance(), deviceMeta); + device.addDeviceListener(this); + device.addMeasurementListener(this); + + logArea.appendText("Starting log...\n"); + + Appender appender = new AppenderBase() { + // private final DateTimeFormatter formatter = DateTimeFormatter.ISO_TIME; + @Override + protected void append(ILoggingEvent e) { + logArea.appendText(String.format("%s > (%s) [%s] %s%n", + e.getLoggerName(), + Instant.now().toString(), + e.getLevel(), + e.getFormattedMessage())); + } + }; + + appender.start(); + + device.getLogger().addAppender(appender); + + device.init(); + } + + @Override + public void notifyDeviceInitialized(Device device) { + Collection channels = this.device.getChanels(); + for (PKT8Device.Channel channel : channels) { + if (!plottables.hasPlottable(channel.getName())) { + + //plot config from device configuration + Meta deviceLineMeta = channel.meta().getNode("plot", channel.meta()); + + //Do not use view config here, it is applyed separately + DynamicPlottable plottable = new DynamicPlottable(channel.getName(), deviceLineMeta); + plottables.addPlottable(plottable); + plotFrame.add(plottable); + } + } + startStopButton.setDisable(false); + + if (currentPlotConfig != null) { + applyViewConfig(currentPlotConfig); + } + } + + public void applyViewConfig(Meta viewConfig) { + plottables.applyConfig(viewConfig); + } + + @Override + public void notifyDeviceShutdown(Device device) { + startStopButton.setDisable(true); + } + + @Override + public void notifyDeviceStateChanged(Device device, String name, Value oldState, Value newState) { + + } + +// @Override +// public void sendMessage(Device device, int priority, Meta message) { +// String tag = message.getString("tag", ""); +// logArea.appendText(String.format("%s > (%s) [%s] %s%n", device.getName(), Instant.now().toString(), tag, message)); +// } + + @Override + public void notifyMeasurementStarted(MeasurementDevice device, Meta measurement) { + + } + + @Override + public void notifyMeasurementStopped(MeasurementDevice device) { + + } + + @Override + public void notifyMeasurementResult(MeasurementDevice device, Meta measurement, Data measurementResult) { + plottables.put(measurementResult.channel, measurementResult.temperature); + } + + @FXML + private void onStartStopClick(ActionEvent event) { + if (device != null) { + try { + if (startStopButton.isSelected()) { + device.start(); + } else { + //in case device started + device.stop(); + } + } catch (ControlException ex) { + + } + } + } + +} diff --git a/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8VirtualPort.java b/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8VirtualPort.java new file mode 100644 index 00000000..54341886 --- /dev/null +++ b/numass-control/cryotemp/src/main/java/inr/numass/cryotemp/PKT8VirtualPort.java @@ -0,0 +1,87 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package inr.numass.cryotemp; + +import hep.dataforge.control.ports.VirtualPort; +import hep.dataforge.exceptions.PortException; +import hep.dataforge.meta.Annotated; +import hep.dataforge.meta.Meta; +import hep.dataforge.meta.MetaUtils; +import hep.dataforge.values.Value; + +import java.time.Duration; +import java.util.Random; + + +/** + * @author Alexander Nozik + */ +public class PKT8VirtualPort extends VirtualPort implements Annotated { + + private final Meta meta; + private final Random generator = new Random(); + + public PKT8VirtualPort(String portName, Meta meta) { + super(portName); + this.meta = meta; + } + + @Override + public Meta meta() { + return meta; + } + + @Override + protected void evaluateRequest(String request) { + switch (request) { + case "s": + String[] letters = {"a", "b", "c", "d", "e", "f", "g", "h"}; + for (String letter : letters) { + Meta channelMeta = MetaUtils.findNodeByValue(meta(), "channel", "letter", Value.of(letter)); + + double average; + double sigma; + if (channelMeta != null) { + average = channelMeta.getDouble("av", 1200); + sigma = channelMeta.getDouble("sigma", 50); + } else { + average = 1200d; + sigma = 50d; + } + + this.planRegularResponse( + () -> { + double res = average + generator.nextGaussian() * sigma; + //TODO convert double value to formatted string + return letter + "000120000\n"; + }, + Duration.ZERO, Duration.ofSeconds(5), letter, "measurement" + ); + } + return; + case "p": + cancelByTag("measurement"); + this.recievePhrase("stopped\n\r"); + return; + } + } + + @Override + public void open() throws PortException { + + } + + @Override + public boolean isOpen() { + return true; + } + + @Override + public void close() throws Exception { + cancelByTag("measurement"); + } + +} diff --git a/numass-control/cryotemp/src/main/resources/config/defaultConfig.xml b/numass-control/cryotemp/src/main/resources/config/defaultConfig.xml new file mode 100644 index 00000000..01795727 --- /dev/null +++ b/numass-control/cryotemp/src/main/resources/config/defaultConfig.xml @@ -0,0 +1,16 @@ + + + + + 120 + + + + + + + + + diff --git a/numass-control/cryotemp/src/main/resources/fxml/PKT8MainView.fxml b/numass-control/cryotemp/src/main/resources/fxml/PKT8MainView.fxml new file mode 100644 index 00000000..7fd4796f --- /dev/null +++ b/numass-control/cryotemp/src/main/resources/fxml/PKT8MainView.fxml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + +