diff --git a/numass-control/control-room/src/main/kotlin/inr/numass/control/ServerApp.kt b/numass-control/control-room/src/main/kotlin/inr/numass/control/ServerApp.kt index 2d4f0555..fa094307 100644 --- a/numass-control/control-room/src/main/kotlin/inr/numass/control/ServerApp.kt +++ b/numass-control/control-room/src/main/kotlin/inr/numass/control/ServerApp.kt @@ -7,13 +7,12 @@ import tornadofx.* * Created by darksnake on 19-May-17. */ class ServerApp : App(BoardView::class) { - val controller: BoardController by inject(); + private val controller: BoardController by inject(); override fun start(stage: Stage) { controller.load(this) super.start(stage) - setDFStageIcon(stage) } override fun stop() { diff --git a/numass-control/src/main/kotlin/inr/numass/control/StorageHelper.kt b/numass-control/src/main/kotlin/inr/numass/control/StorageHelper.kt index 3c34da65..a42f2539 100644 --- a/numass-control/src/main/kotlin/inr/numass/control/StorageHelper.kt +++ b/numass-control/src/main/kotlin/inr/numass/control/StorageHelper.kt @@ -6,13 +6,12 @@ import hep.dataforge.exceptions.StorageException import hep.dataforge.storage.api.TableLoader import hep.dataforge.values.Values 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 { +class StorageHelper(private val device: AbstractDevice, private val loaderFactory: (StorageConnection)-> TableLoader) : AutoCloseable { private val loaderMap = HashMap() fun push(point: Values) { diff --git a/numass-control/vac/src/main/java/inr/numass/control/readvac/CM32Device.java b/numass-control/vac/src/main/java/inr/numass/control/readvac/CM32Device.java deleted file mode 100644 index 2d1939e4..00000000 --- a/numass-control/vac/src/main/java/inr/numass/control/readvac/CM32Device.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package inr.numass.control.readvac; - -import hep.dataforge.context.Context; -import hep.dataforge.control.devices.Device; -import hep.dataforge.control.devices.PortSensor; -import hep.dataforge.control.measurements.Measurement; -import hep.dataforge.control.measurements.SimpleMeasurement; -import hep.dataforge.control.ports.ComPortHandler; -import hep.dataforge.control.ports.PortFactory; -import hep.dataforge.control.ports.PortHandler; -import hep.dataforge.exceptions.ControlException; -import hep.dataforge.meta.Meta; - -/** - * @author Alexander Nozik - */ -public class CM32Device extends PortSensor { - public CM32Device() { - } - - public CM32Device(Context context, Meta meta) { - setContext(context); - setMeta(meta); - } - - @Override - protected PortHandler buildHandler(String portName) throws ControlException { - getLogger().info("Connecting to port {}", portName); - PortHandler newHandler; - if (portName.startsWith("com")) { - newHandler = new ComPortHandler(portName, 2400, 8, 1, 0); - } else { - newHandler = PortFactory.getPort(portName); - } - newHandler.setDelimiter("T--\r"); - return newHandler; - } - - @Override - protected Measurement createMeasurement() { - return new CMVacMeasurement(); - } - - @Override - public String getType() { - return meta().getString("type", "Leibold CM32"); - } - - private class CMVacMeasurement extends SimpleMeasurement { - - private static final String CM32_QUERY = "MES R PM 1\r\n"; - - @Override - protected synchronized Double doMeasure() throws Exception { - - String answer = sendAndWait(CM32_QUERY, timeout()); - - if (answer.isEmpty()) { - this.updateMessage("No signal"); - updateState(CONNECTED_STATE, false); - return null; - } else if (!answer.contains("PM1:mbar")) { - this.updateMessage("Wrong answer: " + answer); - updateState(CONNECTED_STATE, false); - return null; - } else if (answer.substring(14, 17).equals("OFF")) { - this.updateMessage("Off"); - updateState(CONNECTED_STATE, true); - return null; - } else { - this.updateMessage("OK"); - updateState(CONNECTED_STATE, true); - return Double.parseDouble(answer.substring(14, 17) + answer.substring(19, 23)); - } - } - - @Override - public Device getDevice() { - return CM32Device.this; - } - } - -} diff --git a/numass-control/vac/src/main/java/inr/numass/control/readvac/ConsoleVac.java b/numass-control/vac/src/main/java/inr/numass/control/readvac/ConsoleVac.java deleted file mode 100644 index d39f475f..00000000 --- a/numass-control/vac/src/main/java/inr/numass/control/readvac/ConsoleVac.java +++ /dev/null @@ -1,57 +0,0 @@ -package inr.numass.control.readvac; - -import hep.dataforge.control.devices.Sensor; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Options; - -import java.time.Duration; -import java.time.Instant; - -/** - * A console based application to test vacuum readings - * Created by darksnake on 06-Dec-16. - */ -public class ConsoleVac { - @SuppressWarnings("unchecked") - public static void main(String[] args) throws Exception { - Options options = new Options(); - - options.addOption("c", "class", true, "A short or long class name for vacuumeter device"); - options.addOption("n", "name", true, "A device name"); - options.addOption("p", "port", true, "Port name in dataforge-control notation"); - options.addOption("d", "delay", true, "A delay between measurements in Duration notation"); - - if (args.length == 0) { - new HelpFormatter().printHelp("vac-console", options); - return; - } - - DefaultParser parser = new DefaultParser(); - CommandLine cli = parser.parse(options, args); - - String className = cli.getOptionValue("c"); - if (!className.contains(".")) { - className = "inr.numass.readvac.devices." + className; - } - String name = cli.getOptionValue("n", "sensor"); - String port = cli.getOptionValue("p", "com::/dev/ttyUSB0"); - Duration delay = Duration.parse(cli.getOptionValue("d", "PT1M")); - - if (className == null) { - throw new RuntimeException("Vacuumeter class not defined"); - } - Sensor sensor = (Sensor) Class.forName(className) - .getConstructor(String.class).newInstance(port); - try { - sensor.init(); - while (true) { - System.out.printf("(%s) %s -> %g%n", Instant.now().toString(), name, sensor.read()); - Thread.sleep(delay.toMillis()); - } - } finally { - sensor.shutdown(); - } - } -} diff --git a/numass-control/vac/src/main/java/inr/numass/control/readvac/MKSBaratronDevice.java b/numass-control/vac/src/main/java/inr/numass/control/readvac/MKSBaratronDevice.java deleted file mode 100644 index 01613efd..00000000 --- a/numass-control/vac/src/main/java/inr/numass/control/readvac/MKSBaratronDevice.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package inr.numass.control.readvac; - -import hep.dataforge.context.Context; -import hep.dataforge.control.devices.Device; -import hep.dataforge.control.devices.PortSensor; -import hep.dataforge.control.measurements.Measurement; -import hep.dataforge.control.measurements.SimpleMeasurement; -import hep.dataforge.control.ports.PortHandler; -import hep.dataforge.description.ValueDef; -import hep.dataforge.exceptions.ControlException; -import hep.dataforge.meta.Meta; - -/** - * @author Alexander Nozik - */ -@ValueDef(name = "channel") -public class MKSBaratronDevice extends PortSensor { - - public MKSBaratronDevice() { - } - - public MKSBaratronDevice(Context context, Meta meta) { - setContext(context); - setMeta(meta); - } - - - @Override - protected Measurement createMeasurement() { - return new BaratronMeasurement(); - } - - @Override - public String getType() { - return meta().getString("type", "MKS baratron"); - } - - @Override - protected PortHandler buildHandler(String portName) throws ControlException { - PortHandler handler = super.buildHandler(portName); - handler.setDelimiter("\r"); - return handler; - } - - private int getChannel() { - return meta().getInt("channel", 2); - } - - private class BaratronMeasurement extends SimpleMeasurement { - - @Override - public Device getDevice() { - return MKSBaratronDevice.this; - } - - @Override - protected synchronized Double doMeasure() throws Exception { - String answer = sendAndWait("AV" + getChannel() + "\r", timeout()); - if (answer == null || answer.isEmpty()) { -// invalidateState("connection"); - updateState(CONNECTED_STATE, false); - this.updateMessage("No connection"); - return null; - } else { - updateState(CONNECTED_STATE, true); - } - double res = Double.parseDouble(answer); - if (res <= 0) { - this.updateMessage("Non positive"); -// invalidateState("power"); - return null; - } else { - this.updateMessage("OK"); - return res; - } - } - - } -} diff --git a/numass-control/vac/src/main/java/inr/numass/control/readvac/MKSVacDevice.java b/numass-control/vac/src/main/java/inr/numass/control/readvac/MKSVacDevice.java deleted file mode 100644 index 274ede98..00000000 --- a/numass-control/vac/src/main/java/inr/numass/control/readvac/MKSVacDevice.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package inr.numass.control.readvac; - -import hep.dataforge.context.Context; -import hep.dataforge.control.devices.Device; -import hep.dataforge.control.devices.PortSensor; -import hep.dataforge.control.devices.StateDef; -import hep.dataforge.control.measurements.Measurement; -import hep.dataforge.control.measurements.SimpleMeasurement; -import hep.dataforge.control.ports.PortHandler; -import hep.dataforge.description.ValueDef; -import hep.dataforge.exceptions.ControlException; -import hep.dataforge.meta.Meta; -import hep.dataforge.values.Value; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.adapter.JavaBeanBooleanPropertyBuilder; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static hep.dataforge.values.ValueType.BOOLEAN; - -/** - * @author Alexander Nozik - */ -@ValueDef(name = "address", def = "253") -@ValueDef(name = "channel", def = "5") -@ValueDef(name = "powerButton", type = {BOOLEAN}, def = "true") -@StateDef(value = @ValueDef(name = "power", info = "Device powered up"), writable = true) -public class MKSVacDevice extends PortSensor { - - public MKSVacDevice() { - } - - public MKSVacDevice(Context context, Meta meta) { - setContext(context); - setMeta(meta); - } - - private String talk(String requestContent) throws ControlException { - String answer = sendAndWait(String.format("@%s%s;FF", getDeviceAddress(), requestContent), timeout()); - - Matcher match = Pattern.compile("@" + getDeviceAddress() + "ACK(.*);FF").matcher(answer); - if (match.matches()) { - return match.group(1); - } else { - throw new ControlException(answer); - } - } - - @Override - protected PortHandler buildHandler(String portName) throws ControlException { - PortHandler handler = super.buildHandler(portName); - handler.setDelimiter(";FF"); - return handler; - } - - private String getDeviceAddress() { - //PENDING cache this? - return meta().getString("address", "253"); - } - - @Override - protected Measurement createMeasurement() { - return new MKSVacMeasurement(); - } - - @Override - protected Object computeState(String stateName) throws ControlException { - switch (stateName) { - case "power": - return talk("FP?").equals("ON"); - default: - return super.computeState(stateName); - } - } - - @Override - protected void requestStateChange(String stateName, Value value) throws ControlException { - switch (stateName) { - case "power": - setPowerOn(value.booleanValue()); - break; - default: - super.requestStateChange(stateName, value); - } - - } - - @Override - public void shutdown() throws ControlException { - if (isConnected()) { - setPowerOn(false); - } - super.shutdown(); - } - - - private boolean isPowerOn() { - return getState("power").booleanValue(); - } - - private void setPowerOn(boolean powerOn) throws ControlException { - if (powerOn != isPowerOn()) { - if (powerOn) { -// String ans = talkMKS(p1Port, "@253ENC!OFF;FF"); -// if (!ans.equals("OFF")) { -// LoggerFactory.getLogger(getClass()).warn("The @253ENC!OFF;FF command is not working"); -// } - String ans = talk("FP!ON"); - if (ans.equals("ON")) { - updateState("power", true); - } else { - this.notifyError("Failed to set power state", null); - } - } else { - String ans = talk("FP!OFF"); - if (ans.equals("OFF")) { - updateState("power", false); - } else { - this.notifyError("Failed to set power state", null); - } - } - } - } - - public BooleanProperty powerOnProperty() { - try { - return new JavaBeanBooleanPropertyBuilder().bean(this) - .name("powerOn").getter("isPowerOn").setter("setPowerOn").build(); - } catch (NoSuchMethodException ex) { - throw new Error(ex); - } - } - - @Override - public String getType() { - return meta().getString("type", "MKS vacuumeter"); - } - - private int getChannel() { - return meta().getInt("channel", 5); - } - - private class MKSVacMeasurement extends SimpleMeasurement { - - @Override - protected synchronized Double doMeasure() throws Exception { -// if (getState("power").booleanValue()) { - String answer = talk("PR" + getChannel() + "?"); - if (answer == null || answer.isEmpty()) { - updateState(CONNECTED_STATE, false); - this.updateMessage("No connection"); - return null; - } - double res = Double.parseDouble(answer); - if (res <= 0) { - this.updateMessage("No power"); - invalidateState("power"); - return null; - } else { - this.updateMessage("OK"); - return res; - } - } - - @Override - public Device getDevice() { - return MKSVacDevice.this; - } - } -} diff --git a/numass-control/vac/src/main/java/inr/numass/control/readvac/MeradatVacDevice.java b/numass-control/vac/src/main/java/inr/numass/control/readvac/MeradatVacDevice.java deleted file mode 100644 index fabfb73c..00000000 --- a/numass-control/vac/src/main/java/inr/numass/control/readvac/MeradatVacDevice.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package inr.numass.control.readvac; - -import hep.dataforge.context.Context; -import hep.dataforge.control.devices.Device; -import hep.dataforge.control.devices.PortSensor; -import hep.dataforge.control.measurements.Measurement; -import hep.dataforge.control.measurements.SimpleMeasurement; -import hep.dataforge.control.ports.PortHandler; -import hep.dataforge.description.ValueDef; -import hep.dataforge.exceptions.ControlException; -import hep.dataforge.meta.Meta; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.math.RoundingMode; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static hep.dataforge.values.ValueType.NUMBER; - -/** - * @author Alexander Nozik - */ -@ValueDef(name = "address", type = {NUMBER}, def = "1", info = "A modbus address") -public class MeradatVacDevice extends PortSensor { - private static final String REQUEST = "0300000002"; - - public MeradatVacDevice() { - } - - public MeradatVacDevice(Context context, Meta meta) { - setContext(context); - setMeta(meta); - } - - @Override - protected PortHandler buildHandler(String portName) throws ControlException { - PortHandler newHandler = super.buildHandler(portName); - newHandler.setDelimiter("\r\n"); - return newHandler; - } - - @Override - protected Measurement createMeasurement() { - return new MeradatMeasurement(); - } - - @Override - public String getType() { - return meta().getString("type", "Vit vacuumeter"); - } - - public static String calculateLRC(String inputString) { - /* - * String is Hex String, need to convert in ASCII. - */ - byte[] bytes = new BigInteger(inputString, 16).toByteArray(); - int checksum = 0; - for (byte aByte : bytes) { - checksum += aByte; - } - String val = Integer.toHexString(-checksum); - val = val.substring(val.length() - 2).toUpperCase(); - if (val.length() < 2) { - val = "0" + val; - } - - return val; - } - - - private class MeradatMeasurement extends SimpleMeasurement { - - private final String query; // ":010300000002FA\r\n"; - private final Pattern response; - private final String base; - - public MeradatMeasurement() { - base = String.format(":%02d", meta().getInt("address", 1)); - String dataStr = base.substring(1) + REQUEST; - query = base + REQUEST + calculateLRC(dataStr) + "\r\n"; - response = Pattern.compile(base + "0304(\\w{4})(\\w{4})..\r\n"); - } - - @Override - protected synchronized Double doMeasure() throws Exception { - - String answer = sendAndWait(query, timeout(), phrase -> phrase.startsWith(base)); - - if (answer.isEmpty()) { - this.updateMessage("No signal"); - updateState(CONNECTED_STATE, false); - return null; - } else { - Matcher match = response.matcher(answer); - - if (match.matches()) { - double base = (double) (Integer.parseInt(match.group(1), 16)) / 10d; - int exp = Integer.parseInt(match.group(2), 16); - if (exp > 32766) { - exp = exp - 65536; - } - BigDecimal res = BigDecimal.valueOf(base * Math.pow(10, exp)); - res = res.setScale(4, RoundingMode.CEILING); - this.updateMessage("OK"); - updateState(CONNECTED_STATE, true); - return res.doubleValue(); - } else { - this.updateMessage("Wrong answer: " + answer); - updateState(CONNECTED_STATE, false); - return null; - } - } - } - - @Override - public Device getDevice() { - return MeradatVacDevice.this; - } - } - -} diff --git a/numass-control/vac/src/main/java/inr/numass/control/readvac/VacCollectorDevice.java b/numass-control/vac/src/main/java/inr/numass/control/readvac/VacCollectorDevice.java deleted file mode 100644 index 306317fa..00000000 --- a/numass-control/vac/src/main/java/inr/numass/control/readvac/VacCollectorDevice.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package inr.numass.control.readvac; - -import hep.dataforge.context.Context; -import hep.dataforge.control.RoleDef; -import hep.dataforge.control.collectors.RegularPointCollector; -import hep.dataforge.control.collectors.ValueCollector; -import hep.dataforge.control.connections.Roles; -import hep.dataforge.control.connections.StorageConnection; -import hep.dataforge.control.devices.Device; -import hep.dataforge.control.devices.Sensor; -import hep.dataforge.control.devices.StateDef; -import hep.dataforge.control.measurements.AbstractMeasurement; -import hep.dataforge.control.measurements.Measurement; -import hep.dataforge.description.ValueDef; -import hep.dataforge.exceptions.ControlException; -import hep.dataforge.meta.Meta; -import hep.dataforge.storage.api.TableLoader; -import hep.dataforge.storage.commons.LoaderFactory; -import hep.dataforge.tables.TableFormatBuilder; -import hep.dataforge.tables.ValueMap; -import hep.dataforge.utils.DateTimeUtils; -import hep.dataforge.values.Value; -import hep.dataforge.values.ValueType; -import hep.dataforge.values.Values; -import inr.numass.control.StorageHelper; - -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -import static hep.dataforge.control.devices.PortSensor.CONNECTED_STATE; - -/** - * @author Alexander Nozik - */ -@RoleDef(name = Roles.STORAGE_ROLE, objectType = StorageConnection.class, info = "Storage for acquired points") -@StateDef( - value = @ValueDef(name = "storing", info = "Define if this device is currently writes to storage"), - writable = true -) -public class VacCollectorDevice extends Sensor { - - private Map> sensorMap = new LinkedHashMap<>(); - private StorageHelper helper = new StorageHelper(VacCollectorDevice.this, this::buildLoader); - - public VacCollectorDevice() { - } - - public VacCollectorDevice(Context context, Meta meta) { - setContext(context); - setMeta(meta); - } - - - public void setSensors(Iterable> sensors) { - sensorMap = new LinkedHashMap<>(); - for (Sensor sensor : sensors) { - sensorMap.put(sensor.getName(), sensor); - } - } - - public void setSensors(Sensor... sensors) { - setSensors(Arrays.asList(sensors)); - } - - @Override - public void init() throws ControlException { - super.init(); - for (Sensor s : sensorMap.values()) { - s.init(); - } - } - - //TODO add dot path notation for states - - @Override - protected Measurement createMeasurement() { - //TODO use meta - return new VacuumMeasurement(); - } - - @Override - public String getType() { - return "Numass vacuum"; - } - -// public void setDelay(int delay) throws MeasurementException { -// this.delay = delay; -// if (isMeasuring()) { -// getMeasurement().stop(false); -// getMeasurement().start(); -// } -// } - - @Override - public void shutdown() throws ControlException { - super.shutdown(); - helper.close(); - for (Sensor sensor : getSensors()) { - sensor.shutdown(); - } - } - - private TableLoader buildLoader(StorageConnection connection) { - TableFormatBuilder format = new TableFormatBuilder().setType("timestamp", ValueType.TIME); - getSensors().forEach((s) -> { - format.setType(s.getName(), ValueType.NUMBER); - }); - - String suffix = DateTimeUtils.fileSuffix(); - - return LoaderFactory.buildPointLoder(connection.getStorage(), "vactms_" + suffix, "", "timestamp", format.build()); - } - - public Collection> getSensors() { - return sensorMap.values(); - } - - private Duration getAveragingDuration() { - return Duration.parse(meta().getString("averagingDuration", "PT30S")); - } - - private class VacuumMeasurement extends AbstractMeasurement { - - private final ValueCollector collector = new RegularPointCollector(getAveragingDuration(), this::result); - private ScheduledExecutorService executor; - private ScheduledFuture currentTask; - - @Override - public Device getDevice() { - return VacCollectorDevice.this; - } - - - @Override - public void start() { - executor = Executors.newSingleThreadScheduledExecutor((Runnable r) -> new Thread(r, "VacuumMeasurement thread")); - int delay = meta().getInt("delay", 5) * 1000; - currentTask = executor.scheduleWithFixedDelay(() -> { - sensorMap.values().forEach((sensor) -> { - try { - Object value; - if (sensor.optBooleanState(CONNECTED_STATE).orElse(false)) { - value = sensor.read(); - } else { - value = null; - } - collector.put(sensor.getName(), value); - } catch (Exception ex) { - collector.put(sensor.getName(), Value.NULL); - } - }); - }, 0, delay, TimeUnit.MILLISECONDS); - } - - - @Override - protected synchronized void result(Values result, Instant time) { - super.result(result, time); - helper.push(result); - } - - private Values terminator() { - ValueMap.Builder p = new ValueMap.Builder(); - p.putValue("timestamp", DateTimeUtils.now()); - sensorMap.keySet().forEach((n) -> { - p.putValue(n, null); - }); - return p.build(); - } - - @Override - public boolean stop(boolean force) { - boolean isRunning = currentTask != null; - if (isRunning) { - getLogger().debug("Stoping vacuum collector measurement. Writing terminator point"); - result(terminator()); - currentTask.cancel(force); - executor.shutdown(); - currentTask = null; - afterStop(); - } - return isRunning; - } - } - -} diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/CM32Device.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/CM32Device.kt new file mode 100644 index 00000000..82f4a87b --- /dev/null +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/CM32Device.kt @@ -0,0 +1,90 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package inr.numass.control.readvac + +import hep.dataforge.context.Context +import hep.dataforge.control.devices.Device +import hep.dataforge.control.devices.PortSensor +import hep.dataforge.control.measurements.Measurement +import hep.dataforge.control.measurements.SimpleMeasurement +import hep.dataforge.control.ports.ComPortHandler +import hep.dataforge.control.ports.PortFactory +import hep.dataforge.control.ports.PortHandler +import hep.dataforge.exceptions.ControlException +import hep.dataforge.meta.Meta + +/** + * @author Alexander Nozik + */ +class CM32Device : PortSensor { + constructor() {} + + constructor(context: Context, meta: Meta) { + setContext(context) + setMeta(meta) + } + + @Throws(ControlException::class) + override fun buildHandler(portName: String): PortHandler { + logger.info("Connecting to port {}", portName) + val newHandler: PortHandler + if (portName.startsWith("com")) { + newHandler = ComPortHandler(portName, 2400, 8, 1, 0) + } else { + newHandler = PortFactory.getPort(portName) + } + newHandler.setDelimiter("T--\r") + return newHandler + } + + override fun createMeasurement(): Measurement { + return CMVacMeasurement() + } + + override fun getType(): String { + return meta().getString("type", "Leibold CM32") + } + + private inner class CMVacMeasurement : SimpleMeasurement() { + + @Synchronized + @Throws(Exception::class) + override fun doMeasure(): Double? { + + val answer = sendAndWait(CM32_QUERY, timeout()) + + if (answer.isEmpty()) { + this.updateMessage("No signal") + updateState(PortSensor.CONNECTED_STATE, false) + return null + } else if (!answer.contains("PM1:mbar")) { + this.updateMessage("Wrong answer: " + answer) + updateState(PortSensor.CONNECTED_STATE, false) + return null + } else if (answer.substring(14, 17) == "OFF") { + this.updateMessage("Off") + updateState(PortSensor.CONNECTED_STATE, true) + return null + } else { + this.updateMessage("OK") + updateState(PortSensor.CONNECTED_STATE, true) + return java.lang.Double.parseDouble(answer.substring(14, 17) + answer.substring(19, 23)) + } + } + + override fun getDevice(): Device { + return this@CM32Device + } + + + } + + companion object { + + private val CM32_QUERY = "MES R PM 1\r\n" + } + +} diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/ConsoleVac.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/ConsoleVac.kt new file mode 100644 index 00000000..df1e3522 --- /dev/null +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/ConsoleVac.kt @@ -0,0 +1,56 @@ +package inr.numass.control.readvac + +import hep.dataforge.control.devices.Sensor +import org.apache.commons.cli.DefaultParser +import org.apache.commons.cli.HelpFormatter +import org.apache.commons.cli.Options +import java.time.Duration +import java.time.Instant + +/** + * A console based application to test vacuum readings + * Created by darksnake on 06-Dec-16. + */ +object ConsoleVac { + @Throws(Exception::class) + @JvmStatic + fun main(args: Array) { + val options = Options() + + options.addOption("c", "class", true, "A short or long class name for vacuumeter device") + options.addOption("n", "name", true, "A device name") + options.addOption("p", "port", true, "Port name in dataforge-control notation") + options.addOption("d", "delay", true, "A delay between measurements in Duration notation") + + if (args.size == 0) { + HelpFormatter().printHelp("vac-console", options) + return + } + + val parser = DefaultParser() + val cli = parser.parse(options, args) + + var className: String? = cli.getOptionValue("c") + if (!className!!.contains(".")) { + className = "inr.numass.readvac.devices." + className + } + val name = cli.getOptionValue("n", "sensor") + val port = cli.getOptionValue("p", "com::/dev/ttyUSB0") + val delay = Duration.parse(cli.getOptionValue("d", "PT1M")) + + if (className == null) { + throw RuntimeException("Vacuumeter class not defined") + } + val sensor = Class.forName(className) + .getConstructor(String::class.java).newInstance(port) as Sensor + try { + sensor.init() + while (true) { + System.out.printf("(%s) %s -> %g%n", Instant.now().toString(), name, sensor.read()) + Thread.sleep(delay.toMillis()) + } + } finally { + sensor.shutdown() + } + } +} diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MKSBaratronDevice.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MKSBaratronDevice.kt new file mode 100644 index 00000000..ec1e09bf --- /dev/null +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MKSBaratronDevice.kt @@ -0,0 +1,80 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package inr.numass.control.readvac + +import hep.dataforge.context.Context +import hep.dataforge.control.devices.Device +import hep.dataforge.control.devices.PortSensor +import hep.dataforge.control.measurements.Measurement +import hep.dataforge.control.measurements.SimpleMeasurement +import hep.dataforge.control.ports.PortHandler +import hep.dataforge.description.ValueDef +import hep.dataforge.exceptions.ControlException +import hep.dataforge.meta.Meta + +/** + * @author Alexander Nozik + */ +@ValueDef(name = "channel") +class MKSBaratronDevice : PortSensor { + + private val channel: Int + get() = meta().getInt("channel", 2)!! + + constructor() {} + + constructor(context: Context, meta: Meta) { + setContext(context) + setMeta(meta) + } + + + override fun createMeasurement(): Measurement { + return BaratronMeasurement() + } + + override fun getType(): String { + return meta().getString("type", "MKS baratron") + } + + @Throws(ControlException::class) + override fun buildHandler(portName: String): PortHandler { + val handler = super.buildHandler(portName) + handler.setDelimiter("\r") + return handler + } + + private inner class BaratronMeasurement : SimpleMeasurement() { + + override fun getDevice(): Device { + return this@MKSBaratronDevice + } + + @Synchronized + @Throws(Exception::class) + override fun doMeasure(): Double? { + val answer = sendAndWait("AV" + channel + "\r", timeout()) + if (answer == null || answer.isEmpty()) { + // invalidateState("connection"); + updateState(PortSensor.CONNECTED_STATE, false) + this.updateMessage("No connection") + return null + } else { + updateState(PortSensor.CONNECTED_STATE, true) + } + val res = java.lang.Double.parseDouble(answer) + if (res <= 0) { + this.updateMessage("Non positive") + // invalidateState("power"); + return null + } else { + this.updateMessage("OK") + return res + } + } + + } +} diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MKSVacDevice.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MKSVacDevice.kt new file mode 100644 index 00000000..4457b094 --- /dev/null +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MKSVacDevice.kt @@ -0,0 +1,164 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package inr.numass.control.readvac + +import hep.dataforge.context.Context +import hep.dataforge.control.devices.Device +import hep.dataforge.control.devices.PortSensor +import hep.dataforge.control.devices.StateDef +import hep.dataforge.control.measurements.Measurement +import hep.dataforge.control.measurements.SimpleMeasurement +import hep.dataforge.control.ports.PortHandler +import hep.dataforge.description.ValueDef +import hep.dataforge.exceptions.ControlException +import hep.dataforge.meta.Meta +import hep.dataforge.values.Value +import hep.dataforge.values.ValueType.BOOLEAN +import javafx.beans.property.BooleanProperty +import javafx.beans.property.adapter.JavaBeanBooleanPropertyBuilder +import java.util.regex.Pattern + +/** + * @author Alexander Nozik + */ +@ValueDef(name = "address", def = "253") +@ValueDef(name = "channel", def = "5") +@ValueDef(name = "powerButton", type = arrayOf(BOOLEAN), def = "true") +@StateDef(value = ValueDef(name = "power", info = "Device powered up"), writable = true) +class MKSVacDevice : PortSensor { + + private//PENDING cache this? + val deviceAddress: String + get() = meta().getString("address", "253") + + + private// String ans = talkMKS(p1Port, "@253ENC!OFF;FF"); + // if (!ans.equals("OFF")) { + // LoggerFactory.getLogger(getClass()).warn("The @253ENC!OFF;FF command is not working"); + // } + var isPowerOn: Boolean + get() = getState("power").booleanValue() + @Throws(ControlException::class) + set(powerOn) { + if (powerOn != isPowerOn) { + if (powerOn) { + val ans = talk("FP!ON") + if (ans == "ON") { + updateState("power", true) + } else { + this.notifyError("Failed to set power state", null) + } + } else { + val ans = talk("FP!OFF") + if (ans == "OFF") { + updateState("power", false) + } else { + this.notifyError("Failed to set power state", null) + } + } + } + } + + private val channel: Int + get() = meta().getInt("channel", 5)!! + + constructor() {} + + constructor(context: Context, meta: Meta) { + setContext(context) + setMeta(meta) + } + + @Throws(ControlException::class) + private fun talk(requestContent: String): String? { + val answer = sendAndWait(String.format("@%s%s;FF", deviceAddress, requestContent), timeout()) + + val match = Pattern.compile("@" + deviceAddress + "ACK(.*);FF").matcher(answer) + return if (match.matches()) { + match.group(1) + } else { + throw ControlException(answer) + } + } + + @Throws(ControlException::class) + override fun buildHandler(portName: String): PortHandler { + val handler = super.buildHandler(portName) + handler.setDelimiter(";FF") + return handler + } + + override fun createMeasurement(): Measurement { + return MKSVacMeasurement() + } + + @Throws(ControlException::class) + override fun computeState(stateName: String): Any { + when (stateName) { + "power" -> return talk("FP?") == "ON" + else -> return super.computeState(stateName) + } + } + + @Throws(ControlException::class) + override fun requestStateChange(stateName: String, value: Value) { + when (stateName) { + "power" -> isPowerOn = value.booleanValue() + else -> super.requestStateChange(stateName, value) + } + + } + + @Throws(ControlException::class) + override fun shutdown() { + if (isConnected) { + isPowerOn = false + } + super.shutdown() + } + + fun powerOnProperty(): BooleanProperty { + try { + return JavaBeanBooleanPropertyBuilder().bean(this) + .name("powerOn").getter("isPowerOn").setter("setPowerOn").build() + } catch (ex: NoSuchMethodException) { + throw Error(ex) + } + + } + + override fun getType(): String { + return meta().getString("type", "MKS vacuumeter") + } + + private inner class MKSVacMeasurement : SimpleMeasurement() { + + @Synchronized + @Throws(Exception::class) + override fun doMeasure(): Double? { + // if (getState("power").booleanValue()) { + val answer = talk("PR$channel?") + if (answer == null || answer.isEmpty()) { + updateState(PortSensor.CONNECTED_STATE, false) + this.updateMessage("No connection") + return null + } + val res = java.lang.Double.parseDouble(answer) + if (res <= 0) { + this.updateMessage("No power") + invalidateState("power") + return null + } else { + this.updateMessage("OK") + return res + } + } + + override fun getDevice(): Device { + return this@MKSVacDevice + } + } +} diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MeradatVacDevice.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MeradatVacDevice.kt new file mode 100644 index 00000000..ee35e92f --- /dev/null +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MeradatVacDevice.kt @@ -0,0 +1,124 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package inr.numass.control.readvac + +import hep.dataforge.context.Context +import hep.dataforge.control.devices.Device +import hep.dataforge.control.devices.PortSensor +import hep.dataforge.control.measurements.Measurement +import hep.dataforge.control.measurements.SimpleMeasurement +import hep.dataforge.control.ports.PortHandler +import hep.dataforge.description.ValueDef +import hep.dataforge.exceptions.ControlException +import hep.dataforge.meta.Meta +import hep.dataforge.values.ValueType.NUMBER +import java.math.BigDecimal +import java.math.BigInteger +import java.math.RoundingMode +import java.util.regex.Pattern + +/** + * @author Alexander Nozik + */ +@ValueDef(name = "address", type = arrayOf(NUMBER), def = "1", info = "A modbus address") +class MeradatVacDevice : PortSensor { + + constructor() {} + + constructor(context: Context, meta: Meta) { + setContext(context) + setMeta(meta) + } + + @Throws(ControlException::class) + override fun buildHandler(portName: String): PortHandler { + val newHandler = super.buildHandler(portName) + newHandler.setDelimiter("\r\n") + return newHandler + } + + override fun createMeasurement(): Measurement { + return MeradatMeasurement() + } + + override fun getType(): String { + return meta().getString("type", "Vit vacuumeter") + } + + + private inner class MeradatMeasurement : SimpleMeasurement() { + + private val query: String // ":010300000002FA\r\n"; + private val response: Pattern + private val base: String + + init { + base = String.format(":%02d", meta().getInt("address", 1)) + val dataStr = base.substring(1) + REQUEST + query = base + REQUEST + calculateLRC(dataStr) + "\r\n" + response = Pattern.compile(base + "0304(\\w{4})(\\w{4})..\r\n") + } + + @Synchronized + @Throws(Exception::class) + override fun doMeasure(): Double? { + + val answer = sendAndWait(query, timeout()) { phrase -> phrase.startsWith(base) } + + if (answer.isEmpty()) { + this.updateMessage("No signal") + updateState(PortSensor.CONNECTED_STATE, false) + return null + } else { + val match = response.matcher(answer) + + if (match.matches()) { + val base = Integer.parseInt(match.group(1), 16).toDouble() / 10.0 + var exp = Integer.parseInt(match.group(2), 16) + if (exp > 32766) { + exp = exp - 65536 + } + var res = BigDecimal.valueOf(base * Math.pow(10.0, exp.toDouble())) + res = res.setScale(4, RoundingMode.CEILING) + this.updateMessage("OK") + updateState(PortSensor.CONNECTED_STATE, true) + return res.toDouble() + } else { + this.updateMessage("Wrong answer: " + answer) + updateState(PortSensor.CONNECTED_STATE, false) + return null + } + } + } + + override fun getDevice(): Device { + return this@MeradatVacDevice + } + } + + companion object { + private val REQUEST = "0300000002" + + fun calculateLRC(inputString: String): String { + /* + * String is Hex String, need to convert in ASCII. + */ + val bytes = BigInteger(inputString, 16).toByteArray() + var checksum = 0 + for (aByte in bytes) { + checksum += aByte.toInt() + } + var `val` = Integer.toHexString(-checksum) + `val` = `val`.substring(`val`.length - 2).toUpperCase() + if (`val`.length < 2) { + `val` = "0" + `val` + } + + return `val` + } + } + +} diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDevice.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDevice.kt new file mode 100644 index 00000000..76eb61ef --- /dev/null +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDevice.kt @@ -0,0 +1,179 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package inr.numass.control.readvac + +import hep.dataforge.context.Context +import hep.dataforge.control.RoleDef +import hep.dataforge.control.collectors.RegularPointCollector +import hep.dataforge.control.connections.Roles +import hep.dataforge.control.connections.StorageConnection +import hep.dataforge.control.devices.Device +import hep.dataforge.control.devices.DeviceHub +import hep.dataforge.control.devices.PortSensor.CONNECTED_STATE +import hep.dataforge.control.devices.Sensor +import hep.dataforge.control.devices.StateDef +import hep.dataforge.control.measurements.AbstractMeasurement +import hep.dataforge.control.measurements.Measurement +import hep.dataforge.description.ValueDef +import hep.dataforge.exceptions.ControlException +import hep.dataforge.meta.Meta +import hep.dataforge.names.Name +import hep.dataforge.storage.api.TableLoader +import hep.dataforge.storage.commons.LoaderFactory +import hep.dataforge.tables.TableFormatBuilder +import hep.dataforge.tables.ValueMap +import hep.dataforge.utils.DateTimeUtils +import hep.dataforge.values.Value +import hep.dataforge.values.ValueType +import hep.dataforge.values.Values +import inr.numass.control.StorageHelper +import java.time.Duration +import java.time.Instant +import java.util.* +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.ScheduledFuture +import java.util.concurrent.TimeUnit +import java.util.stream.Stream + +/** + * @author [Alexander Nozik](mailto:altavir@gmail.com) + */ +@RoleDef(name = Roles.STORAGE_ROLE, objectType = StorageConnection::class, info = "Storage for acquired points") +@StateDef(value = ValueDef(name = "storing", info = "Define if this device is currently writes to storage"), writable = true) +class VacCollectorDevice : Sensor, DeviceHub { + + private var sensorMap: MutableMap> = LinkedHashMap() + private val helper = StorageHelper(this, this::buildLoader) + + val sensors: Collection> + get() = sensorMap.values + + private val averagingDuration: Duration + get() = Duration.parse(meta().getString("averagingDuration", "PT30S")) + + constructor() {} + + constructor(context: Context, meta: Meta) { + setContext(context) + setMeta(meta) + } + + override fun optDevice(name: Name): Optional { + return Optional.ofNullable(sensorMap.get(name.toString())) + } + + override fun deviceNames(): Stream { + return sensorMap.keys.stream().map { Name.ofSingle(it) } + } + + + fun setSensors(sensors: Iterable>) { + sensorMap = LinkedHashMap() + for (sensor in sensors) { + sensorMap.put(sensor.name, sensor) + } + } + + fun setSensors(vararg sensors: Sensor) { + setSensors(Arrays.asList(*sensors)) + } + + @Throws(ControlException::class) + override fun init() { + super.init() + for (s in sensorMap.values) { + s.init() + } + } + + override fun createMeasurement(): Measurement { + //TODO use meta + return VacuumMeasurement() + } + + override fun getType(): String { + return "Numass vacuum" + } + + + @Throws(ControlException::class) + override fun shutdown() { + super.shutdown() + helper.close() + for (sensor in sensors) { + sensor.shutdown() + } + } + + private fun buildLoader(connection: StorageConnection): TableLoader { + val format = TableFormatBuilder().setType("timestamp", ValueType.TIME) + sensors.forEach { s -> format.setType(s.name, ValueType.NUMBER) } + + val suffix = DateTimeUtils.fileSuffix() + + return LoaderFactory.buildPointLoder(connection.storage, "vactms_" + suffix, "", "timestamp", format.build()) + } + + private inner class VacuumMeasurement : AbstractMeasurement() { + + private val collector = RegularPointCollector(averagingDuration) { this.result(it) } + private var executor: ScheduledExecutorService? = null + private var currentTask: ScheduledFuture<*>? = null + + override fun getDevice(): Device { + return this@VacCollectorDevice + } + + + override fun start() { + executor = Executors.newSingleThreadScheduledExecutor { r: Runnable -> Thread(r, "VacuumMeasurement thread") } + val delay = meta().getInt("delay", 5)!! * 1000 + currentTask = executor!!.scheduleWithFixedDelay({ + sensorMap.values.forEach { sensor -> + try { + val value: Any? + value = if (sensor.optBooleanState(CONNECTED_STATE).orElse(false)) { + sensor.read() + } else { + null + } + collector.put(sensor.name, value) + } catch (ex: Exception) { + collector.put(sensor.name, Value.NULL) + } + } + }, 0, delay.toLong(), TimeUnit.MILLISECONDS) + } + + + @Synchronized override fun result(result: Values, time: Instant) { + super.result(result, time) + helper.push(result) + } + + private fun terminator(): Values { + val p = ValueMap.Builder() + p.putValue("timestamp", DateTimeUtils.now()) + sensorMap.keys.forEach { n -> p.putValue(n, null) } + return p.build() + } + + override fun stop(force: Boolean): Boolean { + val isRunning = currentTask != null + if (isRunning) { + logger.debug("Stopping vacuum collector measurement. Writing terminator point") + result(terminator()) + currentTask!!.cancel(force) + executor!!.shutdown() + currentTask = null + afterStop() + } + return isRunning + } + } + +}