From af54525d41f430aacbd8efe4f3cd68a1f5cdd238 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 4 Apr 2018 20:37:04 +0300 Subject: [PATCH] Fix of config editor refresh. Multiple fixes in control. --- .../inr/numass/control/cryotemp/PKT8Device.kt | 6 +- .../numass/control/cryotemp/PKT8Display.kt | 4 +- .../inr/numass/control/magnet/LambdaMagnet.kt | 257 ++++------- .../numass/control/magnet/SafeLambdaMagnet.kt | 6 +- .../control/magnet/fx/MagnetControllerApp.kt | 6 +- .../magnet/fx/MagnetControllerComponent.kt | 420 ++++++++---------- .../inr/numass/control/magnet/fx/TestApp.kt | 5 +- .../inr/numass/control/msp/MspDevice.kt | 10 +- .../inr/numass/control/msp/MspDisplay.kt | 4 +- .../inr/numass/control/DeviceDisplayFX.kt | 163 +++++++ .../kotlin/inr/numass/control/FXExtensions.kt | 19 +- .../inr/numass/control/readvac/CM32Device.kt | 32 +- .../inr/numass/control/readvac/ConsoleVac.kt | 6 +- .../control/readvac/MKSBaratronDevice.kt | 35 +- .../numass/control/readvac/MKSVacDevice.kt | 2 +- .../control/readvac/MeradatVacDevice.kt | 57 ++- .../control/readvac/VacCollectorDevice.kt | 24 +- .../control/readvac/VacCollectorDisplay.kt | 21 +- .../control/readvac/VacDeviceFactory.kt | 2 +- .../inr/numass/control/readvac/VacDisplay.kt | 4 +- 20 files changed, 568 insertions(+), 515 deletions(-) create mode 100644 numass-control/src/main/kotlin/inr/numass/control/DeviceDisplayFX.kt diff --git a/numass-control/cryotemp/src/main/kotlin/inr/numass/control/cryotemp/PKT8Device.kt b/numass-control/cryotemp/src/main/kotlin/inr/numass/control/cryotemp/PKT8Device.kt index e8670667..d646bf7b 100644 --- a/numass-control/cryotemp/src/main/kotlin/inr/numass/control/cryotemp/PKT8Device.kt +++ b/numass-control/cryotemp/src/main/kotlin/inr/numass/control/cryotemp/PKT8Device.kt @@ -79,11 +79,11 @@ class PKT8Device(context: Context, meta: Meta) : PortSensor(context, meta) { tableFormatBuilder.build() } - val sps: String by valueState(SPS).string + val sps: String by valueState(SPS).stringDelegate - val pga: String by valueState(PGA).string + val pga: String by valueState(PGA).stringDelegate - val abuf: String by valueState(ABUF).string + val abuf: String by valueState(ABUF).stringDelegate private val duration = Duration.parse(meta.getString("averagingDuration", "PT30S")) diff --git a/numass-control/cryotemp/src/main/kotlin/inr/numass/control/cryotemp/PKT8Display.kt b/numass-control/cryotemp/src/main/kotlin/inr/numass/control/cryotemp/PKT8Display.kt index 15b54339..163ba345 100644 --- a/numass-control/cryotemp/src/main/kotlin/inr/numass/control/cryotemp/PKT8Display.kt +++ b/numass-control/cryotemp/src/main/kotlin/inr/numass/control/cryotemp/PKT8Display.kt @@ -12,7 +12,7 @@ import hep.dataforge.plots.PlotFrame import hep.dataforge.plots.PlotUtils import hep.dataforge.plots.data.TimePlot import hep.dataforge.plots.jfreechart.JFreeChartFrame -import inr.numass.control.DeviceDisplay +import inr.numass.control.DeviceDisplayFX import javafx.beans.binding.ListBinding import javafx.beans.property.SimpleObjectProperty import javafx.collections.FXCollections @@ -30,7 +30,7 @@ import java.time.Instant /** * Created by darksnake on 30-May-17. */ -class PKT8Display : DeviceDisplay(), PKT8ValueListener { +class PKT8Display : DeviceDisplayFX(), PKT8ValueListener { override fun buildView(device: PKT8Device) = CryoView() diff --git a/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/LambdaMagnet.kt b/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/LambdaMagnet.kt index 13a3e341..98b3d5b3 100644 --- a/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/LambdaMagnet.kt +++ b/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/LambdaMagnet.kt @@ -17,9 +17,7 @@ package inr.numass.control.magnet import hep.dataforge.context.Context import hep.dataforge.control.devices.AbstractDevice -import hep.dataforge.control.devices.booleanState -import hep.dataforge.control.devices.doubleState -import hep.dataforge.control.devices.timeState +import hep.dataforge.control.ports.Port import hep.dataforge.control.ports.PortFactory import hep.dataforge.description.ValueDef import hep.dataforge.exceptions.ControlException @@ -27,12 +25,14 @@ import hep.dataforge.exceptions.PortException import hep.dataforge.meta.Meta import hep.dataforge.states.StateDef import hep.dataforge.states.StateDefs +import hep.dataforge.states.valueState import hep.dataforge.utils.DateTimeUtils -import hep.dataforge.values.Value import hep.dataforge.values.ValueType.* +import kotlinx.coroutines.experimental.runBlocking 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 @@ -41,12 +41,12 @@ import java.util.concurrent.TimeUnit /** * @author Polina */ -@ValueDef(name = "timeout", type = arrayOf(NUMBER), def = "400", info = "A timeout for port response") +@ValueDef(name = "timeout", type = [NUMBER], def = "400", info = "A timeout for port response") @StateDefs( StateDef(value = ValueDef(name = "current", type = arrayOf(NUMBER), def = "0", info = "Current current")), StateDef(value = ValueDef(name = "voltage", type = arrayOf(NUMBER), def = "0", info = "Current voltage")), - StateDef(value = ValueDef(name = "targetCurrent", type = arrayOf(NUMBER), def = "0", 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 = "outCurrent", type = arrayOf(NUMBER), def = "0", info = "Target current"), writable = true), + StateDef(value = ValueDef(name = "outVoltage", type = arrayOf(NUMBER), def = "5.0", 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), def = "0", info = "Time of the last update"), writable = true), StateDef(value = ValueDef(name = "updating", type = arrayOf(BOOLEAN), def = "false", info = "Shows if current ramping in progress"), writable = true), @@ -57,28 +57,70 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La private var closePortOnShutDown = false - private val name: String = meta.getString("name", "LAMBDA") /** * @return the address */ - val address: Int = meta.getInt("address", 1)!! + val address: Int = meta.getInt("address", 1) + + override val name: String = meta.getString("name", "LAMBDA_$address") private val scheduler = ScheduledThreadPoolExecutor(1) - var listener: MagnetStateListener? = null + //var listener: MagnetStateListener? = null // private volatile double current = 0; private val timeout: Duration = meta.optString("timeout").map { Duration.parse(it) }.orElse(Duration.ofMillis(200)) private var monitorTask: Future<*>? = null private var updateTask: Future<*>? = null - var lastUpdate by timeState() + var lastUpdate by valueState("lastUpdate", getter = {0}).timeDelegate 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() + // read-only values of current output + val current = valueState("current", getter = { controller.talk(address, timeout) { s2d(getParameter("MC")) } }) +// val current by current.double + + val voltage = valueState("voltage", getter = { controller.talk(address, timeout) { s2d(getParameter("MV")) } }) +// val voltage by voltage.double + + var target = valueState("target") + + //output values of current and voltage + private var outCurrent by valueState("outCurrent", getter = { controller.talk(address, timeout) { s2d(getParameter("PC")) } }) { _, value -> + if (setParameter("PC", value.doubleValue())) { + lastUpdate = DateTimeUtils.now() + } else { + notifyError("Can't set the target current") + } + return@valueState value + }.doubleDelegate + + private val outVoltage = valueState("outVoltage", getter = { controller.talk(address, timeout) { s2d(getParameter("PV")) } }) { _, value -> + if (!setParameter("PV", value.doubleValue())) { + notifyError("Can't set the target voltage") + } + return@valueState value + } + + val output = valueState("output", getter = { controller.talk(address, timeout) { talk("OUT?") == "OK" } }) { _, value -> + setOutputMode(value.booleanValue()) + } + + var monitoring =valueState("monitoring", getter = { monitorTask != null }) { _, value -> + if (value.booleanValue()) { + startMonitorTask() + } else { + stopMonitorTask() + } + return@valueState value + } + + var updating = valueState("updating", getter = { updateTask != null }) { _, value -> + if (value.booleanValue()) { + startUpdateTask() + } else { + stopUpdateTask() + } + return@valueState value + } /** @@ -100,82 +142,6 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La closePortOnShutDown = true } - - override fun computeState(stateName: String): Any { - return when (stateName) { - "current" -> controller.talk(address, timeout) { s2d(getParameter("MC")) } - "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 -> getLogicalState(stateName) - } - } - - override fun requestStateChange(stateName: String, value: Value) { - when (stateName) { - "targetCurrent" -> { - if (setParameter("PC", value.doubleValue())) { - lastUpdate = DateTimeUtils.now() - } else { - reportError("Can't set the target current", null) - } - } - "targetVoltage" -> { - if (!setParameter("PV", value.doubleValue())) { - reportError("Can't set the target voltage", null) - } - } - "updating" -> if (value.booleanValue()) { - startUpdateTask() - } else { - stopUpdateTask() - } - "monitoring" -> if (value.booleanValue()) { - startMonitorTask() - } else { - stopMonitorTask() - } - "output" -> setOutputMode(value.booleanValue()) - else -> setLogicalState(stateName, value); - } - } - -// 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) -// } -// } -// } -// - - @Throws(ControlException::class) override fun init() { super.init() @@ -196,36 +162,36 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La } - private fun reportError(errorMessage: String, error: Throwable?) { - listener?.error(getName(), errorMessage, error) ?: LoggerFactory.getLogger(javaClass).error(errorMessage, error) - - } +// private fun reportError(errorMessage: String, error: Throwable?) { +// listener?.error(name, errorMessage, error) ?: LoggerFactory.getLogger(javaClass).error(errorMessage, error) +// +// } @Throws(PortException::class) private fun talk(request: String): String { - try { + return try { controller.send(request + "\r") - return controller.waitFor(timeout).trim() - } catch (tex: PortTimeoutException) { + controller.waitFor(timeout).trim() + } catch (tex: Port.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() + 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)) - } - } +// private fun update(key: String, value: String) { +// when (key) { +// "OUT" -> updateState("output", value == "ON") +// "MC" -> updateState("current", s2d(value)) +// "PC" -> updateState("outCurrent", s2d(value)) +// "MV" -> updateState("voltage", s2d(value)) +// "PV" -> updateState("outVoltage", s2d(value)) +// } +// } @Throws(PortException::class) - private fun getParameter(name: String): String = talk(name + "?") + private fun getParameter(name: String): String = talk("$name?") @Throws(PortException::class) private fun setParameter(key: String, state: String): Boolean = "OK" == talk("$key $state") @@ -248,11 +214,7 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La * Cancel current update task */ fun stopUpdateTask() { - updateTask?.let { - it.cancel(false) - lastUpdate = null - listener?.updateTaskStateChanged(getName(), false) - } + updateTask?.cancel(false) } /** @@ -268,48 +230,44 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La stopUpdateTask() val call = { try { - val measuredI = current ?: 0.0 - val targetI = targetCurrent ?: 0.0 - listener?.acceptMeasuredI(getName(), measuredI) + val measuredI = current.doubleValue + val targetI = target.doubleValue + updateState("current",measuredI) if (Math.abs(measuredI - targetI) > CURRENT_PRECISION) { val nextI = nextI(measuredI, targetI) - listener?.acceptNextI(getName(), nextI) - targetCurrent = nextI + outCurrent = nextI } else { stopUpdateTask() } } catch (ex: PortException) { - reportError("Error in update task", ex) + notifyError("Error in update task", ex) stopUpdateTask() } } updateTask = scheduler.scheduleWithFixedDelay(call, 0, delay.toLong(), TimeUnit.MILLISECONDS) - listener?.updateTaskStateChanged(getName(), true) - updateState("updating", Value.of(true)) + updateState("updating", true) } @Throws(PortException::class) private fun setOutputMode(out: Boolean) { val outState: Int = if (out) 1 else 0 if (!setParameter("OUT", outState)) { - listener?.error(getName(), "Can't set output mode", null) + notifyError("Can't set output mode") } else { - requestStateChange("output", Value.of(out)) - listener?.outputModeChanged(getName(), out) + updateState("output", out) } } private fun nextI(measuredI: Double, targetI: Double): Double { -// assert(measuredI != targetI) +// assert(measuredI != target) - var step = if (lastUpdate == null) { + var step = if (lastUpdate == Instant.EPOCH) { MIN_UP_STEP_SIZE } else { //Choose optimal speed but do not exceed maximum speed - Math.min(MAX_STEP_SIZE, - lastUpdate!!.until(DateTimeUtils.now(), ChronoUnit.MILLIS).toDouble() / 60000.0 * speed) + Math.min(MAX_STEP_SIZE, lastUpdate.until(DateTimeUtils.now(), ChronoUnit.MILLIS).toDouble() / 60000.0 * speed) } val res = if (targetI > measuredI) { @@ -336,18 +294,8 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La private fun stopMonitorTask() { monitorTask?.let { it.cancel(true) - listener?.monitorTaskStateChanged(getName(), false) monitorTask = null } - updateState("output", Value.of(false)) - } - - override fun getName(): String { - return if (this.name.isEmpty()) { - "LAMBDA " + address - } else { - this.name - } } /** @@ -356,38 +304,27 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La * * @param delay an interval between scans in milliseconds */ - @JvmOverloads - fun startMonitorTask(delay: Int = DEFAULT_MONITOR_DELAY) { + private fun startMonitorTask(delay: Int = DEFAULT_MONITOR_DELAY) { assert(delay >= 1000) stopMonitorTask() + val call = Runnable { try { - status + runBlocking { + states["voltage"]?.read() + states["current"]?.read() + } } catch (ex: PortException) { - reportError("Port connection exception during status measurement", ex) + notifyError("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("###.##") @@ -407,5 +344,5 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La */ private fun d2s(d: Double): String = LAMBDA_FORMAT.format(d) } - } + diff --git a/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/SafeLambdaMagnet.kt b/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/SafeLambdaMagnet.kt index 18fc9cd9..f1e2caf4 100644 --- a/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/SafeLambdaMagnet.kt +++ b/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/SafeLambdaMagnet.kt @@ -55,8 +55,8 @@ class SafeLambdaMagnet(context: Context, meta: Meta, controller: LambdaPortContr * @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 } + this.addSafeCondition(false) { I -> Math.abs(controller.current - I) <= tolerance } + controller.addSafeCondition(false) { I -> Math.abs(this.current - I) <= tolerance } } @@ -73,7 +73,7 @@ class SafeLambdaMagnet(context: Context, meta: Meta, controller: LambdaPortContr } } - super.setCurrent(current) + super.current = current } interface SafeMagnetCondition { diff --git a/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/fx/MagnetControllerApp.kt b/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/fx/MagnetControllerApp.kt index 83474188..a6ca111a 100644 --- a/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/fx/MagnetControllerApp.kt +++ b/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/fx/MagnetControllerApp.kt @@ -18,7 +18,6 @@ package inr.numass.control.magnet.fx import ch.qos.logback.classic.Level import ch.qos.logback.classic.spi.ILoggingEvent import ch.qos.logback.core.FileAppender -import hep.dataforge.context.Global import hep.dataforge.exceptions.ControlException import hep.dataforge.io.MetaFileReader import inr.numass.control.magnet.LambdaHub @@ -44,14 +43,13 @@ class MagnetControllerApp : Application() { // internal var controllers: MutableList = ArrayList() private lateinit var device: LambdaHub - val context = Global.instance() @Throws(IOException::class, ControlException::class) override fun start(stage: Stage) { Locale.setDefault(Locale.US)// чтобы отделение десятичных знаков было точкой val rootLogger = LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME) as ch.qos.logback.classic.Logger - val logLevel = (parameters.named as java.util.Map).getOrDefault("logLevel", "INFO") + val logLevel = parameters.named.getOrDefault("logLevel", "INFO") rootLogger.level = Level.valueOf(logLevel) @@ -69,8 +67,6 @@ class MagnetControllerApp : Application() { val config = MetaFileReader.instance().read(context,) - device = - // val portName = (parameters.named as java.util.Map).getOrDefault("port", "virtual") // // if (portName == "virtual") { diff --git a/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/fx/MagnetControllerComponent.kt b/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/fx/MagnetControllerComponent.kt index f5dc3202..519e7c38 100644 --- a/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/fx/MagnetControllerComponent.kt +++ b/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/fx/MagnetControllerComponent.kt @@ -16,20 +16,17 @@ package inr.numass.control.magnet.fx import hep.dataforge.exceptions.PortException +import inr.numass.control.DeviceDisplayFX import inr.numass.control.magnet.LambdaMagnet -import inr.numass.control.magnet.MagnetStateListener -import inr.numass.control.magnet.MagnetStatus import javafx.application.Platform import javafx.beans.value.ObservableValue import javafx.event.ActionEvent import javafx.fxml.FXML -import javafx.fxml.FXMLLoader import javafx.fxml.Initializable import javafx.scene.control.* import javafx.scene.layout.AnchorPane import javafx.scene.paint.Color -import org.slf4j.Logger -import org.slf4j.LoggerFactory +import tornadofx.* import java.net.URL import java.util.* @@ -38,250 +35,221 @@ import java.util.* * * @author Alexander Nozik */ -class MagnetControllerComponent : AnchorPane(), Initializable, MagnetStateListener { +class MagnetDisplay : DeviceDisplayFX() { + override fun buildView(device: LambdaMagnet): MagnetControllerComponent? { + return MagnetControllerComponent(device) + } - private var lambdaMagnet: LambdaMagnet? = null - /** - * @return the logger - */ - var logger: Logger? = null - private set + val current by lazy { valueBinding(device.voltage)} - private var showConfirmation = true + val voltage by lazy { valueBinding(device.current)} - @FXML - private val labelI: Label? = null - @FXML - private val labelU: Label? = null - @FXML - private val targetIField: TextField? = null - @FXML - private val magnetName: Label? = null - @FXML - private val monitorButton: ToggleButton? = null - @FXML - private val statusLabel: Label? = null - @FXML - private val setButton: ToggleButton? = null - @FXML - private val magnetSpeedField: TextField? = null + var target by device.target.doubleDelegate - private val targetI: Double - get() = java.lang.Double.parseDouble(targetIField!!.text) + var output by device.output.booleanDelegate - // public MagnetControllerComponent(LambdaMagnet lambdaMagnet) { - // FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/SingleMagnet.fxml")); - // - // loader.setRoot(this); - // loader.setController(this); - // - // try { - // loader.load(); - // } catch (IOException ex) { - // throw new RuntimeException(ex); - // } - // setLambdaMagnet(lambdaMagnet); - // } - /** - * Initializes the controller class. - * - * @param url - * @param rb - */ - override fun initialize(url: URL, rb: ResourceBundle) { + var monitoring by device.monitoring.booleanDelegate - targetIField!!.textProperty().addListener { observable: ObservableValue, oldValue: String, newValue: String -> - if (!newValue.matches("\\d*(\\.)?\\d*".toRegex())) { - targetIField.text = oldValue + var updating by device.updating.booleanDelegate + + + inner class MagnetControllerComponent(val device: LambdaMagnet) : Fragment(), Initializable { + + override val root: AnchorPane by fxml("/fxml/SingleMagnet.fxml") + + private var showConfirmation = true + + val labelI: Label by fxml() + val labelU: Label by fxml() + val targetIField: TextField by fxml() + val magnetName: Label by fxml() + val monitorButton: ToggleButton by fxml() + val statusLabel: Label by fxml() + val setButton: ToggleButton by fxml() + val magnetSpeedField: TextField by fxml() + + /** + * Initializes the controller class. + * + * @param url + * @param rb + */ + override fun initialize(url: URL, rb: ResourceBundle) { + + targetIField.textProperty().addListener { observable: ObservableValue, oldValue: String, newValue: String -> + if (!newValue.matches("\\d*(\\.)?\\d*".toRegex())) { + targetIField.text = oldValue + } + } + + magnetSpeedField.textProperty().addListener { observable: ObservableValue, oldValue: String, newValue: String -> + if (!newValue.matches("\\d*(\\.)?\\d*".toRegex())) { + magnetSpeedField.text = oldValue + } + } + + magnetName.text = device.name + magnetSpeedField.text = device.speed.toString() + + current.onChange { + runLater { + labelI.text = it?.stringValue() + } + } + + voltage.onChange { + runLater { + labelU.text = it?.stringValue() + } + } + + valueBinding(device.output).onChange{ + Platform.runLater { + if (it?.booleanValue() == true) { + this.statusLabel.text = "OK" + this.statusLabel.textFill = Color.BLUE + } else { + this.statusLabel.text = "OFF" + this.statusLabel.textFill = Color.BLACK + } + } } } - magnetSpeedField!!.textProperty().addListener { observable: ObservableValue, oldValue: String, newValue: String -> - if (!newValue.matches("\\d*(\\.)?\\d*".toRegex())) { - magnetSpeedField.text = oldValue + fun setShowConfirmation(showConfirmation: Boolean) { + this.showConfirmation = showConfirmation + } + + @FXML + private fun onOutToggle(event: ActionEvent) { + try { + setOutput(setButton.isSelected) + } catch (ex: PortException) { + displayError(this.device.name, null, ex) } - } - } - fun setShowConfirmation(showConfirmation: Boolean) { - this.showConfirmation = showConfirmation - } - - @FXML - private fun onOutToggle(event: ActionEvent) { - try { - setOutput(setButton!!.isSelected) - } catch (ex: PortException) { - error(this.lambdaMagnet!!.name, null, ex) } - } + @Throws(PortException::class) + private fun setOutput(outputOn: Boolean) { + if (outputOn) { + if (showConfirmation) { + val alert = Alert(Alert.AlertType.WARNING) + alert.contentText = "Изменение токов в сверхпроводящих магнитах можно производить только при выключенном напряжении на спектрометре." + "\nВы уверены что напряжение выключено?" + alert.headerText = "Проверьте напряжение на спектрометре!" + alert.height = 150.0 + alert.title = "Внимание!" + alert.buttonTypes.clear() + alert.buttonTypes.addAll(ButtonType.YES, ButtonType.CANCEL) - @Throws(PortException::class) - private fun setOutput(outputOn: Boolean) { - if (outputOn) { - if (showConfirmation) { - val alert = Alert(Alert.AlertType.WARNING) - alert.contentText = "Изменение токов в сверхпроводящих магнитах можно производить только при выключенном напряжении на спектрометре." + "\nВы уверены что напряжение выключено?" - alert.headerText = "Проверьте напряжение на спектрометре!" - alert.height = 150.0 - alert.title = "Внимание!" - alert.buttonTypes.clear() - alert.buttonTypes.addAll(ButtonType.YES, ButtonType.CANCEL) - - if (alert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.YES) { - startCurrentChange() + if (alert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.YES) { + startCurrentChange() + } else { + setButton.isSelected = false + } } else { - setButton!!.isSelected = false + startCurrentChange() } } else { - startCurrentChange() + device.stopUpdateTask() + targetIField.isDisable = false + magnetSpeedField.isDisable = false } - } else { - getLambdaMagnet().stopUpdateTask() - targetIField!!.isDisable = false - magnetSpeedField!!.isDisable = false - } - } - - @Throws(PortException::class) - private fun startCurrentChange() { - val speed = java.lang.Double.parseDouble(magnetSpeedField!!.text) - if (speed > 0 && speed <= 7) { - lambdaMagnet!!.speed = speed - magnetSpeedField.isDisable = true - getLambdaMagnet().setOutputMode(true) - getLambdaMagnet().startUpdateTask(targetI) - } else { - val alert = Alert(Alert.AlertType.ERROR) - alert.contentText = null - alert.headerText = "Недопустимое значение скорости изменения тока" - alert.title = "Ошибка!" - alert.show() - setButton!!.isSelected = false - magnetSpeedField.text = java.lang.Double.toString(lambdaMagnet!!.speed) } - } - - @FXML - private fun onMonitorToggle(event: ActionEvent) { - if (monitorButton!!.isSelected) { - getLambdaMagnet().startMonitorTask() - } else { - getLambdaMagnet().stopMonitorTask() - this.labelU!!.text = "----" - } - } - - override fun error(name: String, errorMessage: String?, throwable: Throwable) { - Platform.runLater { - this.statusLabel!!.text = "ERROR" - this.statusLabel.textFill = Color.RED - } - this.logger!!.error("ERROR: {}", errorMessage, throwable) - // MagnetStateListener.super.error(address, errorMessage, throwable); //To change body of generated methods, choose Tools | Templates. - } - - /** - * @return the lambdaMagnet - */ - fun getLambdaMagnet(): LambdaMagnet { - if (lambdaMagnet == null) { - throw RuntimeException("Magnet controller not defined") - } - return lambdaMagnet - } - - /** - * @param lambdaMagnet the lambdaMagnet to set - */ - private fun setLambdaMagnet(lambdaMagnet: LambdaMagnet) { - this.lambdaMagnet = lambdaMagnet - logger = LoggerFactory.getLogger("lambda." + lambdaMagnet.name) - lambdaMagnet.listener = this - magnetName!!.text = lambdaMagnet.name - - magnetSpeedField!!.text = java.lang.Double.toString(this.lambdaMagnet!!.speed) - - } - - override fun acceptStatus(name: String, state: MagnetStatus) { - Platform.runLater { - this.labelI!!.text = java.lang.Double.toString(state.measuredCurrent) - this.labelU!!.text = java.lang.Double.toString(state.measuredVoltage) - outputModeChanged(name, state.isOutputOn) - - logger!!.info(String.format("%s (%s): Im = %f, Um = %f, Is = %f, Us = %f;", - name, - state.isOutputOn, - state.measuredCurrent, - state.measuredVoltage, - state.setCurrent, - state.setVoltage - )) - - } - } - - override fun acceptNextI(name: String, nextI: Double) { - logger!!.debug("{}: nextI = {};", name, nextI) - } - - override fun acceptMeasuredI(name: String, measuredI: Double) { - logger!!.debug("{}: measuredI = {};", name, measuredI) - Platform.runLater { this.labelI!!.text = java.lang.Double.toString(measuredI) } - } - - override fun outputModeChanged(name: String, out: Boolean) { - Platform.runLater { - if (out) { - this.statusLabel!!.text = "OK" - this.statusLabel.textFill = Color.BLUE + @Throws(PortException::class) + private fun startCurrentChange() { + val speed = java.lang.Double.parseDouble(magnetSpeedField.text) + if (speed > 0 && speed <= 7) { + device.speed = speed + magnetSpeedField.isDisable = true + target = targetIField.text.toDouble() + output = true + updating = true } else { - this.statusLabel!!.text = "OFF" - this.statusLabel.textFill = Color.BLACK + val alert = Alert(Alert.AlertType.ERROR) + alert.contentText = null + alert.headerText = "Недопустимое значение скорости изменения тока" + alert.title = "Ошибка!" + alert.show() + setButton.isSelected = false + magnetSpeedField.text = java.lang.Double.toString(device.speed) } + + } + + @FXML + private fun onMonitorToggle(event: ActionEvent) { + device + if (monitorButton.isSelected) { + monitoring = true + } else { + monitoring = false + this.labelU.text = "----" + } + } + + fun displayError(name: String, errorMessage: String?, throwable: Throwable) { + Platform.runLater { + this.statusLabel.text = "ERROR" + this.statusLabel.textFill = Color.RED + } + device.logger.error("ERROR: {}", errorMessage, throwable) + // MagnetStateListener.super.error(address, errorMessage, throwable); //To change body of generated methods, choose Tools | Templates. + } + +// /** +// * @param lambdaMagnet the device to set +// */ +// private fun setLambdaMagnet(lambdaMagnet: LambdaMagnet) { +// this.device = lambdaMagnet +// lambdaMagnet.listener = this +// magnetName.text = lambdaMagnet.name +// +// magnetSpeedField.text = java.lang.Double.toString(this.device.speed) +// +// } + + override fun updateTaskStateChanged(name: String, updateTaskRunning: Boolean) { + this.setButton.isSelected = updateTaskRunning + targetIField.isDisable = updateTaskRunning + } + + override fun monitorTaskStateChanged(name: String, monitorTaskRunning: Boolean) { + this.monitorButton.isScaleShape = monitorTaskRunning + } + + // /** + // * @param logger the logger to set + // */ + // public void setLogger(PrintStream logger) { + // this.logger = logger; + // } + override fun displayState(state: String) { + Platform.runLater { this.statusLabel.text = state } + } + + companion object { + +// fun build(lambdaMagnet: LambdaMagnet): MagnetControllerComponent { +// val component = MagnetControllerComponent() +// val loader = FXMLLoader(lambdaMagnet.javaClass.getResource("/fxml/SingleMagnet.fxml")) +// +// loader.setRoot(component) +// loader.setController(component) +// +// try { +// loader.load() +// } catch (ex: Exception) { +// LoggerFactory.getLogger("FX").error("Error during fxml initialization", ex) +// throw Error(ex) +// } +// +// component.setLambdaMagnet(lambdaMagnet) +// return component +// } } } - override fun updateTaskStateChanged(name: String, updateTaskRunning: Boolean) { - this.setButton!!.isSelected = updateTaskRunning - targetIField!!.isDisable = updateTaskRunning - } - - override fun monitorTaskStateChanged(name: String, monitorTaskRunning: Boolean) { - this.monitorButton!!.isScaleShape = monitorTaskRunning - } - - // /** - // * @param logger the logger to set - // */ - // public void setLogger(PrintStream logger) { - // this.logger = logger; - // } - override fun displayState(state: String) { - Platform.runLater { this.statusLabel!!.text = state } - } - - companion object { - - fun build(lambdaMagnet: LambdaMagnet): MagnetControllerComponent { - val component = MagnetControllerComponent() - val loader = FXMLLoader(lambdaMagnet.javaClass.getResource("/fxml/SingleMagnet.fxml")) - - loader.setRoot(component) - loader.setController(component) - - try { - loader.load() - } catch (ex: Exception) { - LoggerFactory.getLogger("FX").error("Error during fxml initialization", ex) - throw Error(ex) - } - - component.setLambdaMagnet(lambdaMagnet) - return component - } - } } + diff --git a/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/fx/TestApp.kt b/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/fx/TestApp.kt index 2ca6c370..cd10bd5d 100644 --- a/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/fx/TestApp.kt +++ b/numass-control/magnet/src/main/kotlin/inr/numass/control/magnet/fx/TestApp.kt @@ -7,6 +7,7 @@ package inr.numass.control.magnet.fx import ch.qos.logback.classic.Level import ch.qos.logback.classic.Logger +import hep.dataforge.context.Global import hep.dataforge.control.ports.GenericPortController import hep.dataforge.control.ports.Port import hep.dataforge.control.ports.PortFactory @@ -57,7 +58,7 @@ object Talk { } val handler: Port handler = PortFactory.build(portName) - handler.setPhraseCondition { str: String -> str.endsWith("\r") } + val controller = GenericPortController(Global,handler,"\r") // LambdaMagnet controller = new LambdaMagnet(handler, 1); val reader = BufferedReader(InputStreamReader(System.`in`)) @@ -68,7 +69,7 @@ object Talk { while ("exit" != nextString) { try { val start = DateTimeUtils.now() - val answer = GenericPortController.sendAndWait(handler, nextString + "\r", Duration.ofSeconds(1)) + val answer = controller.sendAndWait(nextString + "\r", Duration.ofSeconds(1)) //String answer = controller.request(nextString); System.out.printf("ANSWER (latency = %s): %s;%n", Duration.between(start, DateTimeUtils.now()), answer.trim { it <= ' ' }) } catch (ex: PortException) { diff --git a/numass-control/msp/src/main/kotlin/inr/numass/control/msp/MspDevice.kt b/numass-control/msp/src/main/kotlin/inr/numass/control/msp/MspDevice.kt index b7332ee3..9ee8380f 100644 --- a/numass-control/msp/src/main/kotlin/inr/numass/control/msp/MspDevice.kt +++ b/numass-control/msp/src/main/kotlin/inr/numass/control/msp/MspDevice.kt @@ -62,21 +62,21 @@ class MspDevice(context: Context, meta: Meta) : PortSensor(context, meta) { // private var measurementDelegate: Consumer? = null - val selected: Boolean by valueState("selected").boolean + val selected: Boolean by valueState("selected").booleanDelegate var controlled: Boolean by valueState("controlled") { _, value -> control(value.booleanValue()) - }.boolean + }.booleanDelegate var filament by valueState("filament") { old, value -> selectFilament(value.intValue()) - }.int + }.intDelegate var filamentOn: Boolean by valueState("filamentOn") { _, value -> setFilamentOn(value.booleanValue()) - }.boolean + }.booleanDelegate - var peakJumpZero: Double by valueState("peakJump.zero").double + var peakJumpZero: Double by valueState("peakJump.zero").doubleDelegate private val averagingDuration: Duration = Duration.parse(meta.getString("averagingDuration", "PT30S")) diff --git a/numass-control/msp/src/main/kotlin/inr/numass/control/msp/MspDisplay.kt b/numass-control/msp/src/main/kotlin/inr/numass/control/msp/MspDisplay.kt index 9907d7b7..740f50ec 100644 --- a/numass-control/msp/src/main/kotlin/inr/numass/control/msp/MspDisplay.kt +++ b/numass-control/msp/src/main/kotlin/inr/numass/control/msp/MspDisplay.kt @@ -32,7 +32,7 @@ import hep.dataforge.plots.data.TimePlot.Companion.setMaxItems import hep.dataforge.plots.data.TimePlot.Companion.setPrefItems import hep.dataforge.plots.jfreechart.JFreeChartFrame import hep.dataforge.values.Value -import inr.numass.control.DeviceDisplay +import inr.numass.control.DeviceDisplayFX import inr.numass.control.deviceStateIndicator import inr.numass.control.deviceStateToggle import inr.numass.control.switch @@ -53,7 +53,7 @@ import tornadofx.* * @author darksnake */ -class MspDisplay() : DeviceDisplay(), DeviceListener, NamedValueListener { +class MspDisplay() : DeviceDisplayFX(), DeviceListener, NamedValueListener { private val table = FXCollections.observableHashMap() diff --git a/numass-control/src/main/kotlin/inr/numass/control/DeviceDisplayFX.kt b/numass-control/src/main/kotlin/inr/numass/control/DeviceDisplayFX.kt new file mode 100644 index 00000000..589febb6 --- /dev/null +++ b/numass-control/src/main/kotlin/inr/numass/control/DeviceDisplayFX.kt @@ -0,0 +1,163 @@ +package inr.numass.control + +import hep.dataforge.connections.Connection +import hep.dataforge.control.connections.Roles +import hep.dataforge.control.devices.Device +import hep.dataforge.control.devices.DeviceListener +import hep.dataforge.control.devices.PortSensor +import hep.dataforge.control.devices.Sensor +import hep.dataforge.exceptions.NameNotFoundException +import hep.dataforge.fx.bindWindow +import hep.dataforge.states.State +import hep.dataforge.states.ValueState +import hep.dataforge.values.Value +import javafx.beans.binding.BooleanBinding +import javafx.beans.binding.ObjectBinding +import javafx.beans.property.BooleanProperty +import javafx.beans.property.SimpleObjectProperty +import javafx.geometry.Pos +import javafx.scene.Parent +import javafx.scene.layout.HBox +import javafx.scene.layout.Priority +import tornadofx.* +import java.util.* +import kotlin.reflect.KClass +import kotlin.reflect.full.createInstance + + +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +annotation class DeviceView(val value: KClass>) + +/** + * Get existing view connection or create a new one + */ +fun Device.getDisplay(): DeviceDisplayFX<*> { + val type = (this::class.annotations.find { it is DeviceView } as DeviceView?)?.value ?: DefaultDisplay::class + return optConnection(Roles.VIEW_ROLE, DeviceDisplayFX::class.java).orElseGet { + type.createInstance().also { + connect(it, Roles.VIEW_ROLE, Roles.DEVICE_LISTENER_ROLE); + } + } +} + + +/** + * + * An FX View to represent the device + * Created by darksnake on 14-May-17. + */ +abstract class DeviceDisplayFX : Component(), Connection, DeviceListener { + + private val bindings = HashMap>() + + private val deviceProperty = SimpleObjectProperty(this, "device", null) + val device: D by deviceProperty + + // private val viewProperty = SimpleObjectProperty(this, "view", null) + val view: UIComponent? by lazy { + buildView(device) + } + + override fun isOpen(): Boolean = this.deviceProperty.get() != null + + override fun open(obj: Any) { + if (!isOpen) { + @Suppress("UNCHECKED_CAST") + deviceProperty.set(obj as D) + } else { + log.warning("Connection already opened") + } + + } + + override fun close() { + if (isOpen) { + view?.close() + deviceProperty.set(null) + } + } + + protected abstract fun buildView(device: D): UIComponent?; + + /** + * Create a binding for specific state and register it in update listener + */ + private fun bindState(state: State): ObjectBinding { + val binding = object : ObjectBinding() { + override fun computeValue(): T { + return state.value + } + } + bindings.putIfAbsent(state.name, binding) + return binding + } + + fun valueBinding(state: ValueState): ObjectBinding{ + return bindState(state) + } + + fun valueBinding(stateName: String): ObjectBinding { + val state: ValueState = device.states.filterIsInstance(ValueState::class.java).find { it.name == stateName } + ?: throw NameNotFoundException("State with name $stateName not found") + return valueBinding(state) + } + + fun booleanBinding(stateName: String): BooleanBinding { + return valueBinding(stateName).booleanBinding { it?.booleanValue() ?: false } + } + + /** + * Bind existing boolean property to writable device state + + * @param state + * @param property + */ + protected fun bindBooleanToState(state: String, property: BooleanProperty) { + valueBinding(state).addListener { _, oldValue, newValue -> + if (isOpen && oldValue != newValue) { + runLater { property.value = newValue.booleanValue() } + } + } + property.addListener { _, oldValue, newValue -> + if (isOpen && oldValue != newValue) { + device.states[state] = newValue + } + } + } + + override fun notifyStateChanged(device: Device, name: String, state: Any) { + bindings[name]?.invalidate() + } + + open fun getBoardView(): Parent { + return HBox().apply { + alignment = Pos.CENTER_LEFT + vgrow = Priority.ALWAYS; + deviceStateIndicator(this@DeviceDisplayFX, Device.INITIALIZED_STATE) + deviceStateIndicator(this@DeviceDisplayFX, PortSensor.CONNECTED_STATE) + deviceStateIndicator(this@DeviceDisplayFX, Sensor.MEASURING_STATE) + deviceStateIndicator(this@DeviceDisplayFX, "storing") + pane { + hgrow = Priority.ALWAYS + } + togglebutton("View") { + isSelected = false + if (view == null) { + isDisable = true + } + view?.bindWindow(selectedProperty()) + } + } + } +} + + +/** + * Default display shows only board pane and nothing else + */ +class DefaultDisplay : DeviceDisplayFX() { + override fun buildView(device: Device): UIComponent? = null +} + diff --git a/numass-control/src/main/kotlin/inr/numass/control/FXExtensions.kt b/numass-control/src/main/kotlin/inr/numass/control/FXExtensions.kt index f1c72105..937c2246 100644 --- a/numass-control/src/main/kotlin/inr/numass/control/FXExtensions.kt +++ b/numass-control/src/main/kotlin/inr/numass/control/FXExtensions.kt @@ -2,8 +2,8 @@ package inr.numass.control import hep.dataforge.fx.plots.PlotContainer import hep.dataforge.kodex.KMetaBuilder -import hep.dataforge.plots.Plot import hep.dataforge.plots.PlotFrame +import hep.dataforge.plots.Plottable import hep.dataforge.plots.jfreechart.JFreeChartFrame import hep.dataforge.values.Value import javafx.beans.value.ObservableValue @@ -17,7 +17,6 @@ import javafx.scene.shape.Circle import javafx.scene.shape.StrokeType import org.controlsfx.control.ToggleSwitch import tornadofx.* -import java.util.* /** @@ -74,7 +73,7 @@ class Indicator(radius: Double = 10.0) : Circle(radius, Color.GRAY) { fun EventTarget.indicator(radius: Double = 10.0, op: (Indicator.() -> Unit) = {}): Indicator = opcr(this, Indicator(radius), op) -fun Indicator.bind(connection: DeviceDisplay<*>, state: String, transform: ((Value) -> Paint)? = null) { +fun Indicator.bind(connection: DeviceDisplayFX<*>, state: String, transform: ((Value) -> Paint)? = null) { tooltip(state) if (transform != null) { bind(connection.getValueBinding(state), transform); @@ -92,7 +91,7 @@ fun Indicator.bind(connection: DeviceDisplay<*>, state: String, transform: ((Val /** * State name + indicator */ -fun EventTarget.deviceStateIndicator(connection: DeviceDisplay<*>, state: String, showName: Boolean = true, transform: ((Value) -> Paint)? = null) { +fun EventTarget.deviceStateIndicator(connection: DeviceDisplayFX<*>, state: String, showName: Boolean = true, transform: ((Value) -> Paint)? = null) { if (connection.device.stateNames.contains(state)) { if (showName) { text("${state.toUpperCase()}: ") @@ -109,7 +108,7 @@ fun EventTarget.deviceStateIndicator(connection: DeviceDisplay<*>, state: String /** * A togglebutton + indicator for boolean state */ -fun Node.deviceStateToggle(connection: DeviceDisplay<*>, state: String, title: String = state) { +fun Node.deviceStateToggle(connection: DeviceDisplayFX<*>, state: String, title: String = state) { if (connection.device.stateNames.contains(state)) { togglebutton(title) { isSelected = false @@ -136,11 +135,11 @@ fun EventTarget.switch(text: String = "", op: (ToggleSwitch.() -> Unit) = {}): T /** * Add frame */ -fun BorderPane.plot(plottables: Iterable = Collections.emptyList(), metaTransform: (KMetaBuilder.() -> Unit)? = null): PlotFrame { +fun BorderPane.plot(plottable: Plottable, metaTransform: (KMetaBuilder.() -> Unit)? = null): PlotFrame { val meta = KMetaBuilder("plotFrame"); metaTransform?.invoke(meta) - val plot = JFreeChartFrame(meta) - plot.addAll(plottables) - center = PlotContainer(plot).root - return plot; + val frame = JFreeChartFrame(meta) + frame.add(plottable) + center = PlotContainer(frame).root + return frame; } \ No newline at end of file diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/CM32Device.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/CM32Device.kt index f934c1f2..303e3c83 100644 --- a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/CM32Device.kt +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/CM32Device.kt @@ -34,25 +34,21 @@ class CM32Device(context: Context, meta: Meta) : PortSensor(context, meta) { override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) { measurement { - doMeasure() - } - } + val answer = sendAndWait("MES R PM 1\r\n") - private fun doMeasure(): Meta{ - val answer = sendAndWait("MES R PM 1\r\n") - - return if (answer.isEmpty()) { - updateState(PortSensor.CONNECTED_STATE, false) - produceError("No signal") - } else if (!answer.contains("PM1:mbar")) { - updateState(PortSensor.CONNECTED_STATE, false) - produceError("Wrong answer: $answer") - } else if (answer.substring(14, 17) == "OFF") { - updateState(PortSensor.CONNECTED_STATE, true) - produceError("Off") - } else { - updateState(PortSensor.CONNECTED_STATE, true) - produceResult(answer.substring(14, 17) + answer.substring(19, 23)) + if (answer.isEmpty()) { + updateState(PortSensor.CONNECTED_STATE, false) + notifyError("No signal") + } else if (!answer.contains("PM1:mbar")) { + updateState(PortSensor.CONNECTED_STATE, false) + notifyError("Wrong answer: $answer") + } else if (answer.substring(14, 17) == "OFF") { + updateState(PortSensor.CONNECTED_STATE, true) + notifyError("Off") + } else { + updateState(PortSensor.CONNECTED_STATE, true) + notifyResult(answer.substring(14, 17) + answer.substring(19, 23)) + } } } diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/ConsoleVac.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/ConsoleVac.kt index a16182ac..4307770e 100644 --- a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/ConsoleVac.kt +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/ConsoleVac.kt @@ -1,6 +1,8 @@ package inr.numass.control.readvac import hep.dataforge.control.devices.Sensor +import hep.dataforge.control.devices.Sensor.Companion.RESULT_VALUE +import kotlinx.coroutines.experimental.runBlocking import org.apache.commons.cli.DefaultParser import org.apache.commons.cli.HelpFormatter import org.apache.commons.cli.Options @@ -12,9 +14,9 @@ import java.time.Instant * Created by darksnake on 06-Dec-16. */ object ConsoleVac { - private fun Sensor.read():Double{ + private fun Sensor.read(): Double { this.measure() - + return runBlocking { resultState.future.await().getDouble(RESULT_VALUE)} } @Throws(Exception::class) diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MKSBaratronDevice.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MKSBaratronDevice.kt index af309457..e45454f6 100644 --- a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MKSBaratronDevice.kt +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MKSBaratronDevice.kt @@ -13,6 +13,7 @@ import hep.dataforge.control.ports.PortFactory import hep.dataforge.description.ValueDef import hep.dataforge.meta.Meta import hep.dataforge.states.StateDef +import hep.dataforge.states.valueState import hep.dataforge.values.ValueType import inr.numass.control.DeviceView @@ -24,7 +25,7 @@ import inr.numass.control.DeviceView @StateDef(value = ValueDef(name = "channel", type = [ValueType.NUMBER], def = "2"), writable = true) class MKSBaratronDevice(context: Context, meta: Meta) : PortSensor(context, meta) { - var channel by intState("channel") + var channel by valueState("channel").intDelegate override fun getType(): String { return meta.getString("type", "numass.vac.baratron") @@ -38,24 +39,20 @@ class MKSBaratronDevice(context: Context, meta: Meta) : PortSensor(context, meta override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) { measurement { - doMeasure() - } - } - - private fun doMeasure(): Meta { - val answer = sendAndWait("AV$channel\r") - if (answer.isEmpty()) { - // invalidateState("connection"); - updateState(PortSensor.CONNECTED_STATE, false) - return produceError("No connection") - } else { - updateState(PortSensor.CONNECTED_STATE, true) - } - val res = java.lang.Double.parseDouble(answer) - return if (res <= 0) { - produceError("Non positive") - } else { - produceResult(res) + val answer = sendAndWait("AV$channel\r") + if (answer.isEmpty()) { + // invalidateState("connection"); + updateState(PortSensor.CONNECTED_STATE, false) + notifyError("No connection") + } else { + updateState(PortSensor.CONNECTED_STATE, true) + } + val res = java.lang.Double.parseDouble(answer) + if (res <= 0) { + notifyError("Non positive") + } else { + notifyResult(res) + } } } } diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MKSVacDevice.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MKSVacDevice.kt index 7eb6309e..dc687f78 100644 --- a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MKSVacDevice.kt +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MKSVacDevice.kt @@ -43,7 +43,7 @@ class MKSVacDevice(context: Context, meta: Meta) : PortSensor(context, meta) { if (old != value) { setPowerOn(value.booleanValue()) } - }.boolean + }.booleanDelegate @Throws(ControlException::class) diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MeradatVacDevice.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MeradatVacDevice.kt index 39285ed4..011070e8 100644 --- a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MeradatVacDevice.kt +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/MeradatVacDevice.kt @@ -13,6 +13,7 @@ import hep.dataforge.control.ports.PortFactory import hep.dataforge.description.ValueDef import hep.dataforge.meta.Meta import hep.dataforge.states.StateDef +import hep.dataforge.states.valueState import hep.dataforge.values.ValueType.NUMBER import java.math.BigDecimal import java.math.BigInteger @@ -25,7 +26,7 @@ import java.util.regex.Pattern @StateDef(value = ValueDef(name = "address", type = [NUMBER], def = "1", info = "A modbus address"), writable = true) class MeradatVacDevice(context: Context, meta: Meta) : PortSensor(context, meta) { - var address by intState("address") + var address by valueState("address").intDelegate override fun connect(meta: Meta): GenericPortController { val port: Port = PortFactory.build(meta) @@ -40,43 +41,37 @@ class MeradatVacDevice(context: Context, meta: Meta) : PortSensor(context, meta) override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) { measurement{ - doMeasure() - } - } + val requestBase: String = String.format(":%02d", address) + val dataStr = requestBase.substring(1) + REQUEST + val query = requestBase + REQUEST + calculateLRC(dataStr) + "\r\n" // ":010300000002FA\r\n"; + val response: Pattern = Pattern.compile(requestBase + "0304(\\w{4})(\\w{4})..\r\n") + val answer = sendAndWait(query) { phrase -> phrase.startsWith(requestBase) } - private fun doMeasure(): Meta { - val requestBase: String = String.format(":%02d", address) - val dataStr = requestBase.substring(1) + REQUEST - val query = requestBase + REQUEST + calculateLRC(dataStr) + "\r\n" // ":010300000002FA\r\n"; - val response: Pattern = Pattern.compile(requestBase + "0304(\\w{4})(\\w{4})..\r\n") - - val answer = sendAndWait(query) { phrase -> phrase.startsWith(requestBase) } - - if (answer.isEmpty()) { - updateState(PortSensor.CONNECTED_STATE, false) - return produceError("No signal") - } else { - val match = response.matcher(answer) - - return 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 -= 65536 - } - var res = BigDecimal.valueOf(base * Math.pow(10.0, exp.toDouble())) - res = res.setScale(4, RoundingMode.CEILING) - updateState(PortSensor.CONNECTED_STATE, true) - produceResult(res) - } else { + if (answer.isEmpty()) { updateState(PortSensor.CONNECTED_STATE, false) - produceError("Wrong answer: $answer") + notifyError("No signal") + } 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 -= 65536 + } + var res = BigDecimal.valueOf(base * Math.pow(10.0, exp.toDouble())) + res = res.setScale(4, RoundingMode.CEILING) + updateState(PortSensor.CONNECTED_STATE, true) + notifyResult(res) + } else { + updateState(PortSensor.CONNECTED_STATE, false) + notifyError("Wrong answer: $answer") + } } } } - companion object { private const val REQUEST = "0300000002" diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDevice.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDevice.kt index 99ab1273..ef2ec2b9 100644 --- a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDevice.kt +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDevice.kt @@ -49,20 +49,16 @@ class VacCollectorDevice(context: Context, meta: Meta, val sensors: Collection - if (sensor.optBooleanState(CONNECTED_STATE).orElse(false)) { + if (sensor.states.getBoolean(CONNECTED_STATE,false)) { sensor.measure() } } diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDisplay.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDisplay.kt index 11d42b59..abfa476e 100644 --- a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDisplay.kt +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDisplay.kt @@ -12,9 +12,10 @@ import hep.dataforge.control.measurements.Measurement import hep.dataforge.control.measurements.MeasurementListener import hep.dataforge.fx.bindWindow import hep.dataforge.fx.fragments.LogFragment +import hep.dataforge.plots.PlotGroup import hep.dataforge.plots.data.TimePlot import hep.dataforge.values.Value -import inr.numass.control.DeviceDisplay +import inr.numass.control.DeviceDisplayFX import inr.numass.control.deviceStateToggle import inr.numass.control.plot import javafx.collections.FXCollections @@ -30,7 +31,7 @@ import java.time.Instant * @author [Alexander Nozik](mailto:altavir@gmail.com) */ -class VacCollectorDisplay : DeviceDisplay() { +class VacCollectorDisplay : DeviceDisplayFX() { private val table = FXCollections.observableHashMap() @@ -48,7 +49,7 @@ class VacCollectorDisplay : DeviceDisplay() { private val viewList = FXCollections.observableArrayList(); - override fun buildView(device: VacCollectorDevice): View { + override fun buildView(device: VacCollectorDevice): UIComponent { return VacCollectorView(); } @@ -62,15 +63,15 @@ class VacCollectorDisplay : DeviceDisplay() { } } - inner class VacCollectorView : View("Numass vacuum view") { + inner class VacCollectorView : Fragment("Numass vacuum view") { - private val plottables = TimePlottableGroup().apply { + private val plottables = PlotGroup.typed("vac").apply { viewList.forEach { val plot = TimePlot(it.getTitle(), it.device.name) plot.configure(it.device.meta) add(plot) } - setValue("thickness", 3) + configureValue("thickness", 3) } // private val logWindow = FragmentWindow(LogFragment().apply { @@ -127,13 +128,13 @@ class VacCollectorDisplay : DeviceDisplay() { init { table.addListener { change: MapChangeListener.Change -> if (change.wasAdded()) { - val pl = plottables.get(change.key) + val pl = plottables[change.key] val value = change.valueAdded - if (pl != null) { + (pl as? TimePlot)?.let { if (value > 0) { - pl.put(Value.of(value)) + it.put(Value.of(value)) } else { - pl.put(Value.NULL) + it.put(Value.NULL) } } } diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacDeviceFactory.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacDeviceFactory.kt index ecabf0b7..20b1d7c8 100644 --- a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacDeviceFactory.kt +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacDeviceFactory.kt @@ -34,7 +34,7 @@ class VacDeviceFactory : DeviceFactory { return VacCollectorDevice(context, config, sensors) } -// override fun buildView(device: Device): DeviceDisplay { +// override fun buildView(device: Device): DeviceDisplayFX { // return VacCollectorDisplay(); // } } diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacDisplay.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacDisplay.kt index 51118a19..d618b019 100644 --- a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacDisplay.kt +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacDisplay.kt @@ -7,7 +7,7 @@ package inr.numass.control.readvac import hep.dataforge.control.devices.PortSensor.Companion.CONNECTED_STATE import hep.dataforge.control.devices.Sensor -import inr.numass.control.DeviceDisplay +import inr.numass.control.DeviceDisplayFX import inr.numass.control.switch import javafx.application.Platform import javafx.beans.property.SimpleObjectProperty @@ -28,7 +28,7 @@ import java.time.format.DateTimeFormatter /** * @author [Alexander Nozik](mailto:altavir@gmail.com) */ -open class VacDisplay : DeviceDisplay() { +open class VacDisplay : DeviceDisplayFX() { val statusProperty = SimpleStringProperty("") var status: String by statusProperty