Fix of config editor refresh. Multiple fixes in control.

This commit is contained in:
Alexander Nozik 2018-04-04 20:37:04 +03:00
parent 0085ff3142
commit af54525d41
20 changed files with 568 additions and 515 deletions

View File

@ -79,11 +79,11 @@ class PKT8Device(context: Context, meta: Meta) : PortSensor(context, meta) {
tableFormatBuilder.build() 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")) private val duration = Duration.parse(meta.getString("averagingDuration", "PT30S"))

View File

@ -12,7 +12,7 @@ import hep.dataforge.plots.PlotFrame
import hep.dataforge.plots.PlotUtils import hep.dataforge.plots.PlotUtils
import hep.dataforge.plots.data.TimePlot import hep.dataforge.plots.data.TimePlot
import hep.dataforge.plots.jfreechart.JFreeChartFrame import hep.dataforge.plots.jfreechart.JFreeChartFrame
import inr.numass.control.DeviceDisplay import inr.numass.control.DeviceDisplayFX
import javafx.beans.binding.ListBinding import javafx.beans.binding.ListBinding
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
import javafx.collections.FXCollections import javafx.collections.FXCollections
@ -30,7 +30,7 @@ import java.time.Instant
/** /**
* Created by darksnake on 30-May-17. * Created by darksnake on 30-May-17.
*/ */
class PKT8Display : DeviceDisplay<PKT8Device>(), PKT8ValueListener { class PKT8Display : DeviceDisplayFX<PKT8Device>(), PKT8ValueListener {
override fun buildView(device: PKT8Device) = CryoView() override fun buildView(device: PKT8Device) = CryoView()

View File

@ -17,9 +17,7 @@ package inr.numass.control.magnet
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.control.devices.AbstractDevice import hep.dataforge.control.devices.AbstractDevice
import hep.dataforge.control.devices.booleanState import hep.dataforge.control.ports.Port
import hep.dataforge.control.devices.doubleState
import hep.dataforge.control.devices.timeState
import hep.dataforge.control.ports.PortFactory import hep.dataforge.control.ports.PortFactory
import hep.dataforge.description.ValueDef import hep.dataforge.description.ValueDef
import hep.dataforge.exceptions.ControlException import hep.dataforge.exceptions.ControlException
@ -27,12 +25,14 @@ import hep.dataforge.exceptions.PortException
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.states.StateDef import hep.dataforge.states.StateDef
import hep.dataforge.states.StateDefs import hep.dataforge.states.StateDefs
import hep.dataforge.states.valueState
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 kotlinx.coroutines.experimental.runBlocking
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,12 +41,12 @@ import java.util.concurrent.TimeUnit
/** /**
* @author Polina * @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( @StateDefs(
StateDef(value = ValueDef(name = "current", type = arrayOf(NUMBER), def = "0", info = "Current current")), 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 = "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 = "outCurrent", 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 = "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 = "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 = "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), 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 var closePortOnShutDown = false
private val name: String = meta.getString("name", "LAMBDA")
/** /**
* @return the address * @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) private val scheduler = ScheduledThreadPoolExecutor(1)
var listener: MagnetStateListener? = null //var listener: MagnetStateListener? = null
// private volatile double current = 0; // private volatile double current = 0;
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
var lastUpdate by timeState() var lastUpdate by valueState("lastUpdate", getter = {0}).timeDelegate
private set private set
val current by doubleState() // read-only values of current output
val voltage by doubleState() val current = valueState("current", getter = { controller.talk(address, timeout) { s2d(getParameter("MC")) } })
var targetCurrent by doubleState() // val current by current.double
var targetVoltage by doubleState()
var output by booleanState() val voltage = valueState("voltage", getter = { controller.talk(address, timeout) { s2d(getParameter("MV")) } })
var monitoring by booleanState() // 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 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) @Throws(ControlException::class)
override fun init() { override fun init() {
super.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?) { // private fun reportError(errorMessage: String, error: Throwable?) {
listener?.error(getName(), errorMessage, error) ?: LoggerFactory.getLogger(javaClass).error(errorMessage, error) // listener?.error(name, errorMessage, error) ?: LoggerFactory.getLogger(javaClass).error(errorMessage, error)
//
} // }
@Throws(PortException::class) @Throws(PortException::class)
private fun talk(request: String): String { private fun talk(request: String): String {
try { return try {
controller.send(request + "\r") controller.send(request + "\r")
return controller.waitFor(timeout).trim() controller.waitFor(timeout).trim()
} catch (tex: PortTimeoutException) { } catch (tex: Port.PortTimeoutException) {
//Single retry on timeout //Single retry on timeout
LoggerFactory.getLogger(javaClass).warn("A timeout exception for request '$request'. Making another attempt.") LoggerFactory.getLogger(javaClass).warn("A timeout exception for request '$request'. Making another attempt.")
controller.send(request + "\r") controller.send(request + "\r")
return controller.waitFor(timeout).trim() controller.waitFor(timeout).trim()
} }
} }
private fun update(key: String, value: String) { // private fun update(key: String, value: String) {
when (key) { // when (key) {
"OUT" -> updateState("output", value == "ON") // "OUT" -> updateState("output", value == "ON")
"MC" -> updateState("current", s2d(value)) // "MC" -> updateState("current", s2d(value))
"PC" -> updateState("targetCurrent", s2d(value)) // "PC" -> updateState("outCurrent", s2d(value))
"MV" -> updateState("voltage", s2d(value)) // "MV" -> updateState("voltage", s2d(value))
"PV" -> updateState("targetVoltage", s2d(value)) // "PV" -> updateState("outVoltage", s2d(value))
} // }
} // }
@Throws(PortException::class) @Throws(PortException::class)
private fun getParameter(name: String): String = talk(name + "?") private fun getParameter(name: String): String = talk("$name?")
@Throws(PortException::class) @Throws(PortException::class)
private fun setParameter(key: String, state: String): Boolean = "OK" == talk("$key $state") 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 * Cancel current update task
*/ */
fun stopUpdateTask() { fun stopUpdateTask() {
updateTask?.let { updateTask?.cancel(false)
it.cancel(false)
lastUpdate = null
listener?.updateTaskStateChanged(getName(), false)
}
} }
/** /**
@ -268,48 +230,44 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
stopUpdateTask() stopUpdateTask()
val call = { val call = {
try { try {
val measuredI = current ?: 0.0 val measuredI = current.doubleValue
val targetI = targetCurrent ?: 0.0 val targetI = target.doubleValue
listener?.acceptMeasuredI(getName(), measuredI) updateState("current",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) outCurrent = nextI
targetCurrent = nextI
} else { } else {
stopUpdateTask() stopUpdateTask()
} }
} catch (ex: PortException) { } catch (ex: PortException) {
reportError("Error in update task", ex) notifyError("Error in update task", ex)
stopUpdateTask() stopUpdateTask()
} }
} }
updateTask = scheduler.scheduleWithFixedDelay(call, 0, delay.toLong(), TimeUnit.MILLISECONDS) updateTask = scheduler.scheduleWithFixedDelay(call, 0, delay.toLong(), TimeUnit.MILLISECONDS)
listener?.updateTaskStateChanged(getName(), true) updateState("updating", true)
updateState("updating", Value.of(true))
} }
@Throws(PortException::class) @Throws(PortException::class)
private fun setOutputMode(out: Boolean) { private fun setOutputMode(out: Boolean) {
val outState: Int = if (out) 1 else 0 val outState: Int = if (out) 1 else 0
if (!setParameter("OUT", outState)) { if (!setParameter("OUT", outState)) {
listener?.error(getName(), "Can't set output mode", null) notifyError("Can't set output mode")
} else { } else {
requestStateChange("output", Value.of(out)) updateState("output", out)
listener?.outputModeChanged(getName(), out)
} }
} }
private fun nextI(measuredI: Double, targetI: Double): Double { 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 MIN_UP_STEP_SIZE
} else { } else {
//Choose optimal speed but do not exceed maximum speed //Choose optimal speed but do not exceed maximum speed
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 = if (targetI > measuredI) { val res = if (targetI > measuredI) {
@ -336,18 +294,8 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
private fun stopMonitorTask() { private fun stopMonitorTask() {
monitorTask?.let { monitorTask?.let {
it.cancel(true) it.cancel(true)
listener?.monitorTaskStateChanged(getName(), false)
monitorTask = null 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 * @param delay an interval between scans in milliseconds
*/ */
@JvmOverloads private fun startMonitorTask(delay: Int = DEFAULT_MONITOR_DELAY) {
fun startMonitorTask(delay: Int = DEFAULT_MONITOR_DELAY) {
assert(delay >= 1000) assert(delay >= 1000)
stopMonitorTask() stopMonitorTask()
val call = Runnable { val call = Runnable {
try { try {
status runBlocking {
states["voltage"]?.read()
states["current"]?.read()
}
} catch (ex: PortException) { } catch (ex: PortException) {
reportError("Port connection exception during status measurement", ex) notifyError("Port connection exception during status measurement", ex)
stopMonitorTask() stopMonitorTask()
} }
} }
monitorTask = scheduler.scheduleWithFixedDelay(call, 0, delay.toLong(), TimeUnit.MILLISECONDS) 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 { companion object {
private val LAMBDA_FORMAT = DecimalFormat("###.##") 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) private fun d2s(d: Double): String = LAMBDA_FORMAT.format(d)
} }
} }

View File

@ -55,8 +55,8 @@ class SafeLambdaMagnet(context: Context, meta: Meta, controller: LambdaPortContr
* @param tolerance * @param tolerance
*/ */
fun bindTo(controller: SafeLambdaMagnet, tolerance: Double) { fun bindTo(controller: SafeLambdaMagnet, tolerance: Double) {
this.addSafeCondition(false) { I -> Math.abs(controller.getCurrent() - I) <= tolerance } this.addSafeCondition(false) { I -> Math.abs(controller.current - I) <= tolerance }
controller.addSafeCondition(false) { I -> Math.abs(this.getCurrent() - 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 { interface SafeMagnetCondition {

View File

@ -18,7 +18,6 @@ package inr.numass.control.magnet.fx
import ch.qos.logback.classic.Level import ch.qos.logback.classic.Level
import ch.qos.logback.classic.spi.ILoggingEvent import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.FileAppender import ch.qos.logback.core.FileAppender
import hep.dataforge.context.Global
import hep.dataforge.exceptions.ControlException import hep.dataforge.exceptions.ControlException
import hep.dataforge.io.MetaFileReader import hep.dataforge.io.MetaFileReader
import inr.numass.control.magnet.LambdaHub import inr.numass.control.magnet.LambdaHub
@ -44,14 +43,13 @@ class MagnetControllerApp : Application() {
// internal var controllers: MutableList<SafeLambdaMagnet> = ArrayList() // internal var controllers: MutableList<SafeLambdaMagnet> = ArrayList()
private lateinit var device: LambdaHub private lateinit var device: LambdaHub
val context = Global.instance()
@Throws(IOException::class, ControlException::class) @Throws(IOException::class, ControlException::class)
override fun start(stage: Stage) { override fun start(stage: Stage) {
Locale.setDefault(Locale.US)// чтобы отделение десятичных знаков было точкой Locale.setDefault(Locale.US)// чтобы отделение десятичных знаков было точкой
val rootLogger = LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME) as ch.qos.logback.classic.Logger 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<String, String>).getOrDefault("logLevel", "INFO") val logLevel = parameters.named.getOrDefault("logLevel", "INFO")
rootLogger.level = Level.valueOf(logLevel) rootLogger.level = Level.valueOf(logLevel)
@ -69,8 +67,6 @@ class MagnetControllerApp : Application() {
val config = MetaFileReader.instance().read(context,) val config = MetaFileReader.instance().read(context,)
device =
// val portName = (parameters.named as java.util.Map<String, String>).getOrDefault("port", "virtual") // val portName = (parameters.named as java.util.Map<String, String>).getOrDefault("port", "virtual")
// //
// if (portName == "virtual") { // if (portName == "virtual") {

View File

@ -16,20 +16,17 @@
package inr.numass.control.magnet.fx package inr.numass.control.magnet.fx
import hep.dataforge.exceptions.PortException import hep.dataforge.exceptions.PortException
import inr.numass.control.DeviceDisplayFX
import inr.numass.control.magnet.LambdaMagnet import inr.numass.control.magnet.LambdaMagnet
import inr.numass.control.magnet.MagnetStateListener
import inr.numass.control.magnet.MagnetStatus
import javafx.application.Platform import javafx.application.Platform
import javafx.beans.value.ObservableValue import javafx.beans.value.ObservableValue
import javafx.event.ActionEvent import javafx.event.ActionEvent
import javafx.fxml.FXML import javafx.fxml.FXML
import javafx.fxml.FXMLLoader
import javafx.fxml.Initializable import javafx.fxml.Initializable
import javafx.scene.control.* import javafx.scene.control.*
import javafx.scene.layout.AnchorPane import javafx.scene.layout.AnchorPane
import javafx.scene.paint.Color import javafx.scene.paint.Color
import org.slf4j.Logger import tornadofx.*
import org.slf4j.LoggerFactory
import java.net.URL import java.net.URL
import java.util.* import java.util.*
@ -38,50 +35,39 @@ import java.util.*
* *
* @author Alexander Nozik * @author Alexander Nozik
*/ */
class MagnetControllerComponent : AnchorPane(), Initializable, MagnetStateListener { class MagnetDisplay : DeviceDisplayFX<LambdaMagnet>() {
override fun buildView(device: LambdaMagnet): MagnetControllerComponent? {
return MagnetControllerComponent(device)
}
private var lambdaMagnet: LambdaMagnet? = null val current by lazy { valueBinding(device.voltage)}
/**
* @return the logger val voltage by lazy { valueBinding(device.current)}
*/
var logger: Logger? = null var target by device.target.doubleDelegate
private set
var output by device.output.booleanDelegate
var monitoring by device.monitoring.booleanDelegate
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 private var showConfirmation = true
@FXML val labelI: Label by fxml()
private val labelI: Label? = null val labelU: Label by fxml()
@FXML val targetIField: TextField by fxml()
private val labelU: Label? = null val magnetName: Label by fxml()
@FXML val monitorButton: ToggleButton by fxml()
private val targetIField: TextField? = null val statusLabel: Label by fxml()
@FXML val setButton: ToggleButton by fxml()
private val magnetName: Label? = null val magnetSpeedField: TextField by fxml()
@FXML
private val monitorButton: ToggleButton? = null
@FXML
private val statusLabel: Label? = null
@FXML
private val setButton: ToggleButton? = null
@FXML
private val magnetSpeedField: TextField? = null
private val targetI: Double
get() = java.lang.Double.parseDouble(targetIField!!.text)
// 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. * Initializes the controller class.
* *
@ -90,17 +76,44 @@ class MagnetControllerComponent : AnchorPane(), Initializable, MagnetStateListen
*/ */
override fun initialize(url: URL, rb: ResourceBundle) { override fun initialize(url: URL, rb: ResourceBundle) {
targetIField!!.textProperty().addListener { observable: ObservableValue<out String>, oldValue: String, newValue: String -> targetIField.textProperty().addListener { observable: ObservableValue<out String>, oldValue: String, newValue: String ->
if (!newValue.matches("\\d*(\\.)?\\d*".toRegex())) { if (!newValue.matches("\\d*(\\.)?\\d*".toRegex())) {
targetIField.text = oldValue targetIField.text = oldValue
} }
} }
magnetSpeedField!!.textProperty().addListener { observable: ObservableValue<out String>, oldValue: String, newValue: String -> magnetSpeedField.textProperty().addListener { observable: ObservableValue<out String>, oldValue: String, newValue: String ->
if (!newValue.matches("\\d*(\\.)?\\d*".toRegex())) { if (!newValue.matches("\\d*(\\.)?\\d*".toRegex())) {
magnetSpeedField.text = oldValue 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
}
}
}
} }
fun setShowConfirmation(showConfirmation: Boolean) { fun setShowConfirmation(showConfirmation: Boolean) {
@ -110,9 +123,9 @@ class MagnetControllerComponent : AnchorPane(), Initializable, MagnetStateListen
@FXML @FXML
private fun onOutToggle(event: ActionEvent) { private fun onOutToggle(event: ActionEvent) {
try { try {
setOutput(setButton!!.isSelected) setOutput(setButton.isSelected)
} catch (ex: PortException) { } catch (ex: PortException) {
error(this.lambdaMagnet!!.name, null, ex) displayError(this.device.name, null, ex)
} }
} }
@ -132,126 +145,78 @@ class MagnetControllerComponent : AnchorPane(), Initializable, MagnetStateListen
if (alert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.YES) { if (alert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.YES) {
startCurrentChange() startCurrentChange()
} else { } else {
setButton!!.isSelected = false setButton.isSelected = false
} }
} else { } else {
startCurrentChange() startCurrentChange()
} }
} else { } else {
getLambdaMagnet().stopUpdateTask() device.stopUpdateTask()
targetIField!!.isDisable = false targetIField.isDisable = false
magnetSpeedField!!.isDisable = false magnetSpeedField.isDisable = false
} }
} }
@Throws(PortException::class) @Throws(PortException::class)
private fun startCurrentChange() { private fun startCurrentChange() {
val speed = java.lang.Double.parseDouble(magnetSpeedField!!.text) val speed = java.lang.Double.parseDouble(magnetSpeedField.text)
if (speed > 0 && speed <= 7) { if (speed > 0 && speed <= 7) {
lambdaMagnet!!.speed = speed device.speed = speed
magnetSpeedField.isDisable = true magnetSpeedField.isDisable = true
getLambdaMagnet().setOutputMode(true) target = targetIField.text.toDouble()
getLambdaMagnet().startUpdateTask(targetI) output = true
updating = true
} else { } else {
val alert = Alert(Alert.AlertType.ERROR) val alert = Alert(Alert.AlertType.ERROR)
alert.contentText = null alert.contentText = null
alert.headerText = "Недопустимое значение скорости изменения тока" alert.headerText = "Недопустимое значение скорости изменения тока"
alert.title = "Ошибка!" alert.title = "Ошибка!"
alert.show() alert.show()
setButton!!.isSelected = false setButton.isSelected = false
magnetSpeedField.text = java.lang.Double.toString(lambdaMagnet!!.speed) magnetSpeedField.text = java.lang.Double.toString(device.speed)
} }
} }
@FXML @FXML
private fun onMonitorToggle(event: ActionEvent) { private fun onMonitorToggle(event: ActionEvent) {
if (monitorButton!!.isSelected) { device
getLambdaMagnet().startMonitorTask() if (monitorButton.isSelected) {
monitoring = true
} else { } else {
getLambdaMagnet().stopMonitorTask() monitoring = false
this.labelU!!.text = "----" this.labelU.text = "----"
} }
} }
override fun error(name: String, errorMessage: String?, throwable: Throwable) { fun displayError(name: String, errorMessage: String?, throwable: Throwable) {
Platform.runLater { Platform.runLater {
this.statusLabel!!.text = "ERROR" this.statusLabel.text = "ERROR"
this.statusLabel.textFill = Color.RED this.statusLabel.textFill = Color.RED
} }
this.logger!!.error("ERROR: {}", errorMessage, throwable) device.logger.error("ERROR: {}", errorMessage, throwable)
// MagnetStateListener.super.error(address, errorMessage, throwable); //To change body of generated methods, choose Tools | Templates. // MagnetStateListener.super.error(address, errorMessage, throwable); //To change body of generated methods, choose Tools | Templates.
} }
/** // /**
* @return the lambdaMagnet // * @param lambdaMagnet the device to set
*/ // */
fun getLambdaMagnet(): LambdaMagnet { // private fun setLambdaMagnet(lambdaMagnet: LambdaMagnet) {
if (lambdaMagnet == null) { // this.device = lambdaMagnet
throw RuntimeException("Magnet controller not defined") // lambdaMagnet.listener = this
} // magnetName.text = lambdaMagnet.name
return lambdaMagnet //
} // magnetSpeedField.text = java.lang.Double.toString(this.device.speed)
//
/** // }
* @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
} else {
this.statusLabel!!.text = "OFF"
this.statusLabel.textFill = Color.BLACK
}
}
}
override fun updateTaskStateChanged(name: String, updateTaskRunning: Boolean) { override fun updateTaskStateChanged(name: String, updateTaskRunning: Boolean) {
this.setButton!!.isSelected = updateTaskRunning this.setButton.isSelected = updateTaskRunning
targetIField!!.isDisable = updateTaskRunning targetIField.isDisable = updateTaskRunning
} }
override fun monitorTaskStateChanged(name: String, monitorTaskRunning: Boolean) { override fun monitorTaskStateChanged(name: String, monitorTaskRunning: Boolean) {
this.monitorButton!!.isScaleShape = monitorTaskRunning this.monitorButton.isScaleShape = monitorTaskRunning
} }
// /** // /**
@ -261,27 +226,30 @@ class MagnetControllerComponent : AnchorPane(), Initializable, MagnetStateListen
// this.logger = logger; // this.logger = logger;
// } // }
override fun displayState(state: String) { override fun displayState(state: String) {
Platform.runLater { this.statusLabel!!.text = state } Platform.runLater { this.statusLabel.text = state }
} }
companion object { companion object {
fun build(lambdaMagnet: LambdaMagnet): MagnetControllerComponent { // fun build(lambdaMagnet: LambdaMagnet): MagnetControllerComponent {
val component = MagnetControllerComponent() // val component = MagnetControllerComponent()
val loader = FXMLLoader(lambdaMagnet.javaClass.getResource("/fxml/SingleMagnet.fxml")) // val loader = FXMLLoader(lambdaMagnet.javaClass.getResource("/fxml/SingleMagnet.fxml"))
//
loader.setRoot(component) // loader.setRoot(component)
loader.setController(component) // loader.setController(component)
//
try { // try {
loader.load<Any>() // loader.load<Any>()
} catch (ex: Exception) { // } catch (ex: Exception) {
LoggerFactory.getLogger("FX").error("Error during fxml initialization", ex) // LoggerFactory.getLogger("FX").error("Error during fxml initialization", ex)
throw Error(ex) // throw Error(ex)
} // }
//
component.setLambdaMagnet(lambdaMagnet) // component.setLambdaMagnet(lambdaMagnet)
return component // return component
// }
} }
} }
} }

View File

@ -7,6 +7,7 @@ package inr.numass.control.magnet.fx
import ch.qos.logback.classic.Level import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger import ch.qos.logback.classic.Logger
import hep.dataforge.context.Global
import hep.dataforge.control.ports.GenericPortController import hep.dataforge.control.ports.GenericPortController
import hep.dataforge.control.ports.Port import hep.dataforge.control.ports.Port
import hep.dataforge.control.ports.PortFactory import hep.dataforge.control.ports.PortFactory
@ -57,7 +58,7 @@ object Talk {
} }
val handler: Port val handler: Port
handler = PortFactory.build(portName) handler = PortFactory.build(portName)
handler.setPhraseCondition { str: String -> str.endsWith("\r") } val controller = GenericPortController(Global,handler,"\r")
// LambdaMagnet controller = new LambdaMagnet(handler, 1); // LambdaMagnet controller = new LambdaMagnet(handler, 1);
val reader = BufferedReader(InputStreamReader(System.`in`)) val reader = BufferedReader(InputStreamReader(System.`in`))
@ -68,7 +69,7 @@ object Talk {
while ("exit" != nextString) { while ("exit" != nextString) {
try { try {
val start = DateTimeUtils.now() 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); //String answer = controller.request(nextString);
System.out.printf("ANSWER (latency = %s): %s;%n", Duration.between(start, DateTimeUtils.now()), answer.trim { it <= ' ' }) System.out.printf("ANSWER (latency = %s): %s;%n", Duration.between(start, DateTimeUtils.now()), answer.trim { it <= ' ' })
} catch (ex: PortException) { } catch (ex: PortException) {

View File

@ -62,21 +62,21 @@ class MspDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
// private var measurementDelegate: Consumer<MspResponse>? = null // private var measurementDelegate: Consumer<MspResponse>? = null
val selected: Boolean by valueState("selected").boolean val selected: Boolean by valueState("selected").booleanDelegate
var controlled: Boolean by valueState("controlled") { _, value -> var controlled: Boolean by valueState("controlled") { _, value ->
control(value.booleanValue()) control(value.booleanValue())
}.boolean }.booleanDelegate
var filament by valueState("filament") { old, value -> var filament by valueState("filament") { old, value ->
selectFilament(value.intValue()) selectFilament(value.intValue())
}.int }.intDelegate
var filamentOn: Boolean by valueState("filamentOn") { _, value -> var filamentOn: Boolean by valueState("filamentOn") { _, value ->
setFilamentOn(value.booleanValue()) 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")) private val averagingDuration: Duration = Duration.parse(meta.getString("averagingDuration", "PT30S"))

View File

@ -32,7 +32,7 @@ import hep.dataforge.plots.data.TimePlot.Companion.setMaxItems
import hep.dataforge.plots.data.TimePlot.Companion.setPrefItems import hep.dataforge.plots.data.TimePlot.Companion.setPrefItems
import hep.dataforge.plots.jfreechart.JFreeChartFrame import hep.dataforge.plots.jfreechart.JFreeChartFrame
import hep.dataforge.values.Value import hep.dataforge.values.Value
import inr.numass.control.DeviceDisplay import inr.numass.control.DeviceDisplayFX
import inr.numass.control.deviceStateIndicator import inr.numass.control.deviceStateIndicator
import inr.numass.control.deviceStateToggle import inr.numass.control.deviceStateToggle
import inr.numass.control.switch import inr.numass.control.switch
@ -53,7 +53,7 @@ import tornadofx.*
* @author darksnake * @author darksnake
*/ */
class MspDisplay() : DeviceDisplay<MspDevice>(), DeviceListener, NamedValueListener { class MspDisplay() : DeviceDisplayFX<MspDevice>(), DeviceListener, NamedValueListener {
private val table = FXCollections.observableHashMap<String, Value>() private val table = FXCollections.observableHashMap<String, Value>()

View File

@ -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<out DeviceDisplayFX<*>>)
/**
* 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<D : Device> : Component(), Connection, DeviceListener {
private val bindings = HashMap<String, ObjectBinding<*>>()
private val deviceProperty = SimpleObjectProperty<D>(this, "device", null)
val device: D by deviceProperty
// private val viewProperty = SimpleObjectProperty<UIComponent>(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 <T : Any> bindState(state: State<T>): ObjectBinding<T> {
val binding = object : ObjectBinding<T>() {
override fun computeValue(): T {
return state.value
}
}
bindings.putIfAbsent(state.name, binding)
return binding
}
fun valueBinding(state: ValueState): ObjectBinding<Value>{
return bindState(state)
}
fun valueBinding(stateName: String): ObjectBinding<Value> {
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<Device>() {
override fun buildView(device: Device): UIComponent? = null
}

View File

@ -2,8 +2,8 @@ package inr.numass.control
import hep.dataforge.fx.plots.PlotContainer import hep.dataforge.fx.plots.PlotContainer
import hep.dataforge.kodex.KMetaBuilder import hep.dataforge.kodex.KMetaBuilder
import hep.dataforge.plots.Plot
import hep.dataforge.plots.PlotFrame import hep.dataforge.plots.PlotFrame
import hep.dataforge.plots.Plottable
import hep.dataforge.plots.jfreechart.JFreeChartFrame import hep.dataforge.plots.jfreechart.JFreeChartFrame
import hep.dataforge.values.Value import hep.dataforge.values.Value
import javafx.beans.value.ObservableValue import javafx.beans.value.ObservableValue
@ -17,7 +17,6 @@ import javafx.scene.shape.Circle
import javafx.scene.shape.StrokeType import javafx.scene.shape.StrokeType
import org.controlsfx.control.ToggleSwitch import org.controlsfx.control.ToggleSwitch
import tornadofx.* 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 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) tooltip(state)
if (transform != null) { if (transform != null) {
bind(connection.getValueBinding(state), transform); bind(connection.getValueBinding(state), transform);
@ -92,7 +91,7 @@ fun Indicator.bind(connection: DeviceDisplay<*>, state: String, transform: ((Val
/** /**
* State name + indicator * 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 (connection.device.stateNames.contains(state)) {
if (showName) { if (showName) {
text("${state.toUpperCase()}: ") text("${state.toUpperCase()}: ")
@ -109,7 +108,7 @@ fun EventTarget.deviceStateIndicator(connection: DeviceDisplay<*>, state: String
/** /**
* A togglebutton + indicator for boolean state * 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)) { if (connection.device.stateNames.contains(state)) {
togglebutton(title) { togglebutton(title) {
isSelected = false isSelected = false
@ -136,11 +135,11 @@ fun EventTarget.switch(text: String = "", op: (ToggleSwitch.() -> Unit) = {}): T
/** /**
* Add frame * Add frame
*/ */
fun BorderPane.plot(plottables: Iterable<Plot> = Collections.emptyList(), metaTransform: (KMetaBuilder.() -> Unit)? = null): PlotFrame { fun BorderPane.plot(plottable: Plottable, metaTransform: (KMetaBuilder.() -> Unit)? = null): PlotFrame {
val meta = KMetaBuilder("plotFrame"); val meta = KMetaBuilder("plotFrame");
metaTransform?.invoke(meta) metaTransform?.invoke(meta)
val plot = JFreeChartFrame(meta) val frame = JFreeChartFrame(meta)
plot.addAll(plottables) frame.add(plottable)
center = PlotContainer(plot).root center = PlotContainer(frame).root
return plot; return frame;
} }

View File

@ -34,25 +34,21 @@ class CM32Device(context: Context, meta: Meta) : PortSensor(context, meta) {
override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) { override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) {
measurement { measurement {
doMeasure()
}
}
private fun doMeasure(): Meta{
val answer = sendAndWait("MES R PM 1\r\n") val answer = sendAndWait("MES R PM 1\r\n")
return if (answer.isEmpty()) { if (answer.isEmpty()) {
updateState(PortSensor.CONNECTED_STATE, false) updateState(PortSensor.CONNECTED_STATE, false)
produceError("No signal") notifyError("No signal")
} else if (!answer.contains("PM1:mbar")) { } else if (!answer.contains("PM1:mbar")) {
updateState(PortSensor.CONNECTED_STATE, false) updateState(PortSensor.CONNECTED_STATE, false)
produceError("Wrong answer: $answer") notifyError("Wrong answer: $answer")
} else if (answer.substring(14, 17) == "OFF") { } else if (answer.substring(14, 17) == "OFF") {
updateState(PortSensor.CONNECTED_STATE, true) updateState(PortSensor.CONNECTED_STATE, true)
produceError("Off") notifyError("Off")
} else { } else {
updateState(PortSensor.CONNECTED_STATE, true) updateState(PortSensor.CONNECTED_STATE, true)
produceResult(answer.substring(14, 17) + answer.substring(19, 23)) notifyResult(answer.substring(14, 17) + answer.substring(19, 23))
}
} }
} }

View File

@ -1,6 +1,8 @@
package inr.numass.control.readvac package inr.numass.control.readvac
import hep.dataforge.control.devices.Sensor 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.DefaultParser
import org.apache.commons.cli.HelpFormatter import org.apache.commons.cli.HelpFormatter
import org.apache.commons.cli.Options import org.apache.commons.cli.Options
@ -12,9 +14,9 @@ import java.time.Instant
* Created by darksnake on 06-Dec-16. * Created by darksnake on 06-Dec-16.
*/ */
object ConsoleVac { object ConsoleVac {
private fun Sensor.read():Double{ private fun Sensor.read(): Double {
this.measure() this.measure()
return runBlocking { resultState.future.await().getDouble(RESULT_VALUE)}
} }
@Throws(Exception::class) @Throws(Exception::class)

View File

@ -13,6 +13,7 @@ import hep.dataforge.control.ports.PortFactory
import hep.dataforge.description.ValueDef import hep.dataforge.description.ValueDef
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.states.StateDef import hep.dataforge.states.StateDef
import hep.dataforge.states.valueState
import hep.dataforge.values.ValueType import hep.dataforge.values.ValueType
import inr.numass.control.DeviceView 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) @StateDef(value = ValueDef(name = "channel", type = [ValueType.NUMBER], def = "2"), writable = true)
class MKSBaratronDevice(context: Context, meta: Meta) : PortSensor(context, meta) { class MKSBaratronDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
var channel by intState("channel") var channel by valueState("channel").intDelegate
override fun getType(): String { override fun getType(): String {
return meta.getString("type", "numass.vac.baratron") 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) { override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) {
measurement { measurement {
doMeasure()
}
}
private fun doMeasure(): Meta {
val answer = sendAndWait("AV$channel\r") val answer = sendAndWait("AV$channel\r")
if (answer.isEmpty()) { if (answer.isEmpty()) {
// invalidateState("connection"); // invalidateState("connection");
updateState(PortSensor.CONNECTED_STATE, false) updateState(PortSensor.CONNECTED_STATE, false)
return produceError("No connection") notifyError("No connection")
} else { } else {
updateState(PortSensor.CONNECTED_STATE, true) updateState(PortSensor.CONNECTED_STATE, true)
} }
val res = java.lang.Double.parseDouble(answer) val res = java.lang.Double.parseDouble(answer)
return if (res <= 0) { if (res <= 0) {
produceError("Non positive") notifyError("Non positive")
} else { } else {
produceResult(res) notifyResult(res)
}
} }
} }
} }

View File

@ -43,7 +43,7 @@ class MKSVacDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
if (old != value) { if (old != value) {
setPowerOn(value.booleanValue()) setPowerOn(value.booleanValue())
} }
}.boolean }.booleanDelegate
@Throws(ControlException::class) @Throws(ControlException::class)

View File

@ -13,6 +13,7 @@ import hep.dataforge.control.ports.PortFactory
import hep.dataforge.description.ValueDef import hep.dataforge.description.ValueDef
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.states.StateDef import hep.dataforge.states.StateDef
import hep.dataforge.states.valueState
import hep.dataforge.values.ValueType.NUMBER import hep.dataforge.values.ValueType.NUMBER
import java.math.BigDecimal import java.math.BigDecimal
import java.math.BigInteger 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) @StateDef(value = ValueDef(name = "address", type = [NUMBER], def = "1", info = "A modbus address"), writable = true)
class MeradatVacDevice(context: Context, meta: Meta) : PortSensor(context, meta) { 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 { override fun connect(meta: Meta): GenericPortController {
val port: Port = PortFactory.build(meta) val port: Port = PortFactory.build(meta)
@ -40,12 +41,6 @@ class MeradatVacDevice(context: Context, meta: Meta) : PortSensor(context, meta)
override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) { override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) {
measurement{ measurement{
doMeasure()
}
}
private fun doMeasure(): Meta {
val requestBase: String = String.format(":%02d", address) val requestBase: String = String.format(":%02d", address)
val dataStr = requestBase.substring(1) + REQUEST val dataStr = requestBase.substring(1) + REQUEST
val query = requestBase + REQUEST + calculateLRC(dataStr) + "\r\n" // ":010300000002FA\r\n"; val query = requestBase + REQUEST + calculateLRC(dataStr) + "\r\n" // ":010300000002FA\r\n";
@ -55,11 +50,11 @@ class MeradatVacDevice(context: Context, meta: Meta) : PortSensor(context, meta)
if (answer.isEmpty()) { if (answer.isEmpty()) {
updateState(PortSensor.CONNECTED_STATE, false) updateState(PortSensor.CONNECTED_STATE, false)
return produceError("No signal") notifyError("No signal")
} else { } else {
val match = response.matcher(answer) val match = response.matcher(answer)
return if (match.matches()) { if (match.matches()) {
val base = Integer.parseInt(match.group(1), 16).toDouble() / 10.0 val base = Integer.parseInt(match.group(1), 16).toDouble() / 10.0
var exp = Integer.parseInt(match.group(2), 16) var exp = Integer.parseInt(match.group(2), 16)
if (exp > 32766) { if (exp > 32766) {
@ -68,14 +63,14 @@ class MeradatVacDevice(context: Context, meta: Meta) : PortSensor(context, meta)
var res = BigDecimal.valueOf(base * Math.pow(10.0, exp.toDouble())) var res = BigDecimal.valueOf(base * Math.pow(10.0, exp.toDouble()))
res = res.setScale(4, RoundingMode.CEILING) res = res.setScale(4, RoundingMode.CEILING)
updateState(PortSensor.CONNECTED_STATE, true) updateState(PortSensor.CONNECTED_STATE, true)
produceResult(res) notifyResult(res)
} else { } else {
updateState(PortSensor.CONNECTED_STATE, false) updateState(PortSensor.CONNECTED_STATE, false)
produceError("Wrong answer: $answer") notifyError("Wrong answer: $answer")
}
} }
} }
} }
companion object { companion object {
private const val REQUEST = "0300000002" private const val REQUEST = "0300000002"

View File

@ -49,20 +49,16 @@ class VacCollectorDevice(context: Context, meta: Meta, val sensors: Collection<S
private val helper = StorageHelper(this, this::buildLoader) private val helper = StorageHelper(this, this::buildLoader)
private val collector = object : DeviceListener { private val collector = object : DeviceListener {
override fun notifyStateChanged(device: Device, name: String, state: Any) {
if (name == MEASUREMENT_RESULT_STATE) {
collector.put(device.name, (state as Meta).getValue("value"))
}
}
val averagingDuration: Duration = Duration.parse(meta.getString("averagingDuration", "PT30S")) val averagingDuration: Duration = Duration.parse(meta.getString("averagingDuration", "PT30S"))
private val collector = RegularPointCollector(averagingDuration) { private val collector = RegularPointCollector(averagingDuration) {
notifyResult(it) notifyResult(it)
} }
override fun notifyStateChanged(device: Device, name: String, state: Value) {
}
override fun notifyMetaStateChanged(device: Device, name: String, state: Meta) {
if (name == MEASUREMENT_RESULT_STATE) {
collector.put(device.name, state.getValue("value"))
}
}
} }
@ -116,9 +112,11 @@ class VacCollectorDevice(context: Context, meta: Meta, val sensors: Collection<S
helper.push(values) helper.push(values)
} }
override fun onStateChange(stateName: String, oldState: Value?, newState: Value) {
override fun onStateChange(stateName: String, value: Any) {
if (stateName == MEASURING_STATE) { if (stateName == MEASURING_STATE) {
if (!newState.booleanValue()) { if (!(value as Value).booleanValue()) {
notifyResult(terminator()) notifyResult(terminator())
} }
} }
@ -135,7 +133,7 @@ class VacCollectorDevice(context: Context, meta: Meta, val sensors: Collection<S
while (true) { while (true) {
notifyMeasurementState(MeasurementState.IN_PROGRESS) notifyMeasurementState(MeasurementState.IN_PROGRESS)
sensors.forEach { sensor -> sensors.forEach { sensor ->
if (sensor.optBooleanState(CONNECTED_STATE).orElse(false)) { if (sensor.states.getBoolean(CONNECTED_STATE,false)) {
sensor.measure() sensor.measure()
} }
} }

View File

@ -12,9 +12,10 @@ import hep.dataforge.control.measurements.Measurement
import hep.dataforge.control.measurements.MeasurementListener import hep.dataforge.control.measurements.MeasurementListener
import hep.dataforge.fx.bindWindow import hep.dataforge.fx.bindWindow
import hep.dataforge.fx.fragments.LogFragment import hep.dataforge.fx.fragments.LogFragment
import hep.dataforge.plots.PlotGroup
import hep.dataforge.plots.data.TimePlot import hep.dataforge.plots.data.TimePlot
import hep.dataforge.values.Value import hep.dataforge.values.Value
import inr.numass.control.DeviceDisplay import inr.numass.control.DeviceDisplayFX
import inr.numass.control.deviceStateToggle import inr.numass.control.deviceStateToggle
import inr.numass.control.plot import inr.numass.control.plot
import javafx.collections.FXCollections import javafx.collections.FXCollections
@ -30,7 +31,7 @@ import java.time.Instant
* @author [Alexander Nozik](mailto:altavir@gmail.com) * @author [Alexander Nozik](mailto:altavir@gmail.com)
*/ */
class VacCollectorDisplay : DeviceDisplay<VacCollectorDevice>() { class VacCollectorDisplay : DeviceDisplayFX<VacCollectorDevice>() {
private val table = FXCollections.observableHashMap<String, Double>() private val table = FXCollections.observableHashMap<String, Double>()
@ -48,7 +49,7 @@ class VacCollectorDisplay : DeviceDisplay<VacCollectorDevice>() {
private val viewList = FXCollections.observableArrayList<VacDisplay>(); private val viewList = FXCollections.observableArrayList<VacDisplay>();
override fun buildView(device: VacCollectorDevice): View { override fun buildView(device: VacCollectorDevice): UIComponent {
return VacCollectorView(); return VacCollectorView();
} }
@ -62,15 +63,15 @@ class VacCollectorDisplay : DeviceDisplay<VacCollectorDevice>() {
} }
} }
inner class VacCollectorView : View("Numass vacuum view") { inner class VacCollectorView : Fragment("Numass vacuum view") {
private val plottables = TimePlottableGroup().apply { private val plottables = PlotGroup.typed<TimePlot>("vac").apply {
viewList.forEach { viewList.forEach {
val plot = TimePlot(it.getTitle(), it.device.name) val plot = TimePlot(it.getTitle(), it.device.name)
plot.configure(it.device.meta) plot.configure(it.device.meta)
add(plot) add(plot)
} }
setValue("thickness", 3) configureValue("thickness", 3)
} }
// private val logWindow = FragmentWindow(LogFragment().apply { // private val logWindow = FragmentWindow(LogFragment().apply {
@ -127,13 +128,13 @@ class VacCollectorDisplay : DeviceDisplay<VacCollectorDevice>() {
init { init {
table.addListener { change: MapChangeListener.Change<out String, out Double> -> table.addListener { change: MapChangeListener.Change<out String, out Double> ->
if (change.wasAdded()) { if (change.wasAdded()) {
val pl = plottables.get(change.key) val pl = plottables[change.key]
val value = change.valueAdded val value = change.valueAdded
if (pl != null) { (pl as? TimePlot)?.let {
if (value > 0) { if (value > 0) {
pl.put(Value.of(value)) it.put(Value.of(value))
} else { } else {
pl.put(Value.NULL) it.put(Value.NULL)
} }
} }
} }

View File

@ -34,7 +34,7 @@ class VacDeviceFactory : DeviceFactory {
return VacCollectorDevice(context, config, sensors) return VacCollectorDevice(context, config, sensors)
} }
// override fun buildView(device: Device): DeviceDisplay<VacCollectorDevice> { // override fun buildView(device: Device): DeviceDisplayFX<VacCollectorDevice> {
// return VacCollectorDisplay(); // return VacCollectorDisplay();
// } // }
} }

View File

@ -7,7 +7,7 @@ package inr.numass.control.readvac
import hep.dataforge.control.devices.PortSensor.Companion.CONNECTED_STATE import hep.dataforge.control.devices.PortSensor.Companion.CONNECTED_STATE
import hep.dataforge.control.devices.Sensor import hep.dataforge.control.devices.Sensor
import inr.numass.control.DeviceDisplay import inr.numass.control.DeviceDisplayFX
import inr.numass.control.switch import inr.numass.control.switch
import javafx.application.Platform import javafx.application.Platform
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
@ -28,7 +28,7 @@ import java.time.format.DateTimeFormatter
/** /**
* @author [Alexander Nozik](mailto:altavir@gmail.com) * @author [Alexander Nozik](mailto:altavir@gmail.com)
*/ */
open class VacDisplay : DeviceDisplay<Sensor>() { open class VacDisplay : DeviceDisplayFX<Sensor>() {
val statusProperty = SimpleStringProperty("") val statusProperty = SimpleStringProperty("")
var status: String by statusProperty var status: String by statusProperty