Some fixes to numass devices
This commit is contained in:
parent
b070156c51
commit
8ecdd75066
@ -7,6 +7,12 @@ allprojects{
|
||||
javaParameters = true
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
experimental {
|
||||
coroutines "enable"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -44,6 +44,14 @@ task debugWithDevice(dependsOn: classes, type: JavaExec) {
|
||||
group "debug"
|
||||
}
|
||||
|
||||
task runWithDevice(dependsOn: classes, type: JavaExec) {
|
||||
main mainClass
|
||||
args = ["--config.resource=/config/control-real.xml"]
|
||||
classpath = sourceSets.main.runtimeClasspath + configurations.devices
|
||||
description "Start application in debug mode"
|
||||
group "debug"
|
||||
}
|
||||
|
||||
task startScriptWithDevices(type: CreateStartScripts) {
|
||||
applicationName = "control-room-devices"
|
||||
mainClassName = mainClass
|
||||
|
@ -0,0 +1,38 @@
|
||||
<config>
|
||||
<server port="8337"/>
|
||||
<!--<storage path="/home/numass-storage/" type="numass"/>-->
|
||||
<numass run="2017_11"/>
|
||||
<device type="numass.pkt8" name="thermo-1" averagingDuration ="PT60S" port = "192.168.111.36:4001" abuf ="1" >
|
||||
<channel designation="a" name="T1" r0="1000" transformationType="hyperbolic" coefs="[17.066, -249.29, 1663.6, -5263.8, 9004.7, -7151.8, 2288.3]"/>
|
||||
<channel designation="b" name="T2" r0="1000" transformationType="hyperbolic" coefs="[73.202, -841.5, 3995.9, -9579.1, 12361.0, -7976.5, 2161.3]"/>
|
||||
<channel designation="c" name="T3" r0="1000" transformationType="hyperbolic" coefs="[52.402, -579.55, 2665.5, -6110.0, 7501.9, -4553.5, 1197.1]"/>
|
||||
<channel designation="d" name="T4" visible="false" r0="1000" transformationType="hyperbolic" coefs="[17.136, -249.09, 1667.5, -5316.4, 9226.7, -7515.6, 2382.4]"/>
|
||||
<channel designation="e" name="T5" visible="false" r0="1000" transformationType="hyperbolic" coefs="[58.060, -664.38, 3158.3, -7531.0, 9616.3, -6029.3, 1589.6]"/>
|
||||
<channel designation="g" name="T7" r0="1000" transformationType="hyperbolic" coefs="[71.984, -822.09, 3872.0, -9183.0, 11729.0, -7518.6, 2034.2]"/>
|
||||
<channel designation="h" name="T8" visible ="false" r0="1000" transformationType="hyperbolic" coefs="[23.894, -358.42, 2355.2, -7509.8, 12893.0, -10454.0, 3403.9]"/>
|
||||
<plotConfig maxItems="5000" thickness="3"/>
|
||||
</device>
|
||||
<!-- <device type="numass.msp" name="msp">
|
||||
<connection ip="192.168.111.11" port="10014"/>
|
||||
<peakJump>
|
||||
<peak mass="2" title = "H2" color="black" thickness="4"/>
|
||||
<peak mass="3" title = "T/HD" visible = "false"/>
|
||||
<peak mass="4" title = "D2/TH" color="blue"/>
|
||||
<peak mass="5" title = "TD" color="green" thickness="4"/>
|
||||
<peak mass="6" title = "T2" color="red" thickness="4"/>
|
||||
<peak mass="12" title = "C" visible = "false"/>
|
||||
<peak mass="14" title = "N" visible = "false"/>
|
||||
<peak mass="18" title = "H2O" color = "cyan" visible = "false"/>
|
||||
<peak mass="28" title = "N2" color = "magenta"/>
|
||||
<peak mass="32" title = "O2" color = "orange" visible = "false"/>
|
||||
</peakJump>
|
||||
</device>
|
||||
<device type="numass.vac">
|
||||
<sensor name="P1" color="red" port="tcp::192.168.111.33:4002" sensorType="mks"/>
|
||||
<sensor name="P2" color="blue" port="tcp::192.168.111.32:4002" sensorType="CM32"/>
|
||||
<sensor name="P3" color="green" port="tcp::192.168.111.32:4003" sensorType="CM32"/>
|
||||
<sensor name="Px" color="black" port="tcp::192.168.111.33:4003" sensorType="meradat" address="1"/>
|
||||
<sensor name="Baratron" color="orange" port="tcp::192.168.111.33:4004" sensorType="baratron"/>
|
||||
<!–<sensor name="Collector" color="magenta" port="tcp::192.168.111.33:4003" sensorType="meradat" address="5"/>–>
|
||||
</device>-->
|
||||
</config>
|
@ -1,496 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Alexander Nozik.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package inr.numass.control.magnet;
|
||||
|
||||
import hep.dataforge.context.Context;
|
||||
import hep.dataforge.control.devices.AbstractDevice;
|
||||
import hep.dataforge.control.devices.StateDef;
|
||||
import hep.dataforge.control.ports.GenericPortController;
|
||||
import hep.dataforge.control.ports.PortFactory;
|
||||
import hep.dataforge.control.ports.PortTimeoutException;
|
||||
import hep.dataforge.description.ValueDef;
|
||||
import hep.dataforge.exceptions.ControlException;
|
||||
import hep.dataforge.exceptions.PortException;
|
||||
import hep.dataforge.meta.Meta;
|
||||
import hep.dataforge.utils.DateTimeUtils;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static hep.dataforge.values.ValueType.*;
|
||||
|
||||
/**
|
||||
* @author Polina
|
||||
*/
|
||||
@ValueDef(name = "timeout", type = {NUMBER}, def = "400", info = "A timeout for port response")
|
||||
@StateDef(value = @ValueDef(name = "output", type = BOOLEAN, info = "Weather output on or off"), writable = true)
|
||||
@StateDef(value = @ValueDef(name = "current", type = NUMBER, info = "Current current"))
|
||||
@StateDef(value = @ValueDef(name = "voltage", type = NUMBER, info = "Current voltage"))
|
||||
@StateDef(value = @ValueDef(name = "targetCurrent", type = NUMBER, info = "Target current"), writable = true)
|
||||
@StateDef(value = @ValueDef(name = "targetVoltage", type = NUMBER, info = "Target voltage"), writable = true)
|
||||
@StateDef(value = @ValueDef(name = "lastUpdate", type = TIME, info = "Time of the last update"), writable = true)
|
||||
public class LambdaMagnet extends AbstractDevice {
|
||||
|
||||
private static final DecimalFormat LAMBDA_FORMAT = new DecimalFormat("###.##");
|
||||
public static double CURRENT_PRECISION = 0.05;
|
||||
// public static double CURRENT_STEP = 0.05;
|
||||
public static int DEFAULT_DELAY = 1;
|
||||
public static int DEFAULT_MONITOR_DELAY = 2000;
|
||||
public static double MAX_STEP_SIZE = 0.2;
|
||||
public static double MIN_UP_STEP_SIZE = 0.005;
|
||||
public static double MIN_DOWN_STEP_SIZE = 0.05;
|
||||
public static double MAX_SPEED = 5d; // 5 A per minute
|
||||
|
||||
private boolean closePortOnShutDown = false;
|
||||
|
||||
private final String name;
|
||||
private final int address;
|
||||
private final ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
|
||||
|
||||
protected MagnetStateListener listener;
|
||||
// private volatile double current = 0;
|
||||
private final Duration timeout;
|
||||
// private Future monitorTask;
|
||||
// private Future updateTask;
|
||||
// private Instant lastUpdate = null;
|
||||
|
||||
private double speed = MAX_SPEED;
|
||||
|
||||
private final GenericPortController controller;
|
||||
|
||||
|
||||
/**
|
||||
* A setup for single magnet controller
|
||||
*
|
||||
* @param context
|
||||
* @param meta
|
||||
* @throws ControlException
|
||||
*/
|
||||
public LambdaMagnet(Context context, Meta meta) throws ControlException {
|
||||
this(context, meta, new GenericPortController(context, PortFactory.getPort(meta.getString("port"))));
|
||||
closePortOnShutDown = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize magnet device with given port controller
|
||||
*
|
||||
* @param context
|
||||
* @param meta
|
||||
* @param controller
|
||||
*/
|
||||
public LambdaMagnet(Context context, Meta meta, GenericPortController controller) {
|
||||
super(context, meta);
|
||||
this.controller = controller;
|
||||
name = meta.getString("name", "LAMBDA");
|
||||
address = meta.getInt("address", 1);
|
||||
timeout = meta.optString("timeout").map(Duration::parse).orElse(Duration.ofMillis(200));
|
||||
}
|
||||
|
||||
// /**
|
||||
// * This method creates an element of class MegnetController with exact
|
||||
// * parameters. If you have two parameters for your method - the next
|
||||
// * constructor will be used.
|
||||
// *
|
||||
// * @param name
|
||||
// * @param port number of COM-port on your computer that you want to use
|
||||
// * @param address number of TDK - Lambda
|
||||
// * @param timeout waiting time for response
|
||||
// */
|
||||
// public LambdaMagnet(String name, Port port, int address, int timeout) {
|
||||
// this.name = name;
|
||||
// this.port = port;
|
||||
// this.port.setDelimiter("\r");//PENDING меняем состояние внешнего объекта?
|
||||
// this.address = address;
|
||||
// this.timeout = Duration.ofMillis(timeout);
|
||||
// }
|
||||
//
|
||||
// public LambdaMagnet(Port port, int address, int timeout) {
|
||||
// this(null, port, address, timeout);
|
||||
// }
|
||||
//
|
||||
// public LambdaMagnet(Port port, int address) {
|
||||
// this(null, port, address);
|
||||
// }
|
||||
//
|
||||
// public LambdaMagnet(String name, Port port, int address) {
|
||||
// this(name, port, address, 300);
|
||||
// }
|
||||
|
||||
|
||||
@Override
|
||||
public void init() throws ControlException {
|
||||
super.init();
|
||||
controller.open();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws ControlException {
|
||||
super.shutdown();
|
||||
try {
|
||||
controller.close();
|
||||
if (closePortOnShutDown) {
|
||||
controller.getPort().close();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new ControlException("Failed to close the port", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method converts double to LAMBDA string
|
||||
*
|
||||
* @param d double that should be converted to string
|
||||
* @return string
|
||||
*/
|
||||
private static String d2s(double d) {
|
||||
return LAMBDA_FORMAT.format(d);
|
||||
}
|
||||
|
||||
public void setListener(MagnetStateListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
// public double getMeasuredI() {
|
||||
// return current;
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// public void acceptPhrase(String message) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void reportError(String errorMessage, Throwable error) {
|
||||
// if (this.listener != null) {
|
||||
// listener.error(getName(), errorMessage, error);
|
||||
// } else {
|
||||
// LoggerFactory.getLogger(getClass()).error(errorMessage, error);
|
||||
// }
|
||||
// }
|
||||
|
||||
private void reportError(String errorMessage, Throwable error) {
|
||||
if (this.listener != null) {
|
||||
listener.error(getName(), errorMessage, error);
|
||||
} else {
|
||||
LoggerFactory.getLogger(getClass()).error(errorMessage, error);
|
||||
}
|
||||
}
|
||||
|
||||
private String talk(String request) throws PortException {
|
||||
try {
|
||||
controller.send(request + "\r");
|
||||
return controller.waitFor(timeout).trim();
|
||||
} catch (PortTimeoutException tex) {
|
||||
//Single retry on timeout
|
||||
LoggerFactory.getLogger(getClass()).warn("A timeout exception for request '" + request + "'. Making another atempt.");
|
||||
controller.send(request + "\r");
|
||||
return controller.waitFor(timeout).trim();
|
||||
}
|
||||
}
|
||||
|
||||
private String getParameter(String name) throws PortException {
|
||||
String res = talk(name + "?");
|
||||
return res;
|
||||
}
|
||||
|
||||
private boolean setParameter(String name, String state) throws PortException {
|
||||
String res = talk(name + " " + state);
|
||||
return "OK".equals(res);
|
||||
}
|
||||
|
||||
private boolean setParameter(String name, int state) throws PortException {
|
||||
String res = talk(name + " " + state);
|
||||
return "OK".equals(res);
|
||||
}
|
||||
|
||||
private boolean setParameter(String name, double state) throws PortException {
|
||||
String res = talk(name + " " + d2s(state));
|
||||
return "OK".equals(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract number from LAMBDA response
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
private double s2d(String str) {
|
||||
return Double.valueOf(str);
|
||||
}
|
||||
|
||||
private double getCurrent() throws PortException {
|
||||
if (!setADR()) {
|
||||
if (listener != null) {
|
||||
listener.error(getName(), "Can't set address", null);
|
||||
}
|
||||
throw new PortException("Can't set address");
|
||||
}
|
||||
return s2d(getParameter("MC"));
|
||||
}
|
||||
|
||||
protected void setCurrent(double current) throws PortException {
|
||||
if (!setParameter("PC", current)) {
|
||||
reportError("Can't set the current", null);
|
||||
} else {
|
||||
lastUpdate = DateTimeUtils.now();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean setADR() throws PortException {
|
||||
if (setParameter("ADR", getAddress())) {
|
||||
if (listener != null) {
|
||||
listener.addressChanged(getName(), address);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets status of magnet for current moment
|
||||
*
|
||||
* @return status of magnet
|
||||
*/
|
||||
private MagnetStatus getStatus() throws PortException {
|
||||
if (!setADR()) {
|
||||
return MagnetStatus.off();
|
||||
}
|
||||
|
||||
boolean out;
|
||||
|
||||
out = "ON".equals(talk("OUT?"));
|
||||
|
||||
double measuredCurrent = s2d(getParameter("MC"));
|
||||
this.current = measuredCurrent;
|
||||
double setCurrent = s2d(getParameter("PC"));
|
||||
double measuredVoltage = s2d(getParameter("MV"));
|
||||
double setVoltage = s2d(getParameter("PV"));
|
||||
|
||||
MagnetStatus monitor = new MagnetStatus(out, measuredCurrent, setCurrent, measuredVoltage, setVoltage);
|
||||
|
||||
if (listener != null) {
|
||||
listener.acceptStatus(getName(), monitor);
|
||||
}
|
||||
return monitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel current update task
|
||||
*/
|
||||
public void stopUpdateTask() {
|
||||
if (updateTask != null) {
|
||||
updateTask.cancel(false);
|
||||
lastUpdate = null;
|
||||
if (listener != null) {
|
||||
listener.updateTaskStateChanged(getName(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startUpdateTask(double targetI) {
|
||||
startUpdateTask(targetI, DEFAULT_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start recursive updates of current with given delays between updates. If
|
||||
* delay is 0 then updates are made immediately.
|
||||
*
|
||||
* @param targetI
|
||||
* @param delay
|
||||
*/
|
||||
public void startUpdateTask(double targetI, int delay) {
|
||||
assert delay > 0;
|
||||
stopUpdateTask();
|
||||
Runnable call = () -> {
|
||||
try {
|
||||
double measuredI = getCurrent();
|
||||
this.current = measuredI;
|
||||
|
||||
if (listener != null) {
|
||||
listener.acceptMeasuredI(getName(), measuredI);
|
||||
|
||||
}
|
||||
|
||||
if (Math.abs(measuredI - targetI) > CURRENT_PRECISION) {
|
||||
double nextI = nextI(measuredI, targetI);
|
||||
|
||||
if (listener != null) {
|
||||
listener.acceptNextI(getName(), nextI);
|
||||
}
|
||||
setCurrent(nextI);
|
||||
} else {
|
||||
stopUpdateTask();
|
||||
}
|
||||
|
||||
} catch (PortException ex) {
|
||||
reportError("Error in update task", ex);
|
||||
stopUpdateTask();
|
||||
}
|
||||
};
|
||||
|
||||
updateTask = scheduler.scheduleWithFixedDelay(call, 0, delay, TimeUnit.MILLISECONDS);
|
||||
if (listener != null) {
|
||||
listener.updateTaskStateChanged(getName(), true);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOutputMode(boolean out) throws PortException {
|
||||
if (!setADR()) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
int outState;
|
||||
if (out) {
|
||||
outState = 1;
|
||||
} else {
|
||||
outState = 0;
|
||||
}
|
||||
if (!setParameter("OUT", outState)) {
|
||||
if (listener != null) {
|
||||
listener.error(getName(), "Can't set output mode", null);
|
||||
}
|
||||
} else if (listener != null) {
|
||||
listener.outputModeChanged(getName(), out);
|
||||
}
|
||||
}
|
||||
|
||||
private double nextI(double measuredI, double targetI) {
|
||||
assert measuredI != targetI;
|
||||
|
||||
double step;
|
||||
if (lastUpdate == null) {
|
||||
step = MIN_UP_STEP_SIZE;
|
||||
} else {
|
||||
//Choose optimal speed but do not exceed maximum speed
|
||||
step = Math.min(MAX_STEP_SIZE,
|
||||
(double) lastUpdate.until(DateTimeUtils.now(), ChronoUnit.MILLIS) / 60000d * getSpeed());
|
||||
}
|
||||
|
||||
double res;
|
||||
if (targetI > measuredI) {
|
||||
step = Math.max(MIN_UP_STEP_SIZE, step);
|
||||
res = Math.min(targetI, measuredI + step);
|
||||
} else {
|
||||
step = Math.max(MIN_DOWN_STEP_SIZE, step);
|
||||
res = Math.max(targetI, measuredI - step);
|
||||
}
|
||||
|
||||
// не вводится ток меньше 0.5
|
||||
if (res < 0.5 && targetI > CURRENT_PRECISION) {
|
||||
return 0.5;
|
||||
} else if (res < 0.5 && targetI < CURRENT_PRECISION) {
|
||||
return 0;
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel current monitoring task
|
||||
*/
|
||||
public void stopMonitorTask() {
|
||||
if (monitorTask != null) {
|
||||
monitorTask.cancel(true);
|
||||
if (listener != null) {
|
||||
listener.monitorTaskStateChanged(getName(), false);
|
||||
}
|
||||
monitorTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if (this.name == null || this.name.isEmpty()) {
|
||||
return "LAMBDA " + getAddress();
|
||||
} else {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
public void startMonitorTask() {
|
||||
startMonitorTask(DEFAULT_MONITOR_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start monitoring task which checks for magnet status and then waits for
|
||||
* fixed time.
|
||||
*
|
||||
* @param delay an interval between scans in milliseconds
|
||||
*/
|
||||
public void startMonitorTask(int delay) {
|
||||
assert delay >= 1000;
|
||||
stopMonitorTask();
|
||||
|
||||
Runnable call = () -> {
|
||||
try {
|
||||
getStatus();
|
||||
} catch (PortException ex) {
|
||||
reportError("Port connection exception during status measurement", ex);
|
||||
stopMonitorTask();
|
||||
}
|
||||
};
|
||||
|
||||
monitorTask = scheduler.scheduleWithFixedDelay(call, 0, delay, TimeUnit.MILLISECONDS);
|
||||
|
||||
if (listener != null) {
|
||||
listener.monitorTaskStateChanged(getName(), true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String request(String message) {
|
||||
try {
|
||||
if (!setADR()) {
|
||||
throw new RuntimeException("F")
|
||||
}
|
||||
return talk(message);
|
||||
} catch (PortException ex) {
|
||||
reportError("Can not send message to the port", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the address
|
||||
*/
|
||||
public int getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current change speed in Amper per minute
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public double getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current change speed in Amper per minute
|
||||
*
|
||||
* @param speed
|
||||
*/
|
||||
public void setSpeed(double speed) {
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Alexander Nozik.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package inr.numass.control.magnet;
|
||||
|
||||
import hep.dataforge.control.ports.Port;
|
||||
import hep.dataforge.exceptions.PortException;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Polina
|
||||
*/
|
||||
public class SafeLambdaMagnet extends LambdaMagnet {
|
||||
|
||||
private final Set<SafeMagnetCondition> safeConditions = new HashSet<>();
|
||||
|
||||
public SafeLambdaMagnet(String name, Port port, int address, int timeout, SafeMagnetCondition... safeConditions) {
|
||||
super(name, port, address, timeout);
|
||||
this.safeConditions.addAll(Arrays.asList(safeConditions));
|
||||
}
|
||||
|
||||
public SafeLambdaMagnet(String name, Port port, int address, SafeMagnetCondition... safeConditions) {
|
||||
super(name, port, address);
|
||||
this.safeConditions.addAll(Arrays.asList(safeConditions));
|
||||
}
|
||||
|
||||
public void addSafeCondition(Predicate<Double> condition, boolean isBlocking){
|
||||
this.safeConditions.add(new SafeMagnetCondition() {
|
||||
|
||||
@Override
|
||||
public boolean isSafe(int address, double current) {
|
||||
return condition.test(current);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBloking() {
|
||||
return isBlocking;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add symmetric non-blocking conditions to ensure currents in two magnets have difference within given tolerance.
|
||||
* @param controller
|
||||
* @param tolerance
|
||||
*/
|
||||
public void bindTo(SafeLambdaMagnet controller, double tolerance){
|
||||
this.addSafeCondition((I)->Math.abs(controller.getMeasuredI() - I) <= tolerance, false);
|
||||
controller.addSafeCondition((I)->Math.abs(this.getMeasuredI() - I) <= tolerance, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setCurrent(double current) throws PortException {
|
||||
for (SafeMagnetCondition condition : safeConditions) {
|
||||
if (!condition.isSafe(getAddress(), current)) {
|
||||
if (condition.isBloking()) {
|
||||
condition.onFail();
|
||||
throw new RuntimeException("Can't set current. Condition not satisfied.");
|
||||
} else {
|
||||
if(listener!= null){
|
||||
listener.displayState("BOUND");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.setCurrent(current);
|
||||
}
|
||||
|
||||
public interface SafeMagnetCondition {
|
||||
|
||||
boolean isSafe(int address, double current);
|
||||
|
||||
default boolean isBloking() {
|
||||
return true;
|
||||
}
|
||||
|
||||
default void onFail() {
|
||||
LoggerFactory.getLogger(getClass()).error("Can't set current. Condition not satisfied.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -52,7 +52,7 @@ public class TestSynch {
|
||||
new SafeLambdaMagnet.SafeMagnetCondition() {
|
||||
|
||||
// @Override
|
||||
// public boolean isBloking() {
|
||||
// public boolean isBlocking() {
|
||||
// return false;
|
||||
// }
|
||||
@Override
|
||||
|
@ -1,187 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Alexander Nozik.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package inr.numass.control.magnet;
|
||||
|
||||
import hep.dataforge.control.ports.VirtualPort;
|
||||
import hep.dataforge.exceptions.PortException;
|
||||
import hep.dataforge.meta.Meta;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
public class VirtualLambdaPort extends VirtualPort {
|
||||
|
||||
private static final Duration latency = Duration.ofMillis(50);
|
||||
|
||||
private volatile int currentAddress = -1;
|
||||
private Map<Integer, VirtualMagnetStatus> magnets = new HashMap<>();
|
||||
private final String virtualPortName;
|
||||
|
||||
public VirtualLambdaPort(String portName, Map<Integer, Double> magnets) {
|
||||
this.virtualPortName = portName;
|
||||
magnets.entrySet().stream().forEach((entry) -> {
|
||||
this.magnets.put(entry.getKey(), new VirtualMagnetStatus(entry.getValue()));
|
||||
});
|
||||
}
|
||||
|
||||
public VirtualLambdaPort(String portName, int... magnets) {
|
||||
this.virtualPortName = portName;
|
||||
for (int magnet : magnets) {
|
||||
this.magnets.put(magnet, new VirtualMagnetStatus(0.01));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return virtualPortName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void evaluateRequest(String request) {
|
||||
String comand;
|
||||
String value = "";
|
||||
String[] split = request.split(" ");
|
||||
if (split.length == 1) {
|
||||
comand = request;
|
||||
} else {
|
||||
comand = split[0];
|
||||
value = split[1];
|
||||
}
|
||||
try {
|
||||
evaluateRequest(comand.trim(), value.trim());
|
||||
} catch (RuntimeException ex) {
|
||||
|
||||
receivePhrase("FAIL");//TODO какая команда правильная?
|
||||
LoggerFactory.getLogger(getClass()).error("Request evaluation failure", ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void sendOK() {
|
||||
planResponse("OK", latency);
|
||||
}
|
||||
|
||||
private void evaluateRequest(String comand, String value) {
|
||||
switch (comand) {
|
||||
case "ADR":
|
||||
int address = Integer.parseInt(value);
|
||||
if (magnets.containsKey(address)) {
|
||||
currentAddress = address;
|
||||
sendOK();
|
||||
}
|
||||
return;
|
||||
case "ADR?":
|
||||
planResponse(Integer.toString(currentAddress), latency);
|
||||
return;
|
||||
case "OUT":
|
||||
int state = Integer.parseInt(value);
|
||||
currentMagnet().out = (state == 1);
|
||||
sendOK();
|
||||
return;
|
||||
case "OUT?":
|
||||
boolean out = currentMagnet().out;
|
||||
if (out) {
|
||||
planResponse("ON", latency);
|
||||
} else {
|
||||
planResponse("OFF", latency);
|
||||
}
|
||||
return;
|
||||
case "PC":
|
||||
double current = Double.parseDouble(value);
|
||||
if (current < 0.5) {
|
||||
current = 0;
|
||||
}
|
||||
currentMagnet().current = current;
|
||||
sendOK();
|
||||
return;
|
||||
case "PC?":
|
||||
planResponse(Double.toString(currentMagnet().current), latency);
|
||||
return;
|
||||
case "MC?":
|
||||
planResponse(Double.toString(currentMagnet().current), latency);
|
||||
return;
|
||||
case "PV?":
|
||||
planResponse(Double.toString(currentMagnet().voltage()), latency);
|
||||
return;
|
||||
case "MV?":
|
||||
planResponse(Double.toString(currentMagnet().voltage()), latency);
|
||||
return;
|
||||
default:
|
||||
LoggerFactory.getLogger(getClass()).warn("Unknown comand {}", comand);
|
||||
}
|
||||
}
|
||||
|
||||
private VirtualMagnetStatus currentMagnet() {
|
||||
if (currentAddress < 0) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
return magnets.get(currentAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() throws PortException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Meta meta() {
|
||||
return Meta.buildEmpty("virtualPort");
|
||||
|
||||
}
|
||||
|
||||
private class VirtualMagnetStatus {
|
||||
|
||||
public VirtualMagnetStatus(double resistance) {
|
||||
this.resistance = resistance;
|
||||
this.on = true;
|
||||
this.out = false;
|
||||
this.current = 0;
|
||||
}
|
||||
|
||||
public VirtualMagnetStatus(double resistance, boolean on, boolean out, double current) {
|
||||
this.resistance = resistance;
|
||||
this.on = on;
|
||||
this.out = out;
|
||||
this.current = current;
|
||||
}
|
||||
|
||||
private final double resistance;
|
||||
private boolean on;
|
||||
private boolean out;
|
||||
private double current;
|
||||
|
||||
public double voltage() {
|
||||
return current * resistance;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package inr.numass.control.magnet
|
||||
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.control.devices.AbstractDevice
|
||||
import hep.dataforge.control.devices.Device
|
||||
import hep.dataforge.control.devices.DeviceHub
|
||||
import hep.dataforge.control.devices.StateDef
|
||||
import hep.dataforge.control.ports.PortFactory
|
||||
import hep.dataforge.description.ValueDef
|
||||
import hep.dataforge.kodex.useEachMeta
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.values.ValueType
|
||||
import java.util.*
|
||||
import java.util.stream.Stream
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
@StateDef(value = ValueDef(name = "address", type = arrayOf(ValueType.NUMBER), info = "Current active magnet"))
|
||||
class LambdaHub(context: Context, meta: Meta) : DeviceHub, AbstractDevice(context, meta) {
|
||||
|
||||
private val magnets = ArrayList<LambdaMagnet>();
|
||||
|
||||
private val port = PortFactory.getPort(meta.getString("port"))
|
||||
private val controller = LambdaPortController(context, port)
|
||||
|
||||
init {
|
||||
meta.useEachMeta("magnet") {
|
||||
magnets.add(LambdaMagnet(context, it, controller))
|
||||
}
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
controller.open()
|
||||
}
|
||||
|
||||
override fun shutdown() {
|
||||
super.shutdown()
|
||||
controller.close()
|
||||
port.close()
|
||||
}
|
||||
|
||||
override fun optDevice(name: Name): Optional<Device> {
|
||||
return magnets.stream().filter { it.name == name.toUnescaped() }.map { it as Device }.findFirst()
|
||||
}
|
||||
|
||||
override fun deviceNames(): Stream<Name> {
|
||||
return magnets.stream().map { Name.ofSingle(it.name) }
|
||||
}
|
||||
}
|
@ -0,0 +1,434 @@
|
||||
/*
|
||||
* Copyright 2015 Alexander Nozik.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package inr.numass.control.magnet
|
||||
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.control.devices.AbstractDevice
|
||||
import hep.dataforge.control.devices.StateDef
|
||||
import hep.dataforge.control.devices.StateDefs
|
||||
import hep.dataforge.control.ports.PortFactory
|
||||
import hep.dataforge.control.ports.PortTimeoutException
|
||||
import hep.dataforge.description.ValueDef
|
||||
import hep.dataforge.exceptions.ControlException
|
||||
import hep.dataforge.exceptions.PortException
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.utils.DateTimeUtils
|
||||
import hep.dataforge.values.ValueType.*
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.text.DecimalFormat
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.concurrent.Future
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* @author Polina
|
||||
*/
|
||||
@ValueDef(name = "timeout", type = arrayOf(NUMBER), def = "400", info = "A timeout for port response")
|
||||
@StateDefs(
|
||||
StateDef(value = ValueDef(name = "output", type = arrayOf(BOOLEAN), info = "Weather output on or off"), writable = true),
|
||||
StateDef(value = ValueDef(name = "current", type = arrayOf(NUMBER), info = "Current current")),
|
||||
StateDef(value = ValueDef(name = "voltage", type = arrayOf(NUMBER), info = "Current voltage")),
|
||||
StateDef(value = ValueDef(name = "targetCurrent", type = arrayOf(NUMBER), info = "Target current"), writable = true),
|
||||
StateDef(value = ValueDef(name = "targetVoltage", type = arrayOf(NUMBER), info = "Target voltage"), writable = true),
|
||||
StateDef(value = ValueDef(name = "lastUpdate", type = arrayOf(TIME), info = "Time of the last update"), writable = true),
|
||||
StateDef(value = ValueDef(name = "updating", type = arrayOf(BOOLEAN), info = "Shows if current ramping in progress"), writable = true),
|
||||
StateDef(value = ValueDef(name = "monitoring", type = arrayOf(BOOLEAN), info = "Shows if monitoring task is running"), writable = true)
|
||||
)
|
||||
open class LambdaMagnet(context: Context, meta: Meta, private val controller: LambdaPortController) : AbstractDevice(context, meta) {
|
||||
|
||||
private var closePortOnShutDown = false
|
||||
|
||||
private val name: String? = meta.getString("name", "LAMBDA")
|
||||
/**
|
||||
* @return the address
|
||||
*/
|
||||
val address: Int = meta.getInt("address", 1)!!
|
||||
private val scheduler = ScheduledThreadPoolExecutor(1)
|
||||
|
||||
var listener: MagnetStateListener? = null
|
||||
// private volatile double current = 0;
|
||||
private val timeout: Duration = meta.optString("timeout").map<Duration> { Duration.parse(it) }.orElse(Duration.ofMillis(200))
|
||||
private var monitorTask: Future<*>? = null
|
||||
private var updateTask: Future<*>? = null
|
||||
private var lastUpdate: Instant? = null
|
||||
|
||||
/**
|
||||
* Get current change speed in Amper per minute
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
/**
|
||||
* Set current change speed in Amper per minute
|
||||
*
|
||||
* @param speed
|
||||
*/
|
||||
var speed = MAX_SPEED
|
||||
|
||||
fun getCurrent(): Double = controller.talk(address, timeout) {
|
||||
s2d(getParameter("MC"))
|
||||
}
|
||||
|
||||
protected open fun setCurrent(current: Double) {
|
||||
|
||||
if (!setParameter("PC", current)) {
|
||||
reportError("Can't set the current", null)
|
||||
} else {
|
||||
lastUpdate = DateTimeUtils.now()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets status of magnet for current moment
|
||||
*
|
||||
* @return status of magnet
|
||||
*/
|
||||
private val status: MagnetStatus
|
||||
@Throws(PortException::class)
|
||||
get() {
|
||||
return controller.talk(address, timeout) {
|
||||
val out: Boolean = "ON" == talk("OUT?")
|
||||
|
||||
val measuredCurrent = s2d(getParameter("MC"))
|
||||
updateState("current", measuredCurrent)
|
||||
val setCurrent = s2d(getParameter("PC"))
|
||||
val measuredVoltage = s2d(getParameter("MV"))
|
||||
val setVoltage = s2d(getParameter("PV"))
|
||||
|
||||
MagnetStatus(out, measuredCurrent, setCurrent, measuredVoltage, setVoltage).also {
|
||||
listener?.acceptStatus(getName(), it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A setup for single magnet controller
|
||||
*
|
||||
* @param context
|
||||
* @param meta
|
||||
* @throws ControlException
|
||||
*/
|
||||
@Throws(ControlException::class)
|
||||
constructor(context: Context, meta: Meta) : this(context, meta, LambdaPortController(context, PortFactory.getPort(meta.getString("port")))) {
|
||||
closePortOnShutDown = true
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * This method creates an element of class MegnetController with exact
|
||||
// * parameters. If you have two parameters for your method - the next
|
||||
// * constructor will be used.
|
||||
// *
|
||||
// * @param name
|
||||
// * @param port number of COM-port on your computer that you want to use
|
||||
// * @param address number of TDK - Lambda
|
||||
// * @param timeout waiting time for response
|
||||
// */
|
||||
// public LambdaMagnet(String name, Port port, int address, int timeout) {
|
||||
// this.name = name;
|
||||
// this.port = port;
|
||||
// this.port.setDelimiter("\r");//PENDING меняем состояние внешнего объекта?
|
||||
// this.address = address;
|
||||
// this.timeout = Duration.ofMillis(timeout);
|
||||
// }
|
||||
//
|
||||
// public LambdaMagnet(Port port, int address, int timeout) {
|
||||
// this(null, port, address, timeout);
|
||||
// }
|
||||
//
|
||||
// public LambdaMagnet(Port port, int address) {
|
||||
// this(null, port, address);
|
||||
// }
|
||||
//
|
||||
// public LambdaMagnet(String name, Port port, int address) {
|
||||
// this(name, port, address, 300);
|
||||
// }
|
||||
|
||||
|
||||
@Throws(ControlException::class)
|
||||
override fun init() {
|
||||
super.init()
|
||||
controller.open()
|
||||
}
|
||||
|
||||
@Throws(ControlException::class)
|
||||
override fun shutdown() {
|
||||
super.shutdown()
|
||||
try {
|
||||
if (closePortOnShutDown) {
|
||||
controller.close()
|
||||
controller.port.close()
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
throw ControlException("Failed to close the port", ex)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// public double getMeasuredI() {
|
||||
// return current;
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// public void acceptPhrase(String message) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void reportError(String errorMessage, Throwable error) {
|
||||
// if (this.listener != null) {
|
||||
// listener.error(getName(), errorMessage, error);
|
||||
// } else {
|
||||
// LoggerFactory.getLogger(getClass()).error(errorMessage, error);
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun reportError(errorMessage: String, error: Throwable?) {
|
||||
listener?.error(getName(), errorMessage, error) ?: LoggerFactory.getLogger(javaClass).error(errorMessage, error)
|
||||
|
||||
}
|
||||
|
||||
@Throws(PortException::class)
|
||||
private fun talk(request: String): String {
|
||||
try {
|
||||
controller.send(request + "\r")
|
||||
return controller.waitFor(timeout).trim()
|
||||
} catch (tex: PortTimeoutException) {
|
||||
//Single retry on timeout
|
||||
LoggerFactory.getLogger(javaClass).warn("A timeout exception for request '$request'. Making another attempt.")
|
||||
controller.send(request + "\r")
|
||||
return controller.waitFor(timeout).trim()
|
||||
}
|
||||
}
|
||||
|
||||
private fun update(key: String, value: String) {
|
||||
when (key) {
|
||||
"OUT" -> updateState("output", value == "ON")
|
||||
"MC" -> updateState("current", s2d(value))
|
||||
"PC" -> updateState("targetCurrent", s2d(value))
|
||||
"MV" -> updateState("voltage", s2d(value))
|
||||
"PV" -> updateState("targetVoltage", s2d(value))
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(PortException::class)
|
||||
private fun getParameter(name: String): String {
|
||||
return talk(name + "?").also {
|
||||
update(name, it)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(PortException::class)
|
||||
private fun setParameter(key: String, state: String): Boolean {
|
||||
val res = talk("$key $state")
|
||||
if ("OK" == res) {
|
||||
update(key, state)
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(PortException::class)
|
||||
private fun setParameter(key: String, state: Int): Boolean = setParameter(key, state.toString())
|
||||
|
||||
@Throws(PortException::class)
|
||||
private fun setParameter(key: String, state: Double): Boolean = setParameter(key, d2s(state))
|
||||
|
||||
/**
|
||||
* Extract number from LAMBDA response
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
private fun s2d(str: String): Double = java.lang.Double.valueOf(str)
|
||||
|
||||
/**
|
||||
* Cancel current update task
|
||||
*/
|
||||
fun stopUpdateTask() {
|
||||
updateTask?.let {
|
||||
it.cancel(false)
|
||||
lastUpdate = null
|
||||
listener?.updateTaskStateChanged(getName(), false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start recursive updates of current with given delays between updates. If
|
||||
* delay is 0 then updates are made immediately.
|
||||
*
|
||||
* @param targetI
|
||||
* @param delay
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun startUpdateTask(targetI: Double, delay: Int = DEFAULT_DELAY) {
|
||||
assert(delay > 0)
|
||||
stopUpdateTask()
|
||||
val call = {
|
||||
try {
|
||||
val measuredI = getCurrent()
|
||||
listener?.acceptMeasuredI(getName(), measuredI)
|
||||
if (Math.abs(measuredI - targetI) > CURRENT_PRECISION) {
|
||||
val nextI = nextI(measuredI, targetI)
|
||||
listener?.acceptNextI(getName(), nextI)
|
||||
setCurrent(nextI)
|
||||
} else {
|
||||
stopUpdateTask()
|
||||
}
|
||||
|
||||
} catch (ex: PortException) {
|
||||
reportError("Error in update task", ex)
|
||||
stopUpdateTask()
|
||||
}
|
||||
}
|
||||
|
||||
updateTask = scheduler.scheduleWithFixedDelay(call, 0, delay.toLong(), TimeUnit.MILLISECONDS)
|
||||
listener?.updateTaskStateChanged(getName(), true)
|
||||
}
|
||||
|
||||
@Throws(PortException::class)
|
||||
fun setOutputMode(out: Boolean) {
|
||||
controller.talk(address, timeout) {
|
||||
val outState: Int = if (out) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
if (!setParameter("OUT", outState)) {
|
||||
listener?.error(getName(), "Can't set output mode", null)
|
||||
} else {
|
||||
listener?.outputModeChanged(getName(), out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun nextI(measuredI: Double, targetI: Double): Double {
|
||||
assert(measuredI != targetI)
|
||||
|
||||
var step: Double
|
||||
if (lastUpdate == null) {
|
||||
step = MIN_UP_STEP_SIZE
|
||||
} else {
|
||||
//Choose optimal speed but do not exceed maximum speed
|
||||
step = Math.min(MAX_STEP_SIZE,
|
||||
lastUpdate!!.until(DateTimeUtils.now(), ChronoUnit.MILLIS).toDouble() / 60000.0 * speed)
|
||||
}
|
||||
|
||||
val res: Double
|
||||
if (targetI > measuredI) {
|
||||
step = Math.max(MIN_UP_STEP_SIZE, step)
|
||||
res = Math.min(targetI, measuredI + step)
|
||||
} else {
|
||||
step = Math.max(MIN_DOWN_STEP_SIZE, step)
|
||||
res = Math.max(targetI, measuredI - step)
|
||||
}
|
||||
|
||||
// не вводится ток меньше 0.5
|
||||
return if (res < 0.5 && targetI > CURRENT_PRECISION) {
|
||||
0.5
|
||||
} else if (res < 0.5 && targetI < CURRENT_PRECISION) {
|
||||
0.0
|
||||
} else {
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel current monitoring task
|
||||
*/
|
||||
fun stopMonitorTask() {
|
||||
monitorTask?.let {
|
||||
it.cancel(true)
|
||||
listener?.monitorTaskStateChanged(getName(), false)
|
||||
monitorTask = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return if (this.name == null || this.name.isEmpty()) {
|
||||
"LAMBDA " + address
|
||||
} else {
|
||||
this.name
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start monitoring task which checks for magnet status and then waits for
|
||||
* fixed time.
|
||||
*
|
||||
* @param delay an interval between scans in milliseconds
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun startMonitorTask(delay: Int = DEFAULT_MONITOR_DELAY) {
|
||||
assert(delay >= 1000)
|
||||
stopMonitorTask()
|
||||
|
||||
val call = Runnable {
|
||||
try {
|
||||
status
|
||||
} catch (ex: PortException) {
|
||||
reportError("Port connection exception during status measurement", ex)
|
||||
stopMonitorTask()
|
||||
}
|
||||
}
|
||||
|
||||
monitorTask = scheduler.scheduleWithFixedDelay(call, 0, delay.toLong(), TimeUnit.MILLISECONDS)
|
||||
listener?.monitorTaskStateChanged(getName(), true)
|
||||
|
||||
}
|
||||
|
||||
// fun request(message: String): String? {
|
||||
// try {
|
||||
// if (!setADR()) {
|
||||
// throw RuntimeException("F")
|
||||
// }
|
||||
// return talk(message)
|
||||
// } catch (ex: PortException) {
|
||||
// reportError("Can not send message to the port", ex)
|
||||
// return null
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
companion object {
|
||||
|
||||
private val LAMBDA_FORMAT = DecimalFormat("###.##")
|
||||
var CURRENT_PRECISION = 0.05
|
||||
// public static double CURRENT_STEP = 0.05;
|
||||
var DEFAULT_DELAY = 1
|
||||
var DEFAULT_MONITOR_DELAY = 2000
|
||||
var MAX_STEP_SIZE = 0.2
|
||||
var MIN_UP_STEP_SIZE = 0.005
|
||||
var MIN_DOWN_STEP_SIZE = 0.05
|
||||
var MAX_SPEED = 5.0 // 5 A per minute
|
||||
|
||||
/**
|
||||
* Method converts double to LAMBDA string
|
||||
*
|
||||
* @param d double that should be converted to string
|
||||
* @return string
|
||||
*/
|
||||
private fun d2s(d: Double): String {
|
||||
return LAMBDA_FORMAT.format(d)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,20 +1,95 @@
|
||||
package inr.numass.control.magnet
|
||||
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.control.ports.GenericPortController
|
||||
import hep.dataforge.control.ports.Port
|
||||
import java.time.Duration
|
||||
|
||||
class LambdaPortController(private val port: Port) : Port.PortController {
|
||||
class LambdaPortController(context: Context, port: Port) : GenericPortController(context, port) {
|
||||
private var currentAddress: Int = -1;
|
||||
|
||||
private var address: Int = -1;
|
||||
|
||||
init {
|
||||
port.holdBy(this)
|
||||
private fun setAddress(address: Int, timeout: Duration) {
|
||||
val response = sendAndWait("ADR $address\r", timeout) { true }.trim()
|
||||
if (response == "OK") {
|
||||
currentAddress = address
|
||||
} else {
|
||||
throw RuntimeException("Failed to set address to LAMBDA device on $port")
|
||||
}
|
||||
}
|
||||
|
||||
override fun acceptPhrase(message: String?) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
/**
|
||||
* perform series of synchronous actions ensuring that all of them have the same address
|
||||
*/
|
||||
fun <R> talk(address: Int, timeout: Duration, action: (GenericPortController) -> R): R {
|
||||
synchronized(this) {
|
||||
setAddress(address, timeout)
|
||||
return action(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun portError(errorMessage: String?, error: Throwable?) {
|
||||
super.portError(errorMessage, error)
|
||||
}
|
||||
// override fun getContext(): Context = _context
|
||||
//
|
||||
// private var address: Int = -1;
|
||||
//
|
||||
// private val channel = ConflatedChannel<String>();
|
||||
//
|
||||
// private val listeners = ReferenceRegistry<LambdaPortListener>()
|
||||
//
|
||||
// fun open() {
|
||||
// try {
|
||||
// port.holdBy(this)
|
||||
// if (!port.isOpen) {
|
||||
// port.open()
|
||||
// }
|
||||
// } catch (e: PortException) {
|
||||
// throw RuntimeException("Can't hold the port $port LAMBDA port controller", e)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// private suspend fun setAddress(address: Int, timeout: Duration) {
|
||||
// synchronized(this) {
|
||||
// port.send(this, "ADR $address\r")
|
||||
// val res = channel.receive()
|
||||
// if (res == "OK") {
|
||||
// this.address = address
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private suspend fun sendMessage(message: String): String {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// suspend fun fireSequence(address: Int, timeout: Duration, vararg messages: String) {
|
||||
// setAddress(address, timeout);
|
||||
// for (message in messages) {
|
||||
// sendMessage(message);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun close() {
|
||||
// port.releaseBy(this)
|
||||
// }
|
||||
//
|
||||
// override fun acceptPhrase(message: String) {
|
||||
// async {
|
||||
// channel.send(message);
|
||||
// }
|
||||
// listeners.forEach {
|
||||
// if (it.address == address) {
|
||||
// context.parallelExecutor().submit { it.action(message) }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun acceptError(errorMessage: String?, error: Throwable?) {
|
||||
// listeners.forEach {
|
||||
// if (it.address == address) {
|
||||
// context.parallelExecutor().submit { it.onError(errorMessage, error) }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// class LambdaPortListener(val address: Int, val action: (String) -> Unit, val onError: (String?, Throwable?) -> Unit = { _, _ -> })
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2015 Alexander Nozik.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package inr.numass.control.magnet
|
||||
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.exceptions.PortException
|
||||
import hep.dataforge.meta.Meta
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Polina
|
||||
*/
|
||||
class SafeLambdaMagnet(context: Context, meta: Meta, controller: LambdaPortController) :
|
||||
LambdaMagnet(context, meta, controller) {
|
||||
|
||||
private val safeConditions = HashSet<SafeMagnetCondition>()
|
||||
|
||||
// public SafeLambdaMagnet(String name, Port port, int address, int timeout, SafeMagnetCondition... safeConditions) {
|
||||
// super(name, port, address, timeout);
|
||||
// this.safeConditions.addAll(Arrays.asList(safeConditions));
|
||||
// }
|
||||
//
|
||||
// public SafeLambdaMagnet(String name, Port port, int address, SafeMagnetCondition... safeConditions) {
|
||||
// super(name, port, address);
|
||||
// this.safeConditions.addAll(Arrays.asList(safeConditions));
|
||||
// }
|
||||
|
||||
fun addSafeCondition(isBlocking: Boolean, condition: (Double) -> Boolean) {
|
||||
this.safeConditions.add(object : SafeMagnetCondition {
|
||||
|
||||
override fun isBlocking(): Boolean = isBlocking
|
||||
|
||||
override fun isSafe(address: Int, current: Double): Boolean = condition(current)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Add symmetric non-blocking conditions to ensure currents in two magnets have difference within given tolerance.
|
||||
* @param controller
|
||||
* @param tolerance
|
||||
*/
|
||||
fun bindTo(controller: SafeLambdaMagnet, tolerance: Double) {
|
||||
this.addSafeCondition(false) { I -> Math.abs(controller.getCurrent() - I) <= tolerance }
|
||||
controller.addSafeCondition(false) { I -> Math.abs(this.getCurrent() - I) <= tolerance }
|
||||
}
|
||||
|
||||
|
||||
@Throws(PortException::class)
|
||||
override fun setCurrent(current: Double) {
|
||||
safeConditions
|
||||
.filterNot { it.isSafe(address, current) }
|
||||
.forEach {
|
||||
if (it.isBlocking()) {
|
||||
it.onFail()
|
||||
throw RuntimeException("Can't set current. Condition not satisfied.")
|
||||
} else {
|
||||
listener?.displayState("BOUND")
|
||||
}
|
||||
}
|
||||
|
||||
super.setCurrent(current)
|
||||
}
|
||||
|
||||
interface SafeMagnetCondition {
|
||||
|
||||
fun isBlocking(): Boolean = true
|
||||
|
||||
fun isSafe(address: Int, current: Double): Boolean
|
||||
|
||||
fun onFail() {
|
||||
LoggerFactory.getLogger(javaClass).error("Can't set current. Condition not satisfied.")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2015 Alexander Nozik.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package inr.numass.control.magnet
|
||||
|
||||
import hep.dataforge.control.ports.VirtualPort
|
||||
import hep.dataforge.exceptions.PortException
|
||||
import hep.dataforge.kodex.useEachMeta
|
||||
import hep.dataforge.meta.Meta
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
class VirtualLambdaPort(meta: Meta) : VirtualPort(meta) {
|
||||
|
||||
@Volatile private var currentAddress = -1
|
||||
private val magnets = HashMap<Int, VirtualMagnetStatus>()
|
||||
private val virtualPortName: String = meta.getString("name", "virtual::numass.lambda")
|
||||
|
||||
// constructor(portName: String, magnets: Map<Int, Double>) {
|
||||
// this.virtualPortName = portName
|
||||
// magnets.forEach { key, value -> this.magnets.put(key, VirtualMagnetStatus(value)) }
|
||||
// }
|
||||
//
|
||||
// constructor(portName: String, vararg magnets: Int) {
|
||||
// this.virtualPortName = portName
|
||||
// for (magnet in magnets) {
|
||||
// this.magnets.put(magnet, VirtualMagnetStatus(0.01))
|
||||
// }
|
||||
// }
|
||||
|
||||
init {
|
||||
meta.useEachMeta("magnet") {
|
||||
val num = it.getInt("address", 1)
|
||||
val resistance = it.getDouble("resistance", 1.0)
|
||||
magnets.put(num, VirtualMagnetStatus(resistance))
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = virtualPortName
|
||||
|
||||
override fun evaluateRequest(request: String) {
|
||||
val command: String
|
||||
var value = ""
|
||||
val split = request.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
if (split.size == 1) {
|
||||
command = request
|
||||
} else {
|
||||
command = split[0]
|
||||
value = split[1]
|
||||
}
|
||||
try {
|
||||
evaluateRequest(command.trim { it <= ' ' }, value.trim { it <= ' ' })
|
||||
} catch (ex: RuntimeException) {
|
||||
|
||||
receivePhrase("FAIL")//TODO какая команда правильная?
|
||||
LoggerFactory.getLogger(javaClass).error("Request evaluation failure", ex)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun sendOK() {
|
||||
planResponse("OK", latency)
|
||||
}
|
||||
|
||||
private fun evaluateRequest(comand: String, value: String) {
|
||||
when (comand) {
|
||||
"ADR" -> {
|
||||
val address = Integer.parseInt(value)
|
||||
if (magnets.containsKey(address)) {
|
||||
currentAddress = address
|
||||
sendOK()
|
||||
}
|
||||
return
|
||||
}
|
||||
"ADR?" -> {
|
||||
planResponse(Integer.toString(currentAddress), latency)
|
||||
return
|
||||
}
|
||||
"OUT" -> {
|
||||
val state = Integer.parseInt(value)
|
||||
currentMagnet().out = state == 1
|
||||
sendOK()
|
||||
return
|
||||
}
|
||||
"OUT?" -> {
|
||||
val out = currentMagnet().out
|
||||
if (out) {
|
||||
planResponse("ON", latency)
|
||||
} else {
|
||||
planResponse("OFF", latency)
|
||||
}
|
||||
return
|
||||
}
|
||||
"PC" -> {
|
||||
var current = java.lang.Double.parseDouble(value)
|
||||
if (current < 0.5) {
|
||||
current = 0.0
|
||||
}
|
||||
currentMagnet().current = current
|
||||
sendOK()
|
||||
return
|
||||
}
|
||||
"PC?" -> {
|
||||
planResponse(java.lang.Double.toString(currentMagnet().current), latency)
|
||||
return
|
||||
}
|
||||
"MC?" -> {
|
||||
planResponse(java.lang.Double.toString(currentMagnet().current), latency)
|
||||
return
|
||||
}
|
||||
"PV?" -> {
|
||||
planResponse(java.lang.Double.toString(currentMagnet().getVoltage()), latency)
|
||||
return
|
||||
}
|
||||
"MV?" -> {
|
||||
planResponse(java.lang.Double.toString(currentMagnet().getVoltage()), latency)
|
||||
return
|
||||
}
|
||||
else -> LoggerFactory.getLogger(javaClass).warn("Unknown comand {}", comand)
|
||||
}
|
||||
}
|
||||
|
||||
private fun currentMagnet(): VirtualMagnetStatus {
|
||||
if (currentAddress < 0) {
|
||||
throw RuntimeException()
|
||||
}
|
||||
return magnets[currentAddress]!!
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
override fun close() {
|
||||
|
||||
}
|
||||
|
||||
@Throws(PortException::class)
|
||||
override fun open() {
|
||||
|
||||
}
|
||||
|
||||
override fun isOpen(): Boolean = true
|
||||
|
||||
private inner class VirtualMagnetStatus(val resistance: Double,
|
||||
var on: Boolean = true,
|
||||
var out: Boolean = false,
|
||||
var current: Double = 0.0) {
|
||||
|
||||
fun getVoltage() = current * resistance
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val latency = Duration.ofMillis(50)
|
||||
}
|
||||
}
|
@ -120,7 +120,7 @@ class MspDevice(context: Context, meta: Meta) : PortSensor<Values>(context, meta
|
||||
}
|
||||
}
|
||||
|
||||
override fun portError(errorMessage: String?, error: Throwable?) {
|
||||
override fun acceptError(errorMessage: String?, error: Throwable?) {
|
||||
notifyError(errorMessage, error)
|
||||
}
|
||||
|
||||
|
@ -113,13 +113,13 @@ abstract class DeviceDisplay<D : Device> : Component(), Connection, DeviceListen
|
||||
protected fun bindBooleanToState(state: String, property: BooleanProperty) {
|
||||
getStateBinding(state).addListener { _, oldValue, newValue ->
|
||||
if (isOpen && oldValue !== newValue) {
|
||||
property.value = newValue.booleanValue()
|
||||
runLater { property.value = newValue.booleanValue() }
|
||||
}
|
||||
}
|
||||
property.addListener { _, oldValue, newValue ->
|
||||
if (isOpen && oldValue != newValue) {
|
||||
runAsync {
|
||||
if(!device.isInitialized){
|
||||
if (!device.isInitialized) {
|
||||
device.init()
|
||||
}
|
||||
device.setState(state, newValue).get().booleanValue();
|
||||
|
@ -36,13 +36,9 @@ class CM32Device(context: Context, meta: Meta) : PortSensor<Double>(context, met
|
||||
return new
|
||||
}
|
||||
|
||||
override fun createMeasurement(): Measurement<Double> {
|
||||
return CMVacMeasurement()
|
||||
}
|
||||
override fun createMeasurement(): Measurement<Double> = CMVacMeasurement()
|
||||
|
||||
override fun getType(): String {
|
||||
return meta().getString("type", "Leibold CM32")
|
||||
}
|
||||
override fun getType(): String = meta().getString("type", "numass.vac.CM32")
|
||||
|
||||
private inner class CMVacMeasurement : SimpleMeasurement<Double>() {
|
||||
|
||||
@ -71,9 +67,7 @@ class CM32Device(context: Context, meta: Meta) : PortSensor<Double>(context, met
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDevice(): Device {
|
||||
return this@CM32Device
|
||||
}
|
||||
override fun getDevice(): Device = this@CM32Device
|
||||
|
||||
|
||||
}
|
||||
|
@ -26,13 +26,9 @@ class MKSBaratronDevice(context: Context, meta: Meta) : PortSensor<Double>(conte
|
||||
private val channel: Int = meta().getInt("channel", 2)
|
||||
|
||||
|
||||
override fun createMeasurement(): Measurement<Double> {
|
||||
return BaratronMeasurement()
|
||||
}
|
||||
override fun createMeasurement(): Measurement<Double> = BaratronMeasurement()
|
||||
|
||||
override fun getType(): String {
|
||||
return meta().getString("type", "MKS baratron")
|
||||
}
|
||||
override fun getType(): String = meta().getString("type", "numass.vac.baratron")
|
||||
|
||||
@Throws(ControlException::class)
|
||||
override fun buildPort(portName: String): Port {
|
||||
|
@ -20,7 +20,11 @@ import hep.dataforge.values.Value
|
||||
import hep.dataforge.values.ValueType.BOOLEAN
|
||||
import inr.numass.control.DeviceView
|
||||
import javafx.beans.property.BooleanProperty
|
||||
import javafx.beans.property.SimpleBooleanProperty
|
||||
import javafx.beans.property.SimpleIntegerProperty
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import javafx.beans.property.adapter.JavaBeanBooleanPropertyBuilder
|
||||
import tornadofx.*
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
@ -35,34 +39,17 @@ import java.util.regex.Pattern
|
||||
@DeviceView(VacDisplay::class)
|
||||
class MKSVacDevice(context: Context, meta: Meta) : PortSensor<Double>(context, meta) {
|
||||
|
||||
private val deviceAddress: String
|
||||
get() = meta().getString("address", "253")
|
||||
val deviceAddressProperty = SimpleStringProperty()
|
||||
var deviceAddress by deviceAddressProperty
|
||||
|
||||
|
||||
private 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val isPowerOnProperty = SimpleBooleanProperty()
|
||||
var isPowerOn by isPowerOnProperty
|
||||
|
||||
|
||||
val channelProperty = SimpleIntegerProperty(meta().getInt("channel", 5)!!)
|
||||
var channel by channelProperty
|
||||
|
||||
private val channel: Int = meta().getInt("channel", 5)!!
|
||||
|
||||
@Throws(ControlException::class)
|
||||
private fun talk(requestContent: String): String? {
|
||||
@ -83,17 +70,13 @@ class MKSVacDevice(context: Context, meta: Meta) : PortSensor<Double>(context, m
|
||||
return handler
|
||||
}
|
||||
|
||||
override fun createMeasurement(): Measurement<Double> {
|
||||
return MKSVacMeasurement()
|
||||
}
|
||||
override fun createMeasurement(): Measurement<Double> = MKSVacMeasurement()
|
||||
|
||||
@Throws(ControlException::class)
|
||||
override fun computeState(stateName: String): Any {
|
||||
return when (stateName) {
|
||||
override fun computeState(stateName: String): Any = when (stateName) {
|
||||
"power" -> talk("FP?") == "ON"
|
||||
else -> super.computeState(stateName)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(ControlException::class)
|
||||
override fun requestStateChange(stateName: String, value: Value) {
|
||||
@ -122,9 +105,7 @@ class MKSVacDevice(context: Context, meta: Meta) : PortSensor<Double>(context, m
|
||||
|
||||
}
|
||||
|
||||
override fun getType(): String {
|
||||
return meta().getString("type", "MKS vacuumeter")
|
||||
}
|
||||
override fun getType(): String = meta().getString("type", "MKS vacuumeter")
|
||||
|
||||
private inner class MKSVacMeasurement : SimpleMeasurement<Double>() {
|
||||
|
||||
@ -139,18 +120,16 @@ class MKSVacDevice(context: Context, meta: Meta) : PortSensor<Double>(context, m
|
||||
return null
|
||||
}
|
||||
val res = java.lang.Double.parseDouble(answer)
|
||||
if (res <= 0) {
|
||||
return if (res <= 0) {
|
||||
this.updateMessage("No power")
|
||||
invalidateState("power")
|
||||
return null
|
||||
null
|
||||
} else {
|
||||
this.updateMessage("OK")
|
||||
return res
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDevice(): Device {
|
||||
return this@MKSVacDevice
|
||||
}
|
||||
override fun getDevice(): Device = this@MKSVacDevice
|
||||
}
|
||||
}
|
||||
|
@ -33,23 +33,19 @@ class MeradatVacDevice(context: Context, meta: Meta) : PortSensor<Double>(contex
|
||||
return newHandler
|
||||
}
|
||||
|
||||
override fun createMeasurement(): Measurement<Double> {
|
||||
return MeradatMeasurement()
|
||||
}
|
||||
override fun createMeasurement(): Measurement<Double> = MeradatMeasurement()
|
||||
|
||||
override fun getType(): String {
|
||||
return meta().getString("type", "Vit vacuumeter")
|
||||
}
|
||||
override fun getType(): String = 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
|
||||
private val base: String = String.format(":%02d", meta().getInt("address", 1))
|
||||
private val query: String // ":010300000002FA\r\n";
|
||||
|
||||
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")
|
||||
@ -72,7 +68,7 @@ class MeradatVacDevice(context: Context, meta: Meta) : PortSensor<Double>(contex
|
||||
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
|
||||
exp -= 65536
|
||||
}
|
||||
var res = BigDecimal.valueOf(base * Math.pow(10.0, exp.toDouble()))
|
||||
res = res.setScale(4, RoundingMode.CEILING)
|
||||
@ -87,9 +83,7 @@ class MeradatVacDevice(context: Context, meta: Meta) : PortSensor<Double>(contex
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDevice(): Device {
|
||||
return this@MeradatVacDevice
|
||||
}
|
||||
override fun getDevice(): Device = this@MeradatVacDevice
|
||||
}
|
||||
|
||||
companion object {
|
||||
@ -100,10 +94,7 @@ class MeradatVacDevice(context: Context, meta: Meta) : PortSensor<Double>(contex
|
||||
* 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()
|
||||
}
|
||||
val checksum = bytes.sumBy { it.toInt() }
|
||||
var value = Integer.toHexString(-checksum)
|
||||
value = value.substring(value.length - 2).toUpperCase()
|
||||
if (value.length < 2) {
|
||||
|
@ -55,13 +55,10 @@ class VacCollectorDevice(context: Context, meta: Meta, val sensors: Collection<S
|
||||
get() = Duration.parse(meta().getString("averagingDuration", "PT30S"))
|
||||
|
||||
|
||||
override fun optDevice(name: Name): Optional<Device> {
|
||||
return Optional.ofNullable(sensors.find { it.name == name.toUnescaped() })
|
||||
}
|
||||
override fun optDevice(name: Name): Optional<Device> =
|
||||
Optional.ofNullable(sensors.find { it.name == name.toUnescaped() })
|
||||
|
||||
override fun deviceNames(): Stream<Name> {
|
||||
return sensors.stream().map { Name.ofSingle(it.name) }
|
||||
}
|
||||
override fun deviceNames(): Stream<Name> = sensors.stream().map { Name.ofSingle(it.name) }
|
||||
|
||||
|
||||
override fun init() {
|
||||
@ -70,15 +67,10 @@ class VacCollectorDevice(context: Context, meta: Meta, val sensors: Collection<S
|
||||
s.init()
|
||||
}
|
||||
}
|
||||
|
||||
override fun createMeasurement(): Measurement<Values> {
|
||||
//TODO use meta
|
||||
return VacuumMeasurement()
|
||||
}
|
||||
override fun createMeasurement(): Measurement<Values> = VacuumMeasurement()
|
||||
|
||||
override fun getType(): String {
|
||||
return "Numass vacuum"
|
||||
}
|
||||
override fun getType(): String = "numass.vac.collector"
|
||||
|
||||
|
||||
@Throws(ControlException::class)
|
||||
|
Loading…
Reference in New Issue
Block a user