Updating control
This commit is contained in:
parent
794929890a
commit
515eb2faac
@ -35,16 +35,30 @@ class LambdaHub(context: Context, meta: Meta) : DeviceHub, AbstractDevice(contex
|
|||||||
magnets.add(LambdaMagnet(controller, it))
|
magnets.add(LambdaMagnet(controller, it))
|
||||||
}
|
}
|
||||||
|
|
||||||
meta.useEachMeta("bind") {
|
meta.useEachMeta("bind") { bindMeta ->
|
||||||
TODO("add binding")
|
val first = magnets.find { it.name == bindMeta.getString("first") }
|
||||||
|
val second = magnets.find { it.name == bindMeta.getString("second") }
|
||||||
|
val delta = bindMeta.getDouble("delta")
|
||||||
|
bind(first!!, second!!, delta)
|
||||||
|
logger.info("Bound magnet $first to magnet $second with delta $delta")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add symmetric non-blocking conditions to ensure currents in two magnets have difference within given value.
|
||||||
|
* @param controller
|
||||||
|
* @param difference
|
||||||
|
*/
|
||||||
|
fun bind(first: LambdaMagnet, second: LambdaMagnet, difference: Double) {
|
||||||
|
first.bound = { i -> Math.abs(second.current.doubleValue - i) <= difference }
|
||||||
|
second.bound = { i -> Math.abs(first.current.doubleValue - i) <= difference }
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildPort(): Port {
|
private fun buildPort(): Port {
|
||||||
val portMeta = meta.getMetaOrEmpty("port")
|
val portMeta = meta.getMetaOrEmpty("port")
|
||||||
return if(portMeta.getString("type") == "debug"){
|
return if (portMeta.getString("type") == "debug") {
|
||||||
VirtualLambdaPort(portMeta)
|
VirtualLambdaPort(portMeta)
|
||||||
} else{
|
} else {
|
||||||
PortFactory.build(portMeta)
|
PortFactory.build(portMeta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,9 +82,9 @@ class LambdaHub(context: Context, meta: Meta) : DeviceHub, AbstractDevice(contex
|
|||||||
get() = magnets.stream().map { Name.ofSingle(it.name) }
|
get() = magnets.stream().map { Name.ofSingle(it.name) }
|
||||||
}
|
}
|
||||||
|
|
||||||
class LambdaHubDisplay: DeviceDisplayFX<LambdaHub>() {
|
class LambdaHubDisplay : DeviceDisplayFX<LambdaHub>() {
|
||||||
override fun buildView(device: LambdaHub): UIComponent? {
|
override fun buildView(device: LambdaHub): UIComponent? {
|
||||||
return object: View() {
|
return object : View() {
|
||||||
override val root: Parent = vbox {
|
override val root: Parent = vbox {
|
||||||
device.magnets.forEach {
|
device.magnets.forEach {
|
||||||
this.add(it.getDisplay().view!!)
|
this.add(it.getDisplay().view!!)
|
||||||
|
@ -32,26 +32,25 @@ import hep.dataforge.values.ValueType.*
|
|||||||
import inr.numass.control.DeviceView
|
import inr.numass.control.DeviceView
|
||||||
import inr.numass.control.magnet.fx.MagnetDisplay
|
import inr.numass.control.magnet.fx.MagnetDisplay
|
||||||
import kotlinx.coroutines.experimental.runBlocking
|
import kotlinx.coroutines.experimental.runBlocking
|
||||||
|
import java.time.Duration
|
||||||
import java.time.Instant
|
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.TimeUnit
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Polina
|
* @author Polina
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@StateDefs(
|
@StateDefs(
|
||||||
StateDef(value = ValueDef(name = "current", type = arrayOf(NUMBER), def = "0", info = "Current current")),
|
StateDef(value = ValueDef(name = "current", type = arrayOf(NUMBER), def = "-1", 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 = "-1", info = "Current voltage")),
|
||||||
StateDef(value = ValueDef(name = "outCurrent", 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 = "outVoltage", 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),
|
||||||
StateDef(value = ValueDef(name = "monitoring", type = arrayOf(BOOLEAN), def = "false", info = "Shows if monitoring task is running"), writable = true),
|
StateDef(value = ValueDef(name = "monitoring", type = arrayOf(BOOLEAN), def = "false", info = "Shows if monitoring task is running"), writable = true),
|
||||||
StateDef(value = ValueDef(name = "speed", type = arrayOf(NUMBER), info = "Current change speed in Ampere per minute"), writable = true),
|
StateDef(value = ValueDef(name = "speed", type = arrayOf(NUMBER), def = "5", info = "Current change speed in Ampere per minute"), writable = true),
|
||||||
StateDef(ValueDef(name = "status", type = [STRING], def = "INIT", enumeration = LambdaMagnet.MagnetStatus::class, info = "Current state of magnet operation"))
|
StateDef(ValueDef(name = "status", type = [STRING], def = "INIT", enumeration = LambdaMagnet.MagnetStatus::class, info = "Current state of magnet operation"))
|
||||||
)
|
)
|
||||||
@DeviceView(MagnetDisplay::class)
|
@DeviceView(MagnetDisplay::class)
|
||||||
@ -65,7 +64,7 @@ class LambdaMagnet(private val controller: LambdaPortController, meta: Meta) : A
|
|||||||
val address: Int = meta.getInt("address", 1)
|
val address: Int = meta.getInt("address", 1)
|
||||||
|
|
||||||
override val name: String = meta.getString("name", "LAMBDA_$address")
|
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;
|
||||||
@ -81,24 +80,20 @@ class LambdaMagnet(private val controller: LambdaPortController, meta: Meta) : A
|
|||||||
|
|
||||||
val voltage = valueState("voltage", getter = { s2d(controller.getParameter(address, "MV")) })
|
val voltage = valueState("voltage", getter = { s2d(controller.getParameter(address, "MV")) })
|
||||||
|
|
||||||
var target = valueState("target")
|
val target = valueState("target")
|
||||||
|
|
||||||
//output values of current and voltage
|
//output values of current and voltage
|
||||||
private var outCurrent by valueState("outCurrent", getter = { s2d(controller.getParameter(address, "PC")) }) { _, value ->
|
private var outCurrent by valueState("outCurrent", getter = { s2d(controller.getParameter(address, "PC")) }) { _, value ->
|
||||||
if (controller.setParameter(address, "PC", value.doubleValue())) {
|
setCurrent(value.doubleValue())
|
||||||
lastUpdate = DateTimeUtils.now()
|
|
||||||
} else {
|
|
||||||
notifyError("Can't set the target current")
|
|
||||||
}
|
|
||||||
return@valueState value
|
return@valueState value
|
||||||
}.doubleDelegate
|
}.doubleDelegate
|
||||||
|
|
||||||
private val outVoltage = valueState("outVoltage", getter = { s2d(controller.getParameter(address, "PV")) }) { _, value ->
|
private var outVoltage = valueState("outVoltage", getter = { s2d(controller.getParameter(address, "PV")) }) { _, value ->
|
||||||
if (!controller.setParameter(address, "PV", value.doubleValue())) {
|
if (!controller.setParameter(address, "PV", value.doubleValue())) {
|
||||||
notifyError("Can't set the target voltage")
|
notifyError("Can't set the target voltage")
|
||||||
}
|
}
|
||||||
return@valueState value
|
return@valueState value
|
||||||
}
|
}.doubleDelegate
|
||||||
|
|
||||||
val output = valueState("output", getter = { controller.talk(address, "OUT?") == "OK" }) { _, value ->
|
val output = valueState("output", getter = { controller.talk(address, "OUT?") == "OK" }) { _, value ->
|
||||||
setOutputMode(value.booleanValue())
|
setOutputMode(value.booleanValue())
|
||||||
@ -107,7 +102,7 @@ class LambdaMagnet(private val controller: LambdaPortController, meta: Meta) : A
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var monitoring = valueState("monitoring", getter = { monitorTask != null }) { _, value ->
|
val monitoring = valueState("monitoring", getter = { monitorTask != null }) { _, value ->
|
||||||
if (value.booleanValue()) {
|
if (value.booleanValue()) {
|
||||||
startMonitorTask()
|
startMonitorTask()
|
||||||
} else {
|
} else {
|
||||||
@ -119,7 +114,7 @@ class LambdaMagnet(private val controller: LambdaPortController, meta: Meta) : A
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
var updating = valueState("updating", getter = { updateTask != null }) { _, value ->
|
val updating = valueState("updating", getter = { updateTask != null }) { _, value ->
|
||||||
if (value.booleanValue()) {
|
if (value.booleanValue()) {
|
||||||
startUpdateTask()
|
startUpdateTask()
|
||||||
} else {
|
} else {
|
||||||
@ -143,7 +138,19 @@ class LambdaMagnet(private val controller: LambdaPortController, meta: Meta) : A
|
|||||||
/**
|
/**
|
||||||
* The binding limit for magnet current
|
* The binding limit for magnet current
|
||||||
*/
|
*/
|
||||||
var bound: (Double) -> Boolean = { it < meta.getDouble("maxCurrent") }
|
var bound: (Double) -> Boolean = {
|
||||||
|
it < meta.getDouble("maxCurrent", 170.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setCurrent(current: Double) {
|
||||||
|
return if (controller.setParameter(address, "PC", current)) {
|
||||||
|
lastUpdate = DateTimeUtils.now()
|
||||||
|
//this.current.update(current)
|
||||||
|
} else {
|
||||||
|
notifyError("Can't set the target current")
|
||||||
|
status = MagnetStatus.ERROR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A setup for single magnet controller
|
* A setup for single magnet controller
|
||||||
@ -188,62 +195,9 @@ class LambdaMagnet(private val controller: LambdaPortController, meta: Meta) : A
|
|||||||
private fun s2d(str: String): Double = java.lang.Double.valueOf(str)
|
private fun s2d(str: String): Double = java.lang.Double.valueOf(str)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel current update task
|
* Calculate next current step
|
||||||
*/
|
*/
|
||||||
fun stopUpdateTask() {
|
|
||||||
updateTask?.cancel(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start recursive updates of current with given delays between updates. If
|
|
||||||
* delay is 0 then updates are made immediately.
|
|
||||||
*
|
|
||||||
* @param targetI
|
|
||||||
* @param delay
|
|
||||||
*/
|
|
||||||
|
|
||||||
private fun startUpdateTask(delay: Int = DEFAULT_DELAY) {
|
|
||||||
assert(delay > 0)
|
|
||||||
stopUpdateTask()
|
|
||||||
val call = {
|
|
||||||
try {
|
|
||||||
val measuredI = current.doubleValue
|
|
||||||
val targetI = target.doubleValue
|
|
||||||
updateState("current", measuredI)
|
|
||||||
if (Math.abs(measuredI - targetI) > CURRENT_PRECISION) {
|
|
||||||
val nextI = nextI(measuredI, targetI)
|
|
||||||
if (bound(nextI)) {
|
|
||||||
outCurrent = nextI
|
|
||||||
status = MagnetStatus.OK
|
|
||||||
} else {
|
|
||||||
status = MagnetStatus.BOUND
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stopUpdateTask()
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (ex: PortException) {
|
|
||||||
notifyError("Error in update task", ex)
|
|
||||||
stopUpdateTask()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTask = scheduler.scheduleWithFixedDelay(call, 0, delay.toLong(), TimeUnit.MILLISECONDS)
|
|
||||||
updateState("updating", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(PortException::class)
|
|
||||||
private fun setOutputMode(out: Boolean) {
|
|
||||||
val outState: Int = if (out) 1 else 0
|
|
||||||
if (!controller.setParameter(address, "OUT", outState)) {
|
|
||||||
notifyError("Can't set output mode")
|
|
||||||
} else {
|
|
||||||
updateState("output", out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun nextI(measuredI: Double, targetI: Double): Double {
|
private fun nextI(measuredI: Double, targetI: Double): Double {
|
||||||
|
|
||||||
var step = if (lastUpdate == Instant.EPOCH) {
|
var step = if (lastUpdate == Instant.EPOCH) {
|
||||||
MIN_UP_STEP_SIZE
|
MIN_UP_STEP_SIZE
|
||||||
} else {
|
} else {
|
||||||
@ -269,6 +223,58 @@ class LambdaMagnet(private val controller: LambdaPortController, meta: Meta) : A
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start recursive updates of current with given delays between updates. If
|
||||||
|
* delay is 0 then updates are made immediately.
|
||||||
|
*
|
||||||
|
* @param targetI
|
||||||
|
* @param delay
|
||||||
|
*/
|
||||||
|
private fun startUpdateTask(delay: Long = DEFAULT_DELAY.toLong()) {
|
||||||
|
assert(delay > 0)
|
||||||
|
stopUpdateTask()
|
||||||
|
updateTask = repeat(Duration.ofMillis(delay)) {
|
||||||
|
try {
|
||||||
|
val measuredI = current.readBlocking().doubleValue()
|
||||||
|
val targetI = target.doubleValue
|
||||||
|
if (Math.abs(measuredI - targetI) > CURRENT_PRECISION) {
|
||||||
|
val nextI = nextI(measuredI, targetI)
|
||||||
|
status = if (bound(nextI)) {
|
||||||
|
setCurrent(nextI)
|
||||||
|
MagnetStatus.OK
|
||||||
|
} else {
|
||||||
|
MagnetStatus.BOUND
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setCurrent(targetI)
|
||||||
|
updating.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (ex: PortException) {
|
||||||
|
notifyError("Error in update task", ex)
|
||||||
|
updating.set(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateState("updating", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel current update task
|
||||||
|
*/
|
||||||
|
private fun stopUpdateTask() {
|
||||||
|
updateTask?.cancel(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(PortException::class)
|
||||||
|
private fun setOutputMode(out: Boolean) {
|
||||||
|
val outState: Int = if (out) 1 else 0
|
||||||
|
if (!controller.setParameter(address, "OUT", outState)) {
|
||||||
|
notifyError("Can't set output mode")
|
||||||
|
} else {
|
||||||
|
updateState("output", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel current monitoring task
|
* Cancel current monitoring task
|
||||||
*/
|
*/
|
||||||
@ -285,35 +291,21 @@ class LambdaMagnet(private val controller: LambdaPortController, meta: Meta) : A
|
|||||||
*
|
*
|
||||||
* @param delay an interval between scans in milliseconds
|
* @param delay an interval between scans in milliseconds
|
||||||
*/
|
*/
|
||||||
private fun startMonitorTask(delay: Int = DEFAULT_MONITOR_DELAY) {
|
private fun startMonitorTask(delay: Long = DEFAULT_MONITOR_DELAY.toLong()) {
|
||||||
assert(delay >= 1000)
|
assert(delay >= 1000)
|
||||||
stopMonitorTask()
|
stopMonitorTask()
|
||||||
|
|
||||||
|
monitorTask = repeat(Duration.ofMillis(delay)) {
|
||||||
val call = Runnable {
|
|
||||||
try {
|
try {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
states["voltage"]?.read()
|
voltage.read()
|
||||||
states["current"]?.read()
|
current.read()
|
||||||
}
|
}
|
||||||
} catch (ex: PortException) {
|
} catch (ex: PortException) {
|
||||||
notifyError("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)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add symmetric non-blocking conditions to ensure currents in two magnets have difference within given value.
|
|
||||||
* @param controller
|
|
||||||
* @param difference
|
|
||||||
*/
|
|
||||||
fun bindTo(controller: LambdaMagnet, difference: Double) {
|
|
||||||
this.bound = { i -> Math.abs(controller.current.doubleValue - i) <= difference }
|
|
||||||
controller.bound = { i -> Math.abs(this.current.doubleValue - i) <= difference }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class MagnetStatus {
|
enum class MagnetStatus {
|
||||||
@ -330,7 +322,7 @@ class LambdaMagnet(private val controller: LambdaPortController, meta: Meta) : A
|
|||||||
const val DEFAULT_DELAY = 1
|
const val DEFAULT_DELAY = 1
|
||||||
const val DEFAULT_MONITOR_DELAY = 2000
|
const val DEFAULT_MONITOR_DELAY = 2000
|
||||||
const val MAX_STEP_SIZE = 0.2
|
const val MAX_STEP_SIZE = 0.2
|
||||||
const val MIN_UP_STEP_SIZE = 0.005
|
const val MIN_UP_STEP_SIZE = 0.01
|
||||||
const val MIN_DOWN_STEP_SIZE = 0.05
|
const val MIN_DOWN_STEP_SIZE = 0.05
|
||||||
const val MAX_SPEED = 5.0 // 5 A per minute
|
const val MAX_SPEED = 5.0 // 5 A per minute
|
||||||
|
|
||||||
|
@ -9,27 +9,29 @@ import java.text.DecimalFormat
|
|||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
|
||||||
//@ValueDef(name = "timeout", type = [(ValueType.NUMBER)], def = "400", info = "A timeout for port response")
|
//@ValueDef(name = "timeout", type = [(ValueType.NUMBER)], def = "400", info = "A timeout for port response")
|
||||||
class LambdaPortController(context: Context, port: Port) : GenericPortController(context, port) {
|
class LambdaPortController(context: Context, port: Port) : GenericPortController(context, port, "\r") {
|
||||||
private var currentAddress: Int = -1;
|
private var currentAddress: Int = -1;
|
||||||
|
|
||||||
private val timeout: Duration = port.meta.optString("timeout").map<Duration> { Duration.parse(it) }.orElse(Duration.ofMillis(200))
|
private val timeout: Duration = port.meta.optString("timeout").map<Duration> { Duration.parse(it) }.orElse(Duration.ofMillis(200))
|
||||||
|
|
||||||
private fun setAddress(address: Int, timeout: Duration) {
|
fun setAddress(address: Int) {
|
||||||
val response = sendAndWait("ADR $address\r", timeout) { true }.trim()
|
if(currentAddress!= address) {
|
||||||
if (response == "OK") {
|
val response = sendAndWait("ADR $address\r", timeout) { true }.trim()
|
||||||
currentAddress = address
|
if (response == "OK") {
|
||||||
} else {
|
currentAddress = address
|
||||||
throw RuntimeException("Failed to set address to LAMBDA device on $port")
|
} else {
|
||||||
|
throw RuntimeException("Failed to set address to LAMBDA device on $port")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* perform series of synchronous actions ensuring that all of them have the same address
|
* perform series of synchronous actions ensuring that all of them have the same address
|
||||||
*/
|
*/
|
||||||
private fun <R> talk(address: Int, action: GenericPortController.() -> R): R {
|
fun <R> talk(address: Int, action: GenericPortController.() -> R): R {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
setAddress(address, timeout)
|
setAddress(address)
|
||||||
return this.action()
|
return action.invoke(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +53,14 @@ class LambdaPortController(context: Context, port: Port) : GenericPortController
|
|||||||
|
|
||||||
fun getParameter(address: Int, name: String): String = talk(address, "$name?")
|
fun getParameter(address: Int, name: String): String = talk(address, "$name?")
|
||||||
|
|
||||||
fun setParameter(address: Int, key: String, state: String): Boolean = "OK" == talk(address, "$key $state")
|
fun setParameter(address: Int, key: String, state: String): Boolean {
|
||||||
|
try {
|
||||||
|
return "OK" == talk(address, "$key $state")
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
logger.error("Failed to send message", ex)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun setParameter(address: Int, key: String, state: Int): Boolean = setParameter(address, key, state.toString())
|
fun setParameter(address: Int, key: String, state: Int): Boolean = setParameter(address, key, state.toString())
|
||||||
|
|
||||||
|
@ -28,15 +28,19 @@ import java.util.*
|
|||||||
*/
|
*/
|
||||||
class VirtualLambdaPort(meta: Meta) : VirtualPort(meta) {
|
class VirtualLambdaPort(meta: Meta) : VirtualPort(meta) {
|
||||||
|
|
||||||
@Volatile private var currentAddress = -1
|
var currentAddress = -1
|
||||||
private val magnets = HashMap<Int, VirtualMagnetStatus>()
|
private set
|
||||||
|
|
||||||
|
val statusMap = HashMap<Int, VirtualMagnetStatus>()
|
||||||
override val name: String = meta.getString("name", "virtual::numass.lambda")
|
override val name: String = meta.getString("name", "virtual::numass.lambda")
|
||||||
|
|
||||||
|
override val delimeter: String = "\r"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
meta.useEachMeta("magnet") {
|
meta.useEachMeta("magnet") {
|
||||||
val num = it.getInt("address", 1)
|
val num = it.getInt("address", 1)
|
||||||
val resistance = it.getDouble("resistance", 1.0)
|
val resistance = it.getDouble("resistance", 1.0)
|
||||||
magnets[num] = VirtualMagnetStatus(resistance)
|
statusMap[num] = VirtualMagnetStatus(resistance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,21 +73,18 @@ class VirtualLambdaPort(meta: Meta) : VirtualPort(meta) {
|
|||||||
when (comand) {
|
when (comand) {
|
||||||
"ADR" -> {
|
"ADR" -> {
|
||||||
val address = Integer.parseInt(value)
|
val address = Integer.parseInt(value)
|
||||||
if (magnets.containsKey(address)) {
|
if (statusMap.containsKey(address)) {
|
||||||
currentAddress = address
|
currentAddress = address
|
||||||
sendOK()
|
sendOK()
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
"ADR?" -> {
|
"ADR?" -> {
|
||||||
planResponse(Integer.toString(currentAddress), latency)
|
planResponse(Integer.toString(currentAddress), latency)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
"OUT" -> {
|
"OUT" -> {
|
||||||
val state = Integer.parseInt(value)
|
val state = Integer.parseInt(value)
|
||||||
currentMagnet().out = state == 1
|
currentMagnet().out = state == 1
|
||||||
sendOK()
|
sendOK()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
"OUT?" -> {
|
"OUT?" -> {
|
||||||
val out = currentMagnet().out
|
val out = currentMagnet().out
|
||||||
@ -92,32 +93,28 @@ class VirtualLambdaPort(meta: Meta) : VirtualPort(meta) {
|
|||||||
} else {
|
} else {
|
||||||
planResponse("OFF", latency)
|
planResponse("OFF", latency)
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
"PC" -> {
|
"PC" -> {
|
||||||
var current = java.lang.Double.parseDouble(value)
|
val doubleValue = value.toDouble()
|
||||||
if (current < 0.5) {
|
val current = if (doubleValue < 0.5) {
|
||||||
current = 0.0
|
0.0
|
||||||
|
} else {
|
||||||
|
doubleValue
|
||||||
}
|
}
|
||||||
currentMagnet().current = current
|
currentMagnet().current = current
|
||||||
sendOK()
|
sendOK()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
"PC?" -> {
|
"PC?" -> {
|
||||||
planResponse(java.lang.Double.toString(currentMagnet().current), latency)
|
planResponse(java.lang.Double.toString(currentMagnet().current), latency)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
"MC?" -> {
|
"MC?" -> {
|
||||||
planResponse(java.lang.Double.toString(currentMagnet().current), latency)
|
planResponse(java.lang.Double.toString(currentMagnet().current), latency)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
"PV?" -> {
|
"PV?" -> {
|
||||||
planResponse(java.lang.Double.toString(currentMagnet().voltage), latency)
|
planResponse(java.lang.Double.toString(currentMagnet().voltage), latency)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
"MV?" -> {
|
"MV?" -> {
|
||||||
planResponse(java.lang.Double.toString(currentMagnet().voltage), latency)
|
planResponse(java.lang.Double.toString(currentMagnet().voltage), latency)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
else -> LoggerFactory.getLogger(javaClass).warn("Unknown command {}", comand)
|
else -> LoggerFactory.getLogger(javaClass).warn("Unknown command {}", comand)
|
||||||
}
|
}
|
||||||
@ -127,13 +124,13 @@ class VirtualLambdaPort(meta: Meta) : VirtualPort(meta) {
|
|||||||
if (currentAddress < 0) {
|
if (currentAddress < 0) {
|
||||||
throw RuntimeException()
|
throw RuntimeException()
|
||||||
}
|
}
|
||||||
return magnets[currentAddress]!!
|
return statusMap[currentAddress]!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class VirtualMagnetStatus(val resistance: Double,
|
inner class VirtualMagnetStatus(val resistance: Double,
|
||||||
var on: Boolean = true,
|
var on: Boolean = true,
|
||||||
var out: Boolean = false,
|
var out: Boolean = false,
|
||||||
var current: Double = 0.0) {
|
var current: Double = 0.0) {
|
||||||
|
|
||||||
val voltage get() = current * resistance
|
val voltage get() = current * resistance
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,12 @@
|
|||||||
package inr.numass.control.magnet.fx
|
package inr.numass.control.magnet.fx
|
||||||
|
|
||||||
import hep.dataforge.exceptions.PortException
|
import hep.dataforge.exceptions.PortException
|
||||||
|
import hep.dataforge.fx.asDoubleProperty
|
||||||
|
import hep.dataforge.states.ValueState
|
||||||
import inr.numass.control.DeviceDisplayFX
|
import inr.numass.control.DeviceDisplayFX
|
||||||
import inr.numass.control.magnet.LambdaMagnet
|
import inr.numass.control.magnet.LambdaMagnet
|
||||||
import javafx.application.Platform
|
import javafx.application.Platform
|
||||||
|
import javafx.beans.value.ObservableDoubleValue
|
||||||
import javafx.beans.value.ObservableValue
|
import javafx.beans.value.ObservableValue
|
||||||
import javafx.scene.control.*
|
import javafx.scene.control.*
|
||||||
import javafx.scene.layout.AnchorPane
|
import javafx.scene.layout.AnchorPane
|
||||||
@ -43,13 +46,8 @@ class MagnetDisplay : DeviceDisplayFX<LambdaMagnet>() {
|
|||||||
var showConfirmation = true
|
var showConfirmation = true
|
||||||
|
|
||||||
|
|
||||||
val current = valueBinding(device.voltage)
|
val current: ObservableDoubleValue = device.current.asDoubleProperty()
|
||||||
val voltage = valueBinding(device.current)
|
val voltage: ObservableDoubleValue = device.voltage.asDoubleProperty()
|
||||||
var target by device.target.doubleDelegate
|
|
||||||
var output by device.output.booleanDelegate
|
|
||||||
var monitoring by device.monitoring.booleanDelegate
|
|
||||||
var updating by device.updating.booleanDelegate
|
|
||||||
//TODO add status
|
|
||||||
|
|
||||||
|
|
||||||
val labelI: Label by fxid()
|
val labelI: Label by fxid()
|
||||||
@ -62,7 +60,7 @@ class MagnetDisplay : DeviceDisplayFX<LambdaMagnet>() {
|
|||||||
val magnetSpeedField: TextField by fxid()
|
val magnetSpeedField: TextField by fxid()
|
||||||
|
|
||||||
|
|
||||||
init{
|
init {
|
||||||
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
|
||||||
@ -80,39 +78,43 @@ class MagnetDisplay : DeviceDisplayFX<LambdaMagnet>() {
|
|||||||
|
|
||||||
current.onChange {
|
current.onChange {
|
||||||
runLater {
|
runLater {
|
||||||
labelI.text = it?.stringValue()
|
labelI.text = String.format("%.2f",it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
voltage.onChange {
|
voltage.onChange {
|
||||||
runLater {
|
runLater {
|
||||||
labelU.text = it?.stringValue()
|
labelU.text = String.format("%.4f",it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
valueBinding(device.output).onChange {
|
device.states.getState<ValueState>("status")?.onChange{
|
||||||
|
runLater {
|
||||||
|
this.statusLabel.text = it.stringValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device.output.onChange {
|
||||||
Platform.runLater {
|
Platform.runLater {
|
||||||
if (it?.booleanValue() == true) {
|
if (it.booleanValue()) {
|
||||||
this.statusLabel.text = "OK"
|
|
||||||
this.statusLabel.textFill = Color.BLUE
|
this.statusLabel.textFill = Color.BLUE
|
||||||
} else {
|
} else {
|
||||||
this.statusLabel.text = "OFF"
|
|
||||||
this.statusLabel.textFill = Color.BLACK
|
this.statusLabel.textFill = Color.BLACK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
valueBinding(device.updating).onChange {
|
device.updating.onChange {
|
||||||
val updateTaskRunning = it?.booleanValue() ?: false
|
val updateTaskRunning = it.booleanValue()
|
||||||
runLater {
|
runLater {
|
||||||
this.setButton.isSelected = updateTaskRunning
|
this.setButton.isSelected = updateTaskRunning
|
||||||
targetIField.isDisable = updateTaskRunning
|
targetIField.isDisable = updateTaskRunning
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
valueBinding(device.monitoring).onChange {
|
device.monitoring.onChange {
|
||||||
runLater {
|
runLater {
|
||||||
monitorButton.isScaleShape = it?.booleanValue() ?: false
|
monitorButton.isScaleShape = it.booleanValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,66 +127,65 @@ class MagnetDisplay : DeviceDisplayFX<LambdaMagnet>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
monitorButton.selectedProperty().onChange {
|
monitorButton.selectedProperty().onChange {
|
||||||
if (it) {
|
if (device.monitoring.booleanValue != it) {
|
||||||
monitoring = true
|
if (it) {
|
||||||
} else {
|
device.monitoring.set(true)
|
||||||
monitoring = false
|
} else {
|
||||||
this.labelU.text = "----"
|
device.monitoring.set(false)
|
||||||
|
this.labelU.text = "----"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
magnetSpeedField.text = device.speed.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show confirmation dialog
|
||||||
|
*/
|
||||||
|
private fun confirm(): Boolean {
|
||||||
|
return 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)
|
||||||
|
|
||||||
|
alert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.YES
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(PortException::class)
|
|
||||||
private fun setOutputOn(outputOn: Boolean) {
|
private fun setOutputOn(outputOn: Boolean) {
|
||||||
if (outputOn) {
|
if (outputOn && confirm()) {
|
||||||
if (showConfirmation) {
|
val speed = java.lang.Double.parseDouble(magnetSpeedField.text)
|
||||||
val alert = Alert(Alert.AlertType.WARNING)
|
if (speed > 0 && speed <= LambdaMagnet.MAX_SPEED) {
|
||||||
alert.contentText = "Изменение токов в сверхпроводящих магнитах можно производить только при выключенном напряжении на спектрометре." + "\nВы уверены что напряжение выключено?"
|
device.speed = speed
|
||||||
alert.headerText = "Проверьте напряжение на спектрометре!"
|
magnetSpeedField.isDisable = true
|
||||||
alert.height = 150.0
|
device.target.set(targetIField.text.toDouble())
|
||||||
alert.title = "Внимание!"
|
device.output.set(true)
|
||||||
alert.buttonTypes.clear()
|
device.updating.set(true)
|
||||||
alert.buttonTypes.addAll(ButtonType.YES, ButtonType.CANCEL)
|
|
||||||
|
|
||||||
if (alert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.YES) {
|
|
||||||
startCurrentChange()
|
|
||||||
} else {
|
|
||||||
setButton.isSelected = false
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
startCurrentChange()
|
val alert = Alert(Alert.AlertType.ERROR)
|
||||||
|
alert.contentText = null
|
||||||
|
alert.headerText = "Недопустимое значение скорости изменения тока"
|
||||||
|
alert.title = "Ошибка!"
|
||||||
|
alert.show()
|
||||||
|
setButton.isSelected = false
|
||||||
|
magnetSpeedField.text = device.speed.toString()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
device.stopUpdateTask()
|
device.updating.set(false)
|
||||||
targetIField.isDisable = false
|
targetIField.isDisable = false
|
||||||
magnetSpeedField.isDisable = false
|
magnetSpeedField.isDisable = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(PortException::class)
|
private fun displayError(name: String, errorMessage: String?, throwable: Throwable) {
|
||||||
private fun startCurrentChange() {
|
|
||||||
val speed = java.lang.Double.parseDouble(magnetSpeedField.text)
|
|
||||||
if (speed > 0 && speed <= 7) {
|
|
||||||
device.speed = speed
|
|
||||||
magnetSpeedField.isDisable = true
|
|
||||||
target = targetIField.text.toDouble()
|
|
||||||
output = true
|
|
||||||
updating = true
|
|
||||||
} else {
|
|
||||||
val alert = Alert(Alert.AlertType.ERROR)
|
|
||||||
alert.contentText = null
|
|
||||||
alert.headerText = "Недопустимое значение скорости изменения тока"
|
|
||||||
alert.title = "Ошибка!"
|
|
||||||
alert.show()
|
|
||||||
setButton.isSelected = false
|
|
||||||
magnetSpeedField.text = java.lang.Double.toString(device.speed)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<config>
|
<config>
|
||||||
<device name = "numass.magnets">
|
<device name="numass.magnets">
|
||||||
<magnet name="SOURCE" address="1"/>
|
<magnet name="SOURCE" address="1"/>
|
||||||
<magnet name="PINCH" address="2"/>
|
<magnet name="PINCH" address="2"/>
|
||||||
<magnet name="CONUS" address="3"/>
|
<magnet name="CONUS" address="3"/>
|
||||||
<magnet name="DETECTOR" address="4"/>
|
<magnet name="DETECTOR" address="4"/>
|
||||||
|
<bind first="PINCH" second="CONUS" delta="3"/>
|
||||||
<port type="debug">
|
<port type="debug">
|
||||||
<magnet name="SOURCE" address="1" resistance="0.02"/>
|
<magnet name="SOURCE" address="1" resistance="0.02"/>
|
||||||
<magnet name="PINCH" address="2" resistance="0.01"/>
|
<magnet name="PINCH" address="2" resistance="0.01"/>
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package inr.numass.control.magnet
|
||||||
|
|
||||||
|
import hep.dataforge.context.Global
|
||||||
|
import hep.dataforge.kodex.buildMeta
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class VirtualLambdaPortTest{
|
||||||
|
val magnetMeta = buildMeta {
|
||||||
|
node("magnet"){
|
||||||
|
"address" to 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSendOk(){
|
||||||
|
val port = VirtualLambdaPort(magnetMeta)
|
||||||
|
val controller = LambdaPortController(Global,port)
|
||||||
|
controller.setAddress(2)
|
||||||
|
assertEquals(2,port.currentAddress)
|
||||||
|
}
|
||||||
|
}
|
@ -132,12 +132,12 @@ class MspDisplay() : DeviceDisplayFX<MspDevice>(), DeviceListener, NamedValueLis
|
|||||||
cellFormat {
|
cellFormat {
|
||||||
text = "Filament $it"
|
text = "Filament $it"
|
||||||
}
|
}
|
||||||
disableProperty().bind(booleanBinding(PortSensor.CONNECTED_STATE).not())
|
disableProperty().bind(booleanStateProperty(PortSensor.CONNECTED_STATE).not())
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
padding = Insets(5.0, 0.0, 0.0, 0.0)
|
padding = Insets(5.0, 0.0, 0.0, 0.0)
|
||||||
disableProperty()
|
disableProperty()
|
||||||
.bind(booleanBinding(PortSensor.CONNECTED_STATE))
|
.bind(booleanStateProperty(PortSensor.CONNECTED_STATE))
|
||||||
bindBooleanToState("filamentOn", selectedProperty())
|
bindBooleanToState("filamentOn", selectedProperty())
|
||||||
}
|
}
|
||||||
deviceStateIndicator(this@MspDisplay, "filamentStatus", false) {
|
deviceStateIndicator(this@MspDisplay, "filamentStatus", false) {
|
||||||
@ -152,13 +152,13 @@ class MspDisplay() : DeviceDisplayFX<MspDevice>(), DeviceListener, NamedValueLis
|
|||||||
|
|
||||||
togglebutton("Measure") {
|
togglebutton("Measure") {
|
||||||
isSelected = false
|
isSelected = false
|
||||||
disableProperty().bind(booleanBinding(PortSensor.CONNECTED_STATE).not())
|
disableProperty().bind(booleanStateProperty(PortSensor.CONNECTED_STATE).not())
|
||||||
|
|
||||||
bindBooleanToState(Sensor.MEASURING_STATE, selectedProperty())
|
bindBooleanToState(Sensor.MEASURING_STATE, selectedProperty())
|
||||||
}
|
}
|
||||||
togglebutton("Store") {
|
togglebutton("Store") {
|
||||||
isSelected = false
|
isSelected = false
|
||||||
disableProperty().bind(booleanBinding(Sensor.MEASURING_STATE).not())
|
disableProperty().bind(booleanStateProperty(Sensor.MEASURING_STATE).not())
|
||||||
bindBooleanToState("storing", selectedProperty())
|
bindBooleanToState("storing", selectedProperty())
|
||||||
}
|
}
|
||||||
separator(Orientation.VERTICAL)
|
separator(Orientation.VERTICAL)
|
||||||
|
@ -3,24 +3,22 @@ package inr.numass.control
|
|||||||
import hep.dataforge.connections.Connection
|
import hep.dataforge.connections.Connection
|
||||||
import hep.dataforge.control.connections.Roles
|
import hep.dataforge.control.connections.Roles
|
||||||
import hep.dataforge.control.devices.Device
|
import hep.dataforge.control.devices.Device
|
||||||
import hep.dataforge.control.devices.DeviceListener
|
|
||||||
import hep.dataforge.control.devices.PortSensor
|
import hep.dataforge.control.devices.PortSensor
|
||||||
import hep.dataforge.control.devices.Sensor
|
import hep.dataforge.control.devices.Sensor
|
||||||
import hep.dataforge.exceptions.NameNotFoundException
|
import hep.dataforge.exceptions.NameNotFoundException
|
||||||
|
import hep.dataforge.fx.asBooleanProperty
|
||||||
|
import hep.dataforge.fx.asProperty
|
||||||
import hep.dataforge.fx.bindWindow
|
import hep.dataforge.fx.bindWindow
|
||||||
import hep.dataforge.states.State
|
|
||||||
import hep.dataforge.states.ValueState
|
import hep.dataforge.states.ValueState
|
||||||
import hep.dataforge.values.Value
|
import hep.dataforge.values.Value
|
||||||
import javafx.beans.binding.BooleanBinding
|
|
||||||
import javafx.beans.binding.ObjectBinding
|
|
||||||
import javafx.beans.property.BooleanProperty
|
import javafx.beans.property.BooleanProperty
|
||||||
|
import javafx.beans.property.ObjectProperty
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
import javafx.geometry.Pos
|
import javafx.geometry.Pos
|
||||||
import javafx.scene.Parent
|
import javafx.scene.Parent
|
||||||
import javafx.scene.layout.HBox
|
import javafx.scene.layout.HBox
|
||||||
import javafx.scene.layout.Priority
|
import javafx.scene.layout.Priority
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
import java.util.*
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.full.createInstance
|
import kotlin.reflect.full.createInstance
|
||||||
|
|
||||||
@ -37,7 +35,7 @@ fun Device.getDisplay(): DeviceDisplayFX<*> {
|
|||||||
val type = (this::class.annotations.find { it is DeviceView } as DeviceView?)?.value ?: DefaultDisplay::class
|
val type = (this::class.annotations.find { it is DeviceView } as DeviceView?)?.value ?: DefaultDisplay::class
|
||||||
return optConnection(Roles.VIEW_ROLE, DeviceDisplayFX::class.java).orElseGet {
|
return optConnection(Roles.VIEW_ROLE, DeviceDisplayFX::class.java).orElseGet {
|
||||||
type.createInstance().also {
|
type.createInstance().also {
|
||||||
connect(it, Roles.VIEW_ROLE, Roles.DEVICE_LISTENER_ROLE);
|
connect(it, Roles.VIEW_ROLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -48,9 +46,7 @@ fun Device.getDisplay(): DeviceDisplayFX<*> {
|
|||||||
* An FX View to represent the device
|
* An FX View to represent the device
|
||||||
* Created by darksnake on 14-May-17.
|
* Created by darksnake on 14-May-17.
|
||||||
*/
|
*/
|
||||||
abstract class DeviceDisplayFX<D : Device> : Component(), Connection, DeviceListener {
|
abstract class DeviceDisplayFX<D : Device> : Component(), Connection {
|
||||||
|
|
||||||
private val bindings = HashMap<String, ObjectBinding<*>>()
|
|
||||||
|
|
||||||
private val deviceProperty = SimpleObjectProperty<D>(this, "device", null)
|
private val deviceProperty = SimpleObjectProperty<D>(this, "device", null)
|
||||||
val device: D by deviceProperty
|
val device: D by deviceProperty
|
||||||
@ -81,54 +77,16 @@ abstract class DeviceDisplayFX<D : Device> : Component(), Connection, DeviceList
|
|||||||
|
|
||||||
protected abstract fun buildView(device: D): UIComponent?;
|
protected abstract fun buildView(device: D): UIComponent?;
|
||||||
|
|
||||||
/**
|
fun valueStateProperty(stateName: String): ObjectProperty<Value> {
|
||||||
* 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 }
|
val state: ValueState = device.states.filterIsInstance(ValueState::class.java).find { it.name == stateName }
|
||||||
?: throw NameNotFoundException("State with name $stateName not found")
|
?: throw NameNotFoundException("State with name $stateName not found")
|
||||||
return valueBinding(state)
|
return state.asProperty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun booleanBinding(stateName: String): BooleanBinding {
|
fun booleanStateProperty(stateName: String): BooleanProperty {
|
||||||
return valueBinding(stateName).booleanBinding { it?.booleanValue() ?: false }
|
val state: ValueState = device.states.filterIsInstance(ValueState::class.java).find { it.name == stateName }
|
||||||
}
|
?: throw NameNotFoundException("State with name $stateName not found")
|
||||||
|
return state.asBooleanProperty()
|
||||||
/**
|
|
||||||
* 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 {
|
open fun getBoardView(): Parent {
|
||||||
|
@ -76,9 +76,9 @@ fun EventTarget.indicator(radius: Double = 10.0, op: (Indicator.() -> Unit) = {}
|
|||||||
fun Indicator.bind(connection: DeviceDisplayFX<*>, 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.valueBinding(state), transform);
|
bind(connection.valueStateProperty(state), transform);
|
||||||
} else {
|
} else {
|
||||||
bind(connection.valueBinding(state)) {
|
bind(connection.valueStateProperty(state)) {
|
||||||
when {
|
when {
|
||||||
it.isNull -> Color.GRAY
|
it.isNull -> Color.GRAY
|
||||||
it.booleanValue() -> Color.GREEN
|
it.booleanValue() -> Color.GREEN
|
||||||
@ -117,7 +117,7 @@ fun Node.deviceStateToggle(connection: DeviceDisplayFX<*>, state: String, title:
|
|||||||
connection.device.states[state] = newValue
|
connection.device.states[state] = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
connection.valueBinding(state).onChange {
|
connection.valueStateProperty(state).onChange {
|
||||||
isSelected = it?.booleanValue() ?: false
|
isSelected = it?.booleanValue() ?: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package inr.numass.control
|
package inr.numass.control
|
||||||
|
|
||||||
import ch.qos.logback.classic.Level
|
import ch.qos.logback.classic.Level
|
||||||
import hep.dataforge.control.connections.Roles
|
|
||||||
import hep.dataforge.control.devices.Device
|
import hep.dataforge.control.devices.Device
|
||||||
import hep.dataforge.control.devices.DeviceFactory
|
import hep.dataforge.control.devices.DeviceFactory
|
||||||
import hep.dataforge.exceptions.ControlException
|
import hep.dataforge.exceptions.ControlException
|
||||||
@ -26,7 +25,6 @@ abstract class NumassControlApplication<in D : Device> : App() {
|
|||||||
|
|
||||||
device = setupDevice().also {
|
device = setupDevice().also {
|
||||||
val controller = it.getDisplay()
|
val controller = it.getDisplay()
|
||||||
it.connect(controller, Roles.VIEW_ROLE, Roles.DEVICE_LISTENER_ROLE)
|
|
||||||
val scene = Scene(controller.view?.root ?: controller.getBoardView())
|
val scene = Scene(controller.view?.root ?: controller.getBoardView())
|
||||||
stage.scene = scene
|
stage.scene = scene
|
||||||
|
|
||||||
@ -72,8 +70,8 @@ abstract class NumassControlApplication<in D : Device> : App() {
|
|||||||
try {
|
try {
|
||||||
device?.shutdown()
|
device?.shutdown()
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
|
LoggerFactory.getLogger(javaClass).error("Failed to properly shutdown application", ex);
|
||||||
device?.context?.close()
|
device?.context?.close()
|
||||||
LoggerFactory.getLogger(javaClass).error("Failed to shutdown application", ex);
|
|
||||||
} finally {
|
} finally {
|
||||||
super.stop()
|
super.stop()
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ import hep.dataforge.storage.commons.StorageConnection
|
|||||||
import hep.dataforge.tables.TableFormatBuilder
|
import hep.dataforge.tables.TableFormatBuilder
|
||||||
import hep.dataforge.tables.ValueMap
|
import hep.dataforge.tables.ValueMap
|
||||||
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 hep.dataforge.values.Values
|
import hep.dataforge.values.Values
|
||||||
import inr.numass.control.DeviceView
|
import inr.numass.control.DeviceView
|
||||||
@ -65,7 +64,8 @@ class VacCollectorDevice(context: Context, meta: Meta, val sensors: Collection<S
|
|||||||
override fun optDevice(name: Name): Optional<Device> =
|
override fun optDevice(name: Name): Optional<Device> =
|
||||||
Optional.ofNullable(sensors.find { it.name == name.toUnescaped() })
|
Optional.ofNullable(sensors.find { it.name == name.toUnescaped() })
|
||||||
|
|
||||||
override fun getDeviceNames(): Stream<Name> = sensors.stream().map { Name.ofSingle(it.name) }
|
override val deviceNames: Stream<Name>
|
||||||
|
get() = sensors.stream().map { Name.ofSingle(it.name) }
|
||||||
|
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
@ -113,14 +113,9 @@ class VacCollectorDevice(context: Context, meta: Meta, val sensors: Collection<S
|
|||||||
helper.push(values)
|
helper.push(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun stopMeasurement() {
|
||||||
|
super.stopMeasurement()
|
||||||
override fun onStateChange(stateName: String, value: Any) {
|
notifyResult(terminator())
|
||||||
if (stateName == MEASURING_STATE) {
|
|
||||||
if (!(value as Value).booleanValue()) {
|
|
||||||
notifyResult(terminator())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) {
|
override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) {
|
||||||
|
Loading…
Reference in New Issue
Block a user