Fixing numass control

This commit is contained in:
Alexander Nozik 2017-11-23 17:28:02 +03:00
parent 2d8ed4da69
commit 7361f7a875
5 changed files with 158 additions and 153 deletions

View File

@ -23,14 +23,18 @@ import hep.dataforge.control.ports.PortFactory
import hep.dataforge.control.ports.PortTimeoutException import hep.dataforge.control.ports.PortTimeoutException
import hep.dataforge.description.ValueDef import hep.dataforge.description.ValueDef
import hep.dataforge.exceptions.ControlException import hep.dataforge.exceptions.ControlException
import hep.dataforge.exceptions.NameNotFoundException
import hep.dataforge.exceptions.PortException import hep.dataforge.exceptions.PortException
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.utils.DateTimeUtils import hep.dataforge.utils.DateTimeUtils
import hep.dataforge.values.Value
import hep.dataforge.values.ValueType.* import hep.dataforge.values.ValueType.*
import inr.numass.control.booleanState
import inr.numass.control.doubleState
import inr.numass.control.timeState
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.text.DecimalFormat import java.text.DecimalFormat
import java.time.Duration import java.time.Duration
import java.time.Instant
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
import java.util.concurrent.Future import java.util.concurrent.Future
import java.util.concurrent.ScheduledThreadPoolExecutor import java.util.concurrent.ScheduledThreadPoolExecutor
@ -41,14 +45,15 @@ import java.util.concurrent.TimeUnit
*/ */
@ValueDef(name = "timeout", type = arrayOf(NUMBER), def = "400", info = "A timeout for port response") @ValueDef(name = "timeout", type = arrayOf(NUMBER), def = "400", info = "A timeout for port response")
@StateDefs( @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), def = "0", info = "Current current")),
StateDef(value = ValueDef(name = "current", type = arrayOf(NUMBER), info = "Current current")), StateDef(value = ValueDef(name = "voltage", type = arrayOf(NUMBER), def = "0", info = "Current voltage")),
StateDef(value = ValueDef(name = "voltage", type = arrayOf(NUMBER), info = "Current voltage")), StateDef(value = ValueDef(name = "targetCurrent", type = arrayOf(NUMBER), def = "0", info = "Target current"), writable = true),
StateDef(value = ValueDef(name = "targetCurrent", type = arrayOf(NUMBER), info = "Target current"), writable = true), StateDef(value = ValueDef(name = "targetVoltage", type = arrayOf(NUMBER), def = "5.0", info = "Target voltage"), writable = true),
StateDef(value = ValueDef(name = "targetVoltage", type = arrayOf(NUMBER), info = "Target voltage"), writable = true), StateDef(value = ValueDef(name = "output", type = arrayOf(BOOLEAN), def = "false", info = "Weather output on or off"), writable = true),
StateDef(value = ValueDef(name = "lastUpdate", type = arrayOf(TIME), info = "Time of the last update"), writable = true), StateDef(value = ValueDef(name = "lastUpdate", type = arrayOf(TIME), def = "0", 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 = "updating", type = arrayOf(BOOLEAN), def = "false", 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) StateDef(value = ValueDef(name = "monitoring", type = arrayOf(BOOLEAN), def = "false", info = "Shows if monitoring task is running"), writable = true),
StateDef(value = ValueDef(name = "speed", type = arrayOf(NUMBER), info = "Current change speed in Amper per minute"), writable = true)
) )
open class LambdaMagnet(context: Context, meta: Meta, private val controller: LambdaPortController) : AbstractDevice(context, meta) { open class LambdaMagnet(context: Context, meta: Meta, private val controller: LambdaPortController) : AbstractDevice(context, meta) {
@ -66,57 +71,25 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
private val timeout: Duration = meta.optString("timeout").map<Duration> { Duration.parse(it) }.orElse(Duration.ofMillis(200)) private val timeout: Duration = meta.optString("timeout").map<Duration> { Duration.parse(it) }.orElse(Duration.ofMillis(200))
private var monitorTask: Future<*>? = null private var monitorTask: Future<*>? = null
private var updateTask: Future<*>? = null private var updateTask: Future<*>? = null
private var lastUpdate: Instant? = null
var lastUpdate by timeState()
private set
val current by doubleState()
val voltage by doubleState()
var targetCurrent by doubleState()
var targetVoltage by doubleState()
var output by booleanState()
var monitoring by booleanState()
/** /**
* Get current change speed in Amper per minute * current change speed in Amper per minute
*
* @return
*/
/**
* Set current change speed in Amper per minute
* *
* @param speed * @param speed
*/ */
var speed = MAX_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 * A setup for single magnet controller
* *
@ -130,35 +103,85 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
} }
// /** override fun computeState(stateName: String): Any {
// * This method creates an element of class MegnetController with exact when (stateName) {
// * parameters. If you have two parameters for your method - the next "current" -> controller.talk(address, timeout) { s2d(getParameter("MC")) }
// * constructor will be used. "voltage" -> controller.talk(address, timeout) { s2d(getParameter("MV")) }
"targetCurrent" -> controller.talk(address, timeout) { s2d(getParameter("PC")) }
"targetVoltage" -> controller.talk(address, timeout) { s2d(getParameter("PV")) }
"output" -> controller.talk(address, timeout) { talk("OUT?") == "OK" }
"monitoring" -> monitorTask != null
"updating" -> updateTask != null
else -> getDefaultState(stateName)
}
}
override fun requestStateChange(stateName: String, value: Value): Any {
return when (stateName) {
"targetCurrent" -> {
if (!setParameter("PC", value.doubleValue())) {
reportError("Can't set the target current", null)
Value.NULL
} else {
lastUpdate = DateTimeUtils.now()
value
}
}
"targetVoltage" -> {
if (!setParameter("PV", value.doubleValue())) {
reportError("Can't set the target voltage", null)
Value.NULL
} else {
value
}
}
"speed" -> value
"lastUpdate" -> value
"updating" -> if (value.booleanValue()) {
startUpdateTask()
true
} else {
stopUpdateTask()
false
}
"monitoring" -> i
"output" ->
else -> throw NameNotFoundException("State not found", stateName)
}
}
// 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
// * // *
// * @param name // * @return status of magnet
// * @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) { // private val status: MagnetStatus
// this.name = name; // @Throws(PortException::class)
// this.port = port; // get() {
// this.port.setDelimiter("\r");//PENDING меняем состояние внешнего объекта? // return controller.talk(address, timeout) {
// this.address = address; // val out: Boolean = "ON" == talk("OUT?")
// this.timeout = Duration.ofMillis(timeout); //
// } // val measuredCurrent = s2d(getParameter("MC"))
// // updateState("current", measuredCurrent)
// public LambdaMagnet(Port port, int address, int timeout) { // val setCurrent = s2d(getParameter("PC"))
// this(null, port, address, timeout); // val measuredVoltage = s2d(getParameter("MV"))
// } // val setVoltage = s2d(getParameter("PV"))
// //
// public LambdaMagnet(Port port, int address) { // MagnetStatus(out, measuredCurrent, setCurrent, measuredVoltage, setVoltage).also {
// this(null, port, address); // listener?.acceptStatus(getName(), it)
// } // }
// // }
// public LambdaMagnet(String name, Port port, int address) { // }
// this(name, port, address, 300); //
// }
@Throws(ControlException::class) @Throws(ControlException::class)
@ -181,25 +204,6 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
} }
// 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?) { private fun reportError(errorMessage: String, error: Throwable?) {
listener?.error(getName(), errorMessage, error) ?: LoggerFactory.getLogger(javaClass).error(errorMessage, error) listener?.error(getName(), errorMessage, error) ?: LoggerFactory.getLogger(javaClass).error(errorMessage, error)
@ -229,22 +233,10 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
} }
@Throws(PortException::class) @Throws(PortException::class)
private fun getParameter(name: String): String { private fun getParameter(name: String): String = talk(name + "?")
return talk(name + "?").also {
update(name, it)
}
}
@Throws(PortException::class) @Throws(PortException::class)
private fun setParameter(key: String, state: String): Boolean { private fun setParameter(key: String, state: String): Boolean = "OK" == talk("$key $state")
val res = talk("$key $state")
if ("OK" == res) {
update(key, state)
return true
} else {
return false
}
}
@Throws(PortException::class) @Throws(PortException::class)
private fun setParameter(key: String, state: Int): Boolean = setParameter(key, state.toString()) private fun setParameter(key: String, state: Int): Boolean = setParameter(key, state.toString())
@ -279,17 +271,18 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
* @param delay * @param delay
*/ */
@JvmOverloads @JvmOverloads
fun startUpdateTask(targetI: Double, delay: Int = DEFAULT_DELAY) { fun startUpdateTask(delay: Int = DEFAULT_DELAY) {
assert(delay > 0) assert(delay > 0)
stopUpdateTask() stopUpdateTask()
val call = { val call = {
try { try {
val measuredI = getCurrent() val measuredI = current ?: 0.0
val targetI = targetCurrent ?: 0.0
listener?.acceptMeasuredI(getName(), measuredI) listener?.acceptMeasuredI(getName(), measuredI)
if (Math.abs(measuredI - targetI) > CURRENT_PRECISION) { if (Math.abs(measuredI - targetI) > CURRENT_PRECISION) {
val nextI = nextI(measuredI, targetI) val nextI = nextI(measuredI, targetI)
listener?.acceptNextI(getName(), nextI) listener?.acceptNextI(getName(), nextI)
setCurrent(nextI) targetCurrent = nextI
} else { } else {
stopUpdateTask() stopUpdateTask()
} }
@ -323,22 +316,20 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
private fun nextI(measuredI: Double, targetI: Double): Double { private fun nextI(measuredI: Double, targetI: Double): Double {
assert(measuredI != targetI) assert(measuredI != targetI)
var step: Double var step = if (lastUpdate == null) {
if (lastUpdate == null) { MIN_UP_STEP_SIZE
step = MIN_UP_STEP_SIZE
} else { } else {
//Choose optimal speed but do not exceed maximum speed //Choose optimal speed but do not exceed maximum speed
step = Math.min(MAX_STEP_SIZE, Math.min(MAX_STEP_SIZE,
lastUpdate!!.until(DateTimeUtils.now(), ChronoUnit.MILLIS).toDouble() / 60000.0 * speed) lastUpdate!!.until(DateTimeUtils.now(), ChronoUnit.MILLIS).toDouble() / 60000.0 * speed)
} }
val res: Double val res = if (targetI > measuredI) {
if (targetI > measuredI) {
step = Math.max(MIN_UP_STEP_SIZE, step) step = Math.max(MIN_UP_STEP_SIZE, step)
res = Math.min(targetI, measuredI + step) Math.min(targetI, measuredI + step)
} else { } else {
step = Math.max(MIN_DOWN_STEP_SIZE, step) step = Math.max(MIN_DOWN_STEP_SIZE, step)
res = Math.max(targetI, measuredI - step) Math.max(targetI, measuredI - step)
} }
// не вводится ток меньше 0.5 // не вводится ток меньше 0.5
@ -363,7 +354,7 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
} }
override fun getName(): String { override fun getName(): String {
return if (this.name == null || this.name.isEmpty()) { return if (this.name.isEmpty()) {
"LAMBDA " + address "LAMBDA " + address
} else { } else {
this.name this.name
@ -411,14 +402,13 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
companion object { companion object {
private val LAMBDA_FORMAT = DecimalFormat("###.##") private val LAMBDA_FORMAT = DecimalFormat("###.##")
var CURRENT_PRECISION = 0.05 const val CURRENT_PRECISION = 0.05
// public static double CURRENT_STEP = 0.05; const val DEFAULT_DELAY = 1
var DEFAULT_DELAY = 1 const val DEFAULT_MONITOR_DELAY = 2000
var DEFAULT_MONITOR_DELAY = 2000 const val MAX_STEP_SIZE = 0.2
var MAX_STEP_SIZE = 0.2 const val MIN_UP_STEP_SIZE = 0.005
var MIN_UP_STEP_SIZE = 0.005 const val MIN_DOWN_STEP_SIZE = 0.05
var MIN_DOWN_STEP_SIZE = 0.05 const val MAX_SPEED = 5.0 // 5 A per minute
var MAX_SPEED = 5.0 // 5 A per minute
/** /**
* Method converts double to LAMBDA string * Method converts double to LAMBDA string
@ -426,9 +416,7 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
* @param d double that should be converted to string * @param d double that should be converted to string
* @return string * @return string
*/ */
private fun d2s(d: Double): String { private fun d2s(d: Double): String = LAMBDA_FORMAT.format(d)
return LAMBDA_FORMAT.format(d)
}
} }
} }

View File

@ -50,6 +50,25 @@ class NumberStateDelegate(private val stateName: String?) {
} }
} }
class DoubleStateDelegate(private val stateName: String?) {
operator fun getValue(thisRef: Stateful, property: KProperty<*>): Double? =
thisRef.getState(stateName ?: property.name).doubleValue()
operator fun setValue(thisRef: Stateful, property: KProperty<*>, value: Double?) {
thisRef.setState(stateName ?: property.name, value);
}
}
class IntStateDelegate(private val stateName: String?) {
operator fun getValue(thisRef: Stateful, property: KProperty<*>): Int? =
thisRef.getState(stateName ?: property.name).intValue()
operator fun setValue(thisRef: Stateful, property: KProperty<*>, value: Int?) {
thisRef.setState(stateName ?: property.name, value);
}
}
/** /**
* Delegate states to read/write property * Delegate states to read/write property
@ -60,3 +79,5 @@ fun Stateful.stringState(stateName: String? = null) = StringStateDelegate(stateN
fun Stateful.booleanState(stateName: String? = null) = BooleanStateDelegate(stateName) fun Stateful.booleanState(stateName: String? = null) = BooleanStateDelegate(stateName)
fun Stateful.timeState(stateName: String? = null) = TimeStateDelegate(stateName) fun Stateful.timeState(stateName: String? = null) = TimeStateDelegate(stateName)
fun Stateful.numberState(stateName: String? = null) = NumberStateDelegate(stateName) fun Stateful.numberState(stateName: String? = null) = NumberStateDelegate(stateName)
fun Stateful.doubleState(stateName: String? = null) = DoubleStateDelegate(stateName)
fun Stateful.intState(stateName: String? = null) = IntStateDelegate(stateName)

View File

@ -99,9 +99,8 @@ abstract class DeviceDisplay<D : Device> : Component(), Connection, DeviceListen
} }
} }
fun getBooleanStateBinding(state: String): BooleanBinding { fun getBooleanStateBinding(state: String): BooleanBinding =
return getStateBinding(state).booleanBinding { it!!.booleanValue() } getStateBinding(state).booleanBinding { it?.booleanValue() ?: false }
}
/** /**
* Bind existing boolean property to writable device state * Bind existing boolean property to writable device state
@ -122,9 +121,7 @@ abstract class DeviceDisplay<D : Device> : Component(), Connection, DeviceListen
if (!device.isInitialized) { if (!device.isInitialized) {
device.init() device.init()
} }
device.setState(state, newValue).get().booleanValue(); device.setState(state, newValue)
} ui {
property.set(it)
} }
} }
} }

View File

@ -51,12 +51,10 @@ class Indicator(radius: Double = 10.0) : Circle(radius, Color.GRAY) {
*/ */
fun bind(booleanValue: ObservableValue<Boolean?>) { fun bind(booleanValue: ObservableValue<Boolean?>) {
bind(booleanValue) { bind(booleanValue) {
if (it == null) { when {
Color.GRAY it == null -> Color.GRAY
} else if (it) { it -> Color.GREEN
Color.GREEN; else -> Color.RED
} else {
Color.RED;
} }
} }
} }
@ -119,11 +117,12 @@ fun Node.deviceStateToggle(connection: DeviceDisplay<*>, state: String, title: S
isSelected = false isSelected = false
selectedProperty().addListener { _, oldValue, newValue -> selectedProperty().addListener { _, oldValue, newValue ->
if (oldValue != newValue) { if (oldValue != newValue) {
connection.device.setState(state, newValue).thenAccept { connection.device.setState(state, newValue)
isSelected = it.booleanValue()
}
} }
} }
connection.getBooleanStateBinding(state).onChange {
isSelected = it
}
} }
deviceStateIndicator(connection, state, false) deviceStateIndicator(connection, state, false)
} else { } else {

View File

@ -46,7 +46,7 @@ class MKSVacDevice(context: Context, meta: Meta) : PortSensor<Double>(context, m
var isPowerOn by isPowerOnProperty var isPowerOn by isPowerOnProperty
val channelProperty = SimpleIntegerProperty(meta().getInt("channel", 5)!!) val channelProperty = SimpleIntegerProperty(meta.getInt("channel", 5))
var channel by channelProperty var channel by channelProperty
@ -78,7 +78,7 @@ class MKSVacDevice(context: Context, meta: Meta) : PortSensor<Double>(context, m
} }
@Throws(ControlException::class) @Throws(ControlException::class)
override fun requestStateChange(stateName: String, value: Value) { override fun requestStateChange(stateName: String, value: Value): Any {
when (stateName) { when (stateName) {
"power" -> isPowerOn = value.booleanValue() "power" -> isPowerOn = value.booleanValue()
else -> super.requestStateChange(stateName, value) else -> super.requestStateChange(stateName, value)