Numass devices uodate
This commit is contained in:
parent
f387302388
commit
b15ffe6bda
@ -7,13 +7,12 @@ import tornadofx.*
|
|||||||
* Created by darksnake on 19-May-17.
|
* Created by darksnake on 19-May-17.
|
||||||
*/
|
*/
|
||||||
class ServerApp : App(BoardView::class) {
|
class ServerApp : App(BoardView::class) {
|
||||||
val controller: BoardController by inject();
|
private val controller: BoardController by inject();
|
||||||
|
|
||||||
|
|
||||||
override fun start(stage: Stage) {
|
override fun start(stage: Stage) {
|
||||||
controller.load(this)
|
controller.load(this)
|
||||||
super.start(stage)
|
super.start(stage)
|
||||||
setDFStageIcon(stage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stop() {
|
override fun stop() {
|
||||||
|
@ -6,13 +6,12 @@ import hep.dataforge.exceptions.StorageException
|
|||||||
import hep.dataforge.storage.api.TableLoader
|
import hep.dataforge.storage.api.TableLoader
|
||||||
import hep.dataforge.values.Values
|
import hep.dataforge.values.Values
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Function
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper to store points in multiple loaders
|
* A helper to store points in multiple loaders
|
||||||
* Created by darksnake on 16-May-17.
|
* Created by darksnake on 16-May-17.
|
||||||
*/
|
*/
|
||||||
class StorageHelper(private val device: AbstractDevice, private val loaderFactory: Function<StorageConnection, TableLoader>) : AutoCloseable {
|
class StorageHelper(private val device: AbstractDevice, private val loaderFactory: (StorageConnection)-> TableLoader) : AutoCloseable {
|
||||||
private val loaderMap = HashMap<StorageConnection, TableLoader>()
|
private val loaderMap = HashMap<StorageConnection, TableLoader>()
|
||||||
|
|
||||||
fun push(point: Values) {
|
fun push(point: Values) {
|
||||||
|
@ -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<Double> {
|
|
||||||
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<Double> createMeasurement() {
|
|
||||||
return new CMVacMeasurement();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getType() {
|
|
||||||
return meta().getString("type", "Leibold CM32");
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CMVacMeasurement extends SimpleMeasurement<Double> {
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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<Double> sensor = (Sensor<Double>) 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<Double> {
|
|
||||||
|
|
||||||
public MKSBaratronDevice() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public MKSBaratronDevice(Context context, Meta meta) {
|
|
||||||
setContext(context);
|
|
||||||
setMeta(meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Measurement<Double> 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<Double> {
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<Double> {
|
|
||||||
|
|
||||||
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<Double> 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<Double> {
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<Double> {
|
|
||||||
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<Double> 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<Double> {
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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 <a href="mailto:altavir@gmail.com">Alexander Nozik</a>
|
|
||||||
*/
|
|
||||||
@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<Values> {
|
|
||||||
|
|
||||||
private Map<String, Sensor<Double>> 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<Sensor<Double>> sensors) {
|
|
||||||
sensorMap = new LinkedHashMap<>();
|
|
||||||
for (Sensor<Double> sensor : sensors) {
|
|
||||||
sensorMap.put(sensor.getName(), sensor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSensors(Sensor<Double>... sensors) {
|
|
||||||
setSensors(Arrays.asList(sensors));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init() throws ControlException {
|
|
||||||
super.init();
|
|
||||||
for (Sensor<Double> s : sensorMap.values()) {
|
|
||||||
s.init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO add dot path notation for states
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Measurement<Values> 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<Sensor<Double>> getSensors() {
|
|
||||||
return sensorMap.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Duration getAveragingDuration() {
|
|
||||||
return Duration.parse(meta().getString("averagingDuration", "PT30S"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private class VacuumMeasurement extends AbstractMeasurement<Values> {
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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<Double> {
|
||||||
|
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<Double> {
|
||||||
|
return CMVacMeasurement()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getType(): String {
|
||||||
|
return meta().getString("type", "Leibold CM32")
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class CMVacMeasurement : SimpleMeasurement<Double>() {
|
||||||
|
|
||||||
|
@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"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<String>) {
|
||||||
|
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<Double>
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<Double> {
|
||||||
|
|
||||||
|
private val channel: Int
|
||||||
|
get() = meta().getInt("channel", 2)!!
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
constructor(context: Context, meta: Meta) {
|
||||||
|
setContext(context)
|
||||||
|
setMeta(meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun createMeasurement(): Measurement<Double> {
|
||||||
|
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<Double>() {
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -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<Double> {
|
||||||
|
|
||||||
|
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<Double> {
|
||||||
|
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<Double>() {
|
||||||
|
|
||||||
|
@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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<Double> {
|
||||||
|
|
||||||
|
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<Double> {
|
||||||
|
return MeradatMeasurement()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getType(): String {
|
||||||
|
return meta().getString("type", "Vit vacuumeter")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private inner class MeradatMeasurement : SimpleMeasurement<Double>() {
|
||||||
|
|
||||||
|
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`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<Values>, DeviceHub {
|
||||||
|
|
||||||
|
private var sensorMap: MutableMap<String, Sensor<Double>> = LinkedHashMap()
|
||||||
|
private val helper = StorageHelper(this, this::buildLoader)
|
||||||
|
|
||||||
|
val sensors: Collection<Sensor<Double>>
|
||||||
|
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<Device> {
|
||||||
|
return Optional.ofNullable(sensorMap.get(name.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deviceNames(): Stream<Name> {
|
||||||
|
return sensorMap.keys.stream().map { Name.ofSingle(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun setSensors(sensors: Iterable<Sensor<Double>>) {
|
||||||
|
sensorMap = LinkedHashMap()
|
||||||
|
for (sensor in sensors) {
|
||||||
|
sensorMap.put(sensor.name, sensor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSensors(vararg sensors: Sensor<Double>) {
|
||||||
|
setSensors(Arrays.asList(*sensors))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(ControlException::class)
|
||||||
|
override fun init() {
|
||||||
|
super.init()
|
||||||
|
for (s in sensorMap.values) {
|
||||||
|
s.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createMeasurement(): Measurement<Values> {
|
||||||
|
//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<Values>() {
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user