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,250 +35,221 @@ 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
*/
var logger: Logger? = null
private set
private var showConfirmation = true val voltage by lazy { valueBinding(device.current)}
@FXML var target by device.target.doubleDelegate
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
private val targetI: Double var output by device.output.booleanDelegate
get() = java.lang.Double.parseDouble(targetIField!!.text)
// public MagnetControllerComponent(LambdaMagnet lambdaMagnet) { var monitoring by device.monitoring.booleanDelegate
// 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) {
targetIField!!.textProperty().addListener { observable: ObservableValue<out String>, oldValue: String, newValue: String -> var updating by device.updating.booleanDelegate
if (!newValue.matches("\\d*(\\.)?\\d*".toRegex())) {
targetIField.text = oldValue
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<out String>, oldValue: String, newValue: String ->
if (!newValue.matches("\\d*(\\.)?\\d*".toRegex())) {
targetIField.text = oldValue
}
}
magnetSpeedField.textProperty().addListener { observable: ObservableValue<out String>, 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<out String>, oldValue: String, newValue: String -> fun setShowConfirmation(showConfirmation: Boolean) {
if (!newValue.matches("\\d*(\\.)?\\d*".toRegex())) { this.showConfirmation = showConfirmation
magnetSpeedField.text = oldValue }
@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) if (alert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.YES) {
private fun setOutput(outputOn: Boolean) { startCurrentChange()
if (outputOn) { } else {
if (showConfirmation) { setButton.isSelected = false
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()
} else { } else {
setButton!!.isSelected = false startCurrentChange()
} }
} else { } 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)
} }
} @Throws(PortException::class)
private fun startCurrentChange() {
@FXML val speed = java.lang.Double.parseDouble(magnetSpeedField.text)
private fun onMonitorToggle(event: ActionEvent) { if (speed > 0 && speed <= 7) {
if (monitorButton!!.isSelected) { device.speed = speed
getLambdaMagnet().startMonitorTask() magnetSpeedField.isDisable = true
} else { target = targetIField.text.toDouble()
getLambdaMagnet().stopMonitorTask() output = true
this.labelU!!.text = "----" updating = true
}
}
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
} else { } else {
this.statusLabel!!.text = "OFF" val alert = Alert(Alert.AlertType.ERROR)
this.statusLabel.textFill = Color.BLACK 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<Any>()
// } 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<Any>()
} catch (ex: Exception) {
LoggerFactory.getLogger("FX").error("Error during fxml initialization", ex)
throw Error(ex)
}
component.setLambdaMagnet(lambdaMagnet)
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() val answer = sendAndWait("MES R PM 1\r\n")
}
}
private fun doMeasure(): Meta{ if (answer.isEmpty()) {
val answer = sendAndWait("MES R PM 1\r\n") updateState(PortSensor.CONNECTED_STATE, false)
notifyError("No signal")
return if (answer.isEmpty()) { } else if (!answer.contains("PM1:mbar")) {
updateState(PortSensor.CONNECTED_STATE, false) updateState(PortSensor.CONNECTED_STATE, false)
produceError("No signal") notifyError("Wrong answer: $answer")
} else if (!answer.contains("PM1:mbar")) { } else if (answer.substring(14, 17) == "OFF") {
updateState(PortSensor.CONNECTED_STATE, false) updateState(PortSensor.CONNECTED_STATE, true)
produceError("Wrong answer: $answer") notifyError("Off")
} else if (answer.substring(14, 17) == "OFF") { } else {
updateState(PortSensor.CONNECTED_STATE, true) updateState(PortSensor.CONNECTED_STATE, true)
produceError("Off") notifyResult(answer.substring(14, 17) + answer.substring(19, 23))
} else { }
updateState(PortSensor.CONNECTED_STATE, true)
produceResult(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() val answer = sendAndWait("AV$channel\r")
} if (answer.isEmpty()) {
} // invalidateState("connection");
updateState(PortSensor.CONNECTED_STATE, false)
private fun doMeasure(): Meta { notifyError("No connection")
val answer = sendAndWait("AV$channel\r") } else {
if (answer.isEmpty()) { updateState(PortSensor.CONNECTED_STATE, true)
// invalidateState("connection"); }
updateState(PortSensor.CONNECTED_STATE, false) val res = java.lang.Double.parseDouble(answer)
return produceError("No connection") if (res <= 0) {
} else { notifyError("Non positive")
updateState(PortSensor.CONNECTED_STATE, true) } else {
} notifyResult(res)
val res = java.lang.Double.parseDouble(answer) }
return if (res <= 0) {
produceError("Non positive")
} else {
produceResult(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,43 +41,37 @@ 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() 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 { if (answer.isEmpty()) {
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 {
updateState(PortSensor.CONNECTED_STATE, false) 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 { 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