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.
|
||||
*/
|
||||
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() {
|
||||
|
@ -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<StorageConnection, TableLoader>) : AutoCloseable {
|
||||
class StorageHelper(private val device: AbstractDevice, private val loaderFactory: (StorageConnection)-> TableLoader) : AutoCloseable {
|
||||
private val loaderMap = HashMap<StorageConnection, TableLoader>()
|
||||
|
||||
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