Numass control update

This commit is contained in:
Alexander Nozik 2018-04-05 17:03:05 +03:00
parent af54525d41
commit 98ef7688d5
26 changed files with 417 additions and 1013 deletions

View File

@ -16,6 +16,7 @@
package inr.numass.control.cryotemp
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaUtils
import inr.numass.control.NumassControlApplication
import javafx.stage.Stage
@ -32,9 +33,7 @@ class PKT8App : NumassControlApplication<PKT8Device>() {
stage.minWidth = 400.0
}
override fun acceptDevice(meta: Meta): Boolean {
return meta.getString("type") == "PKT8"
override fun getDeviceMeta(config: Meta): Meta {
return MetaUtils.findNode(config,"device"){it.getString("name") == "numass.temp"}.orElseThrow{RuntimeException("Temperature measurement configuration not found")}
}
}

View File

@ -274,7 +274,7 @@ class PKT8Device(context: Context, meta: Meta) : PortSensor(context, meta) {
//send start signal
it.send("s")
notifyMeasurementState(MeasurementState.IN_PROGRESS)
} ?: notifyError("Not connected")
}
}

View File

@ -3,7 +3,7 @@ apply plugin: 'application'
version = "0.3.0"
if (!hasProperty('mainClass')) {
ext.mainClass = 'inr.numass.control.magnet.fx.MagnetControllerApp'
ext.mainClass = 'inr.numass.control.magnet.fx.MagnetApp'
}
mainClassName = mainClass
@ -22,3 +22,11 @@ task talkToServer(type: JavaExec) {
standardInput = System.in
standardOutput = System.out
}
task emulate(type: JavaExec){
group = "application"
main = mainClassName
description = "Test magnet controller with virtual device configuration"
classpath = sourceSets.main.runtimeClasspath
args = ["--config.resource=debug.xml"]
}

View File

@ -26,16 +26,20 @@ class LambdaHub(context: Context, meta: Meta) : DeviceHub, AbstractDevice(contex
init {
meta.useEachMeta("magnet") {
magnets.add(LambdaMagnet(context, it, controller))
magnets.add(LambdaMagnet(controller, it))
}
meta.useEachMeta("bind") {
TODO("add binding")
}
}
private fun buildPort(): Port {
val portName = meta.getString("port");
return if(portName.startsWith("virtual")){
VirtualLambdaPort(meta)
val portMeta = meta.getMetaOrEmpty("port")
return if(portMeta.getString("type") == "debug"){
VirtualLambdaPort(portMeta)
} else{
PortFactory.build(portName);
PortFactory.build(portMeta)
}
}

View File

@ -22,6 +22,7 @@ import hep.dataforge.control.ports.PortFactory
import hep.dataforge.description.ValueDef
import hep.dataforge.exceptions.ControlException
import hep.dataforge.exceptions.PortException
import hep.dataforge.kodex.buildMeta
import hep.dataforge.meta.Meta
import hep.dataforge.states.StateDef
import hep.dataforge.states.StateDefs
@ -29,9 +30,6 @@ import hep.dataforge.states.valueState
import hep.dataforge.utils.DateTimeUtils
import hep.dataforge.values.ValueType.*
import kotlinx.coroutines.experimental.runBlocking
import org.slf4j.LoggerFactory
import java.text.DecimalFormat
import java.time.Duration
import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.concurrent.Future
@ -41,7 +39,7 @@ import java.util.concurrent.TimeUnit
/**
* @author Polina
*/
@ValueDef(name = "timeout", type = [NUMBER], def = "400", info = "A timeout for port response")
@StateDefs(
StateDef(value = ValueDef(name = "current", type = arrayOf(NUMBER), def = "0", info = "Current current")),
StateDef(value = ValueDef(name = "voltage", type = arrayOf(NUMBER), def = "0", info = "Current voltage")),
@ -51,9 +49,10 @@ import java.util.concurrent.TimeUnit
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 = "monitoring", type = arrayOf(BOOLEAN), def = "false", info = "Shows if monitoring task is running"), writable = true),
StateDef(value = ValueDef(name = "speed", type = arrayOf(NUMBER), info = "Current change speed in Amper per minute"), writable = true)
StateDef(value = ValueDef(name = "speed", type = arrayOf(NUMBER), info = "Current change speed in Ampere per minute"), writable = true),
StateDef(ValueDef(name = "state", type = [STRING], def = "INIT", enumeration = LambdaMagnet.MagnetState::class, info = "Current state of magnet operation"))
)
open class LambdaMagnet(context: Context, meta: Meta, private val controller: LambdaPortController) : AbstractDevice(context, meta) {
class LambdaMagnet(private val controller: LambdaPortController, meta: Meta) : AbstractDevice(controller.context, meta) {
private var closePortOnShutDown = false
@ -67,7 +66,7 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
//var listener: MagnetStateListener? = null
// private volatile double current = 0;
private val timeout: Duration = meta.optString("timeout").map<Duration> { Duration.parse(it) }.orElse(Duration.ofMillis(200))
private var monitorTask: Future<*>? = null
private var updateTask: Future<*>? = null
@ -75,17 +74,15 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
private set
// read-only values of current output
val current = valueState("current", getter = { controller.talk(address, timeout) { s2d(getParameter("MC")) } })
// val current by current.double
val current = valueState("current", getter = { s2d(controller.getParameter(address, "MC")) })
val voltage = valueState("voltage", getter = { controller.talk(address, timeout) { s2d(getParameter("MV")) } })
// val voltage by voltage.double
val voltage = valueState("voltage", getter = { s2d(controller.getParameter(address, "MV")) })
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())) {
private var outCurrent by valueState("outCurrent", getter = { s2d(controller.getParameter(address, "PC")) }) { _, value ->
if (controller.setParameter(address, "PC", value.doubleValue())) {
lastUpdate = DateTimeUtils.now()
} else {
notifyError("Can't set the target current")
@ -93,15 +90,18 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
return@valueState value
}.doubleDelegate
private val outVoltage = valueState("outVoltage", getter = { controller.talk(address, timeout) { s2d(getParameter("PV")) } }) { _, value ->
if (!setParameter("PV", value.doubleValue())) {
private val outVoltage = valueState("outVoltage", getter = { s2d(controller.getParameter(address, "PV")) }) { _, value ->
if (!controller.setParameter(address, "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 ->
val output = valueState("output", getter = { controller.talk(address, "OUT?") == "OK" }) { _, value ->
setOutputMode(value.booleanValue())
if (!value.booleanValue()) {
state = MagnetState.OFF
}
}
var monitoring = valueState("monitoring", getter = { monitorTask != null }) { _, value ->
@ -113,6 +113,9 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
return@valueState value
}
/**
*
*/
var updating = valueState("updating", getter = { updateTask != null }) { _, value ->
if (value.booleanValue()) {
startUpdateTask()
@ -124,11 +127,20 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
/**
* current change speed in Amper per minute
* current change speed in Ampere per minute
*
* @param speed
*/
var speed = MAX_SPEED
var speed by valueState("speed").doubleDelegate
var state by valueState("state").enumDelegate<MagnetState>()
private set
/**
* The binding limit for magnet current
*/
var bound: (Double) -> Boolean = { it < meta.getDouble("maxCurrent") }
/**
* A setup for single magnet controller
@ -138,10 +150,12 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
* @throws ControlException
*/
@Throws(ControlException::class)
constructor(context: Context, meta: Meta) : this(context, meta, LambdaPortController(context, PortFactory.build(meta.getString("port")))) {
constructor(context: Context, meta: Meta) : this(LambdaPortController(context, PortFactory.build(meta.getString("port"))), meta) {
closePortOnShutDown = true
}
constructor(context: Context, port: Port, address: Int) : this(LambdaPortController(context, port), buildMeta { "address" to address })
@Throws(ControlException::class)
override fun init() {
super.init()
@ -162,46 +176,6 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
}
// private fun reportError(errorMessage: String, error: Throwable?) {
// listener?.error(name, errorMessage, error) ?: LoggerFactory.getLogger(javaClass).error(errorMessage, error)
//
// }
@Throws(PortException::class)
private fun talk(request: String): String {
return try {
controller.send(request + "\r")
controller.waitFor(timeout).trim()
} catch (tex: Port.PortTimeoutException) {
//Single retry on timeout
LoggerFactory.getLogger(javaClass).warn("A timeout exception for request '$request'. Making another attempt.")
controller.send(request + "\r")
controller.waitFor(timeout).trim()
}
}
// private fun update(key: String, value: String) {
// when (key) {
// "OUT" -> updateState("output", value == "ON")
// "MC" -> updateState("current", s2d(value))
// "PC" -> updateState("outCurrent", s2d(value))
// "MV" -> updateState("voltage", s2d(value))
// "PV" -> updateState("outVoltage", s2d(value))
// }
// }
@Throws(PortException::class)
private fun getParameter(name: String): String = talk("$name?")
@Throws(PortException::class)
private fun setParameter(key: String, state: String): Boolean = "OK" == talk("$key $state")
@Throws(PortException::class)
private fun setParameter(key: String, state: Int): Boolean = setParameter(key, state.toString())
@Throws(PortException::class)
private fun setParameter(key: String, state: Double): Boolean = setParameter(key, d2s(state))
/**
* Extract number from LAMBDA response
*
@ -235,7 +209,12 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
updateState("current", measuredI)
if (Math.abs(measuredI - targetI) > CURRENT_PRECISION) {
val nextI = nextI(measuredI, targetI)
if (bound(nextI)) {
outCurrent = nextI
state = MagnetState.OK
} else {
state = MagnetState.BOUND
}
} else {
stopUpdateTask()
}
@ -253,7 +232,7 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
@Throws(PortException::class)
private fun setOutputMode(out: Boolean) {
val outState: Int = if (out) 1 else 0
if (!setParameter("OUT", outState)) {
if (!controller.setParameter(address, "OUT", outState)) {
notifyError("Can't set output mode")
} else {
updateState("output", out)
@ -261,7 +240,6 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
}
private fun nextI(measuredI: Double, targetI: Double): Double {
// assert(measuredI != target)
var step = if (lastUpdate == Instant.EPOCH) {
MIN_UP_STEP_SIZE
@ -325,9 +303,26 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
}
/**
* 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 MagnetState {
INIT, // no information
OFF, // Magnet output is off
OK, // Magnet ouput is on
ERROR, // Some error
BOUND // Magnet in bound mode
}
companion object {
private val LAMBDA_FORMAT = DecimalFormat("###.##")
const val CURRENT_PRECISION = 0.05
const val DEFAULT_DELAY = 1
const val DEFAULT_MONITOR_DELAY = 2000
@ -336,13 +331,6 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
const val MIN_DOWN_STEP_SIZE = 0.05
const val MAX_SPEED = 5.0 // 5 A per minute
/**
* Method converts double to LAMBDA string
*
* @param d double that should be converted to string
* @return string
*/
private fun d2s(d: Double): String = LAMBDA_FORMAT.format(d)
}
}

View File

@ -3,11 +3,17 @@ package inr.numass.control.magnet
import hep.dataforge.context.Context
import hep.dataforge.control.ports.GenericPortController
import hep.dataforge.control.ports.Port
import hep.dataforge.exceptions.PortException
import org.slf4j.LoggerFactory
import java.text.DecimalFormat
import java.time.Duration
//@ValueDef(name = "timeout", type = [(ValueType.NUMBER)], def = "400", info = "A timeout for port response")
class LambdaPortController(context: Context, port: Port) : GenericPortController(context, port) {
private var currentAddress: Int = -1;
private val timeout: Duration = port.meta.optString("timeout").map<Duration> { Duration.parse(it) }.orElse(Duration.ofMillis(200))
private fun setAddress(address: Int, timeout: Duration) {
val response = sendAndWait("ADR $address\r", timeout) { true }.trim()
if (response == "OK") {
@ -20,10 +26,47 @@ class LambdaPortController(context: Context, port: Port) : GenericPortController
/**
* perform series of synchronous actions ensuring that all of them have the same address
*/
fun <R> talk(address: Int, timeout: Duration, action: (GenericPortController) -> R): R {
private fun <R> talk(address: Int, action: GenericPortController.() -> R): R {
synchronized(this) {
setAddress(address, timeout)
return action(this)
return this.action()
}
}
@Throws(PortException::class)
fun talk(addres: Int, request: String): String {
return talk(addres) {
try {
send(request + "\r")
waitFor(timeout).trim()
} catch (tex: Port.PortTimeoutException) {
//Single retry on timeout
LoggerFactory.getLogger(javaClass).warn("A timeout exception for request '$request'. Making another attempt.")
send(request + "\r")
waitFor(timeout).trim()
}
}
}
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: Int): Boolean = setParameter(address, key, state.toString())
fun setParameter(address: Int, key: String, state: Double): Boolean = setParameter(address, key, d2s(state))
companion object {
private val LAMBDA_FORMAT = DecimalFormat("###.##")
/**
* Method converts double to LAMBDA string
*
* @param d double that should be converted to string
* @return string
*/
private fun d2s(d: Double): String = LAMBDA_FORMAT.format(d)
}
}

View File

@ -1,54 +0,0 @@
/*
* Copyright 2015 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
/**
*
* @author Alexander Nozik
*/
interface MagnetStateListener {
fun acceptStatus(name: String, state: MagnetStatus)
fun acceptNextI(name: String, nextI: Double)
fun acceptMeasuredI(name: String, measuredI: Double)
open fun displayState(state: String) {
}
open fun error(name: String, errorMessage: String, throwable: Throwable?) {
throw RuntimeException(errorMessage, throwable)
}
open fun monitorTaskStateChanged(name: String, monitorTaskRunning: Boolean) {
}
open fun updateTaskStateChanged(name: String, updateTaskRunning: Boolean) {
}
open fun outputModeChanged(name: String, out: Boolean) {
}
fun addressChanged(name: String, address: Int) {
}
}

View File

@ -1,63 +0,0 @@
/*
* Copyright 2015 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
/**
*
* @author Polina
*/
class MagnetStatus(
/**
* @return the isOut
*/
val isOutputOn: Boolean,
/**
* @return the measuredCurrent
*/
val measuredCurrent: Double,
/**
* @return the setCurrent
*/
val setCurrent: Double,
/**
* @return the measuredVoltage
*/
val measuredVoltage: Double,
/**
* @return the setVoltage
*/
val setVoltage: Double) {
/**
* @return the isOn
*/
var isOn: Boolean = false
private set
init {
this.isOn = true
}
companion object {
fun off(): MagnetStatus {
val res = MagnetStatus(false, 0.0, 0.0, 0.0, 0.0)
res.isOn = false
return res
}
}
}

View File

@ -1,90 +0,0 @@
/*
* Copyright 2015 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.Context
import hep.dataforge.exceptions.PortException
import hep.dataforge.meta.Meta
import org.slf4j.LoggerFactory
import java.util.*
/**
*
* @author Polina
*/
class SafeLambdaMagnet(context: Context, meta: Meta, controller: LambdaPortController) :
LambdaMagnet(context, meta, controller) {
private val safeConditions = HashSet<SafeMagnetCondition>()
// public SafeLambdaMagnet(String name, Port port, int address, int timeout, SafeMagnetCondition... safeConditions) {
// super(name, port, address, timeout);
// this.safeConditions.addAll(Arrays.asList(safeConditions));
// }
//
// public SafeLambdaMagnet(String name, Port port, int address, SafeMagnetCondition... safeConditions) {
// super(name, port, address);
// this.safeConditions.addAll(Arrays.asList(safeConditions));
// }
fun addSafeCondition(isBlocking: Boolean, condition: (Double) -> Boolean) {
this.safeConditions.add(object : SafeMagnetCondition {
override fun isBlocking(): Boolean = isBlocking
override fun isSafe(address: Int, current: Double): Boolean = condition(current)
})
}
/**
* Add symmetric non-blocking conditions to ensure currents in two magnets have difference within given tolerance.
* @param controller
* @param tolerance
*/
fun bindTo(controller: SafeLambdaMagnet, tolerance: Double) {
this.addSafeCondition(false) { I -> Math.abs(controller.current - I) <= tolerance }
controller.addSafeCondition(false) { I -> Math.abs(this.current - I) <= tolerance }
}
@Throws(PortException::class)
override fun setCurrent(current: Double) {
safeConditions
.filterNot { it.isSafe(address, current) }
.forEach {
if (it.isBlocking()) {
it.onFail()
throw RuntimeException("Can't set current. Condition not satisfied.")
} else {
listener?.displayState("BOUND")
}
}
super.current = current
}
interface SafeMagnetCondition {
fun isBlocking(): Boolean = true
fun isSafe(address: Int, current: Double): Boolean
fun onFail() {
LoggerFactory.getLogger(javaClass).error("Can't set current. Condition not satisfied.")
}
}
}

View File

@ -1,90 +0,0 @@
/*
* Copyright 2015 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.control.ports.Port
import java.util.*
/**
*
* @author Alexander Nozik
*/
object TestController {
/**
* @param args the command line arguments
* @throws java.lang.Exception
*/
@Throws(Exception::class)
@JvmStatic
fun main(args: Array<String>) {
Locale.setDefault(Locale.US)// чтобы отделение десятичных знаков было точкой
// ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
// rootLogger.setLevel(Level.INFO);
val handler: Port
val firstController: LambdaMagnet
val secondController: LambdaMagnet
// String comName = "COM12";
// handler = new ComPort(comName);
handler = VirtualLambdaPort("COM12", 1, 2, 3, 4)
firstController = LambdaMagnet(handler, 1)
// secondController = new LambdaMagnet(handler, 4);
secondController = SafeLambdaMagnet("TEST", handler, 4, { address: Int, current: Double -> current < 1.0 })
val listener = object : MagnetStateListener {
override fun acceptStatus(name: String, state: MagnetStatus) {
System.out.printf("%s (%s): Im = %f, Um = %f, Is = %f, Us = %f;%n",
name,
state.isOutputOn,
state.measuredCurrent,
state.measuredVoltage,
state.setCurrent,
state.setVoltage
)
}
override fun acceptNextI(name: String, nextI: Double) {
System.out.printf("%s: nextI = %f;%n", name, nextI)
}
override fun acceptMeasuredI(name: String, measuredI: Double) {
System.out.printf("%s: measuredI = %f;%n", name, measuredI)
}
}
firstController.listener = listener
secondController.listener = listener
try {
firstController.startMonitorTask(2000)
secondController.startMonitorTask(2000)
secondController.setOutputMode(true)
secondController.startUpdateTask(2.0, 1000)
System.`in`.read()
firstController.stopMonitorTask()
secondController.stopMonitorTask()
secondController.stopUpdateTask()
secondController.setOutputMode(false)
} finally {
// handler.close();
}
}
}

View File

@ -1,114 +0,0 @@
/*
* Copyright 2015 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 ch.qos.logback.classic.Level
import hep.dataforge.control.ports.Port
import org.slf4j.LoggerFactory
import java.util.*
/**
*
* @author Alexander Nozik
*/
object TestSynch {
private val firstCurrent = 0.0
/**
* @param args the command line arguments
* @throws java.lang.Exception
*/
@Throws(Exception::class)
@JvmStatic
fun main(args: Array<String>) {
Locale.setDefault(Locale.US)// чтобы отделение десятичных знаков было точкой
val rootLogger = LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME) as ch.qos.logback.classic.Logger
rootLogger.level = Level.INFO
val handler: Port
val firstController: LambdaMagnet
val secondController: LambdaMagnet
// String comName = "COM12";
// handler = new ComPort(comName);
handler = VirtualLambdaPort("COM12", 1, 2, 3, 4)
firstController = LambdaMagnet(handler, 1)
// secondController = new LambdaMagnet(handler, 2);
secondController = SafeLambdaMagnet("TEST", handler, 2,
object : SafeLambdaMagnet.SafeMagnetCondition {
// @Override
// public boolean isBlocking() {
// return false;
// }
override fun onFail() {
java.awt.Toolkit.getDefaultToolkit().beep()
}
override fun isSafe(address: Int, current: Double): Boolean {
return Math.abs(current - firstCurrent) <= 0.2
}
})
val listener = object : MagnetStateListener {
override fun acceptStatus(name: String, state: MagnetStatus) {
System.out.printf("%s (%s): Im = %f, Um = %f, Is = %f, Us = %f;%n",
name,
state.isOutputOn,
state.measuredCurrent,
state.measuredVoltage,
state.setCurrent,
state.setVoltage
)
}
override fun acceptNextI(name: String, nextI: Double) {
System.out.printf("%s: nextI = %f;%n", name, nextI)
}
override fun acceptMeasuredI(name: String, measuredI: Double) {
System.out.printf("%s: measuredI = %f;%n", name, measuredI)
}
}
firstController.listener = listener
secondController.listener = listener
try {
firstController.startMonitorTask(2000)
secondController.startMonitorTask(2000)
secondController.setOutputMode(true)
firstController.setOutputMode(true)
firstController.startUpdateTask(1.0, 10)
secondController.startUpdateTask(2.0, 10)
System.`in`.read()
firstController.stopMonitorTask()
secondController.stopMonitorTask()
secondController.stopUpdateTask()
firstController.stopUpdateTask()
secondController.setOutputMode(false)
firstController.setOutputMode(false)
System.exit(0)
} finally {
// handler.close();
}
}
}

View File

@ -16,7 +16,7 @@
package inr.numass.control.magnet
import hep.dataforge.control.ports.VirtualPort
import hep.dataforge.exceptions.PortException
import hep.dataforge.kodex.useEachMeta
import hep.dataforge.meta.Meta
import org.slf4j.LoggerFactory
import java.time.Duration
@ -30,29 +30,17 @@ class VirtualLambdaPort(meta: Meta) : VirtualPort(meta) {
@Volatile private var currentAddress = -1
private val magnets = HashMap<Int, VirtualMagnetStatus>()
private val virtualPortName: String = meta.getString("name", "virtual::numass.lambda")
// constructor(portName: String, magnets: Map<Int, Double>) {
// this.virtualPortName = portName
// magnets.forEach { key, value -> this.magnets.put(key, VirtualMagnetStatus(value)) }
// }
//
// constructor(portName: String, vararg magnets: Int) {
// this.virtualPortName = portName
// for (magnet in magnets) {
// this.magnets.put(magnet, VirtualMagnetStatus(0.01))
// }
// }
override val name: String = meta.getString("name", "virtual::numass.lambda")
init {
meta.useEachMeta("magnet") {
val num = it.getInt("address", 1)
val resistance = it.getDouble("resistance", 1.0)
magnets.put(num, VirtualMagnetStatus(resistance))
magnets[num] = VirtualMagnetStatus(resistance)
}
}
override fun toString(): String = virtualPortName
override fun toString(): String = name
override fun evaluateRequest(request: String) {
val command: String
@ -67,8 +55,7 @@ class VirtualLambdaPort(meta: Meta) : VirtualPort(meta) {
try {
evaluateRequest(command.trim { it <= ' ' }, value.trim { it <= ' ' })
} catch (ex: RuntimeException) {
receivePhrase("FAIL")//TODO какая команда правильная?
receive("FAIL".toByteArray())//TODO какая команда правильная?
LoggerFactory.getLogger(javaClass).error("Request evaluation failure", ex)
}
@ -125,14 +112,14 @@ class VirtualLambdaPort(meta: Meta) : VirtualPort(meta) {
return
}
"PV?" -> {
planResponse(java.lang.Double.toString(currentMagnet().getVoltage()), latency)
planResponse(java.lang.Double.toString(currentMagnet().voltage), latency)
return
}
"MV?" -> {
planResponse(java.lang.Double.toString(currentMagnet().getVoltage()), latency)
planResponse(java.lang.Double.toString(currentMagnet().voltage), latency)
return
}
else -> LoggerFactory.getLogger(javaClass).warn("Unknown comand {}", comand)
else -> LoggerFactory.getLogger(javaClass).warn("Unknown command {}", comand)
}
}
@ -143,28 +130,15 @@ class VirtualLambdaPort(meta: Meta) : VirtualPort(meta) {
return magnets[currentAddress]!!
}
@Throws(Exception::class)
override fun close() {
}
@Throws(PortException::class)
override fun open() {
}
override fun isOpen(): Boolean = true
private inner class VirtualMagnetStatus(val resistance: Double,
var on: Boolean = true,
var out: Boolean = false,
var current: Double = 0.0) {
fun getVoltage() = current * resistance
val voltage get() = current * resistance
}
companion object {
private val latency = Duration.ofMillis(50)
}
}

View File

@ -0,0 +1,32 @@
package inr.numass.control.magnet.fx
import hep.dataforge.context.Context
import hep.dataforge.control.devices.Device
import hep.dataforge.control.devices.DeviceFactory
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaUtils
import inr.numass.control.NumassControlApplication
import inr.numass.control.magnet.LambdaHub
import javafx.stage.Stage
class MagnetApp: NumassControlApplication<LambdaHub>() {
override val deviceFactory: DeviceFactory = object :DeviceFactory{
override fun getType(): String {
return "numass.lambda"
}
override fun build(context: Context, meta: Meta): Device {
return LambdaHub(context, meta)
}
}
override fun setupStage(stage: Stage, device: LambdaHub) {
stage.title = "Numass magnet control"
}
override fun getDeviceMeta(config: Meta): Meta {
return MetaUtils.findNode(config,"device"){it.getString("name") == "numass.magnets"}.orElseThrow{RuntimeException("Magnet configuration not found")}
}
}

View File

@ -15,127 +15,109 @@
*/
package inr.numass.control.magnet.fx
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.FileAppender
import hep.dataforge.exceptions.ControlException
import hep.dataforge.io.MetaFileReader
import inr.numass.control.magnet.LambdaHub
import javafx.application.Application
import javafx.scene.Scene
import javafx.scene.layout.VBox
import javafx.stage.Stage
import org.slf4j.LoggerFactory
import java.io.IOException
import java.util.*
import tornadofx.*
/**
*
* @author Alexander Nozik
*/
class MagnetControllerApp : Application() {
class MagnetControllerApp : App() {
// internal var handler: Port
// internal var sourceController: SafeLambdaMagnet
// internal var pinchController: SafeLambdaMagnet
// internal var conusController: SafeLambdaMagnet
// internal var detectorController: SafeLambdaMagnet
// internal var controllers: MutableList<SafeLambdaMagnet> = ArrayList()
private lateinit var device: LambdaHub
@Throws(IOException::class, ControlException::class)
override fun start(stage: Stage) {
Locale.setDefault(Locale.US)// чтобы отделение десятичных знаков было точкой
val rootLogger = LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME) as ch.qos.logback.classic.Logger
val logLevel = parameters.named.getOrDefault("logLevel", "INFO")
rootLogger.level = Level.valueOf(logLevel)
val logFile = parameters.named["logFile"]
if (logFile != null) {
val appender = FileAppender<ILoggingEvent>()
appender.file = logFile
appender.context = rootLogger.loggerContext
appender.start()
rootLogger.addAppender(appender)
}
val config = MetaFileReader.instance().read(context,)
// val portName = (parameters.named as java.util.Map<String, String>).getOrDefault("port", "virtual")
//
// if (portName == "virtual") {
// handler = VirtualLambdaPort("COM12", 1, 2, 3, 4)
// } else {
// handler = PortFactory.getPort(portName)
// //TODO add meta reader here
// private lateinit var device: LambdaHub
//
// @Throws(IOException::class, ControlException::class)
// override fun start(stage: Stage) {
// Locale.setDefault(Locale.US)// чтобы отделение десятичных знаков было точкой
// val rootLogger = LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME) as ch.qos.logback.classic.Logger
//
// val logLevel = parameters.named.getOrDefault("logLevel", "INFO")
//
// rootLogger.level = Level.valueOf(logLevel)
//
// val logFile = parameters.named["logFile"]
//
// if (logFile != null) {
// val appender = FileAppender<ILoggingEvent>()
// appender.file = logFile
// appender.context = rootLogger.loggerContext
// appender.start()
// rootLogger.addAppender(appender)
// }
//
// sourceController = SafeLambdaMagnet("SOURCE", handler, 1)
// pinchController = SafeLambdaMagnet("PINCH", handler, 2)
// conusController = SafeLambdaMagnet("CONUS", handler, 3)
// detectorController = SafeLambdaMagnet("DETECTOR", handler, 4)
//
// conusController.bindTo(pinchController, 30.0)
// val configFileName = parameters.named
// val config = MetaFileReader.instance().read(context,)
//
// controllers.add(sourceController)
// sourceController.speed = 4.0
// controllers.add(pinchController)
// controllers.add(conusController)
// controllers.add(detectorController)
val showConfirmation = java.lang.Boolean.parseBoolean((parameters.named as java.util.Map<String, String>).getOrDefault("confirmOut", "false"))
val vbox = VBox(5.0)
var height = 0.0
var width = 0.0
for (controller in controllers) {
val comp = MagnetControllerComponent.build(controller)
width = Math.max(width, comp.prefWidth)
height += comp.prefHeight + 5
if (!showConfirmation) {
comp.setShowConfirmation(showConfirmation)
}
vbox.children.add(comp)
}
val scene = Scene(vbox, width, height)
stage.title = "Numass magnet view"
stage.scene = scene
stage.isResizable = false
stage.show()
}
@Throws(Exception::class)
override fun stop() {
super.stop() //To change body of generated methods, choose Tools | Templates.
for (magnet in controllers) {
magnet.stopMonitorTask()
magnet.stopUpdateTask()
}
if (handler.isOpen) {
handler.close()
}
System.exit(0)
}
companion object {
/**
* @param args the command line arguments
*/
@JvmStatic
fun main(args: Array<String>) {
Application.launch(*args)
}
}
//// val portName = (parameters.named as java.util.Map<String, String>).getOrDefault("port", "virtual")
////
//// if (portName == "virtual") {
//// handler = VirtualLambdaPort("COM12", 1, 2, 3, 4)
//// } else {
//// handler = PortFactory.getPort(portName)
//// //TODO add meta reader here
//// }
////
//// sourceController = SafeLambdaMagnet("SOURCE", handler, 1)
//// pinchController = SafeLambdaMagnet("PINCH", handler, 2)
//// conusController = SafeLambdaMagnet("CONUS", handler, 3)
//// detectorController = SafeLambdaMagnet("DETECTOR", handler, 4)
////
//// conusController.bindTo(pinchController, 30.0)
////
//// controllers.add(sourceController)
//// sourceController.speed = 4.0
//// controllers.add(pinchController)
//// controllers.add(conusController)
//// controllers.add(detectorController)
//
//
// val showConfirmation = java.lang.Boolean.parseBoolean((parameters.named as java.util.Map<String, String>).getOrDefault("confirmOut", "false"))
//
// val vbox = VBox(5.0)
// var height = 0.0
// var width = 0.0
// for (controller in controllers) {
// val comp = MagnetControllerComponent.build(controller)
// width = Math.max(width, comp.prefWidth)
// height += comp.prefHeight + 5
// if (!showConfirmation) {
// comp.setShowConfirmation(showConfirmation)
// }
// vbox.children.add(comp)
// }
//
// val scene = Scene(vbox, width, height)
//
//
// stage.title = "Numass magnet view"
// stage.scene = scene
// stage.isResizable = false
// stage.show()
// }
//
// @Throws(Exception::class)
// override fun stop() {
// super.stop() //To change body of generated methods, choose Tools | Templates.
// for (magnet in controllers) {
// magnet.stopMonitorTask()
// magnet.stopUpdateTask()
// }
// if (handler.isOpen) {
// handler.close()
// }
// System.exit(0)
// }
//
// companion object {
//
// /**
// * @param args the command line arguments
// */
// @JvmStatic
// fun main(args: Array<String>) {
// Application.launch(*args)
// }
// }
}

View File

@ -20,8 +20,6 @@ import inr.numass.control.DeviceDisplayFX
import inr.numass.control.magnet.LambdaMagnet
import javafx.application.Platform
import javafx.beans.value.ObservableValue
import javafx.event.ActionEvent
import javafx.fxml.FXML
import javafx.fxml.Initializable
import javafx.scene.control.*
import javafx.scene.layout.AnchorPane
@ -114,22 +112,43 @@ class MagnetDisplay : DeviceDisplayFX<LambdaMagnet>() {
}
}
}
valueBinding(device.updating).onChange {
val updateTaskRunning = it?.booleanValue() ?: false
runLater {
this.setButton.isSelected = updateTaskRunning
targetIField.isDisable = updateTaskRunning
}
}
valueBinding(device.monitoring).onChange {
runLater {
monitorButton.isScaleShape = it?.booleanValue() ?: false
}
}
setButton.selectedProperty().onChange {
try {
setOutput(it)
} catch (ex: PortException) {
displayError(this.device.name, null, ex)
}
}
monitorButton.selectedProperty().onChange {
if (it) {
monitoring = true
} else {
monitoring = false
this.labelU.text = "----"
}
}
}
fun setShowConfirmation(showConfirmation: Boolean) {
this.showConfirmation = showConfirmation
}
@FXML
private fun onOutToggle(event: ActionEvent) {
try {
setOutput(setButton.isSelected)
} catch (ex: PortException) {
displayError(this.device.name, null, ex)
}
}
@Throws(PortException::class)
private fun setOutput(outputOn: Boolean) {
if (outputOn) {
@ -178,17 +197,6 @@ class MagnetDisplay : DeviceDisplayFX<LambdaMagnet>() {
}
@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"
@ -198,57 +206,9 @@ class MagnetDisplay : DeviceDisplayFX<LambdaMagnet>() {
// 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) {
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

@ -1,87 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package inr.numass.control.magnet.fx
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import hep.dataforge.context.Global
import hep.dataforge.control.ports.GenericPortController
import hep.dataforge.control.ports.Port
import hep.dataforge.control.ports.PortFactory
import hep.dataforge.exceptions.PortException
import hep.dataforge.utils.DateTimeUtils
import org.slf4j.LoggerFactory
import java.io.BufferedReader
import java.io.InputStreamReader
import java.time.Duration
import java.util.*
/**
*
* @author darksnake
*/
object TestApp {
/**
* @param args the command line arguments
*/
@JvmStatic
fun main(args: Array<String>) {
MagnetControllerApp.main(arrayOf("--port=192.168.111.31:4001"))
//MagnetControllerApp.main(new String[]{"--port=192.168.111.31:4001", "--logLevel=DEBUG"});
}
}
/**
* @author Alexander Nozik
*/
object Talk {
/**
* @param args the command line arguments
*/
@Throws(Exception::class)
@JvmStatic
fun main(args: Array<String>) {
Locale.setDefault(Locale.US)
val rootLogger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as ch.qos.logback.classic.Logger
rootLogger.level = Level.INFO
var portName = "/dev/ttyr00"
if (args.size > 0) {
portName = args[0]
}
val handler: Port
handler = PortFactory.build(portName)
val controller = GenericPortController(Global,handler,"\r")
// LambdaMagnet controller = new LambdaMagnet(handler, 1);
val reader = BufferedReader(InputStreamReader(System.`in`))
System.out.printf("INPUT > ")
var nextString = reader.readLine()
while ("exit" != nextString) {
try {
val start = DateTimeUtils.now()
val answer = controller.sendAndWait(nextString + "\r", Duration.ofSeconds(1))
//String answer = controller.request(nextString);
System.out.printf("ANSWER (latency = %s): %s;%n", Duration.between(start, DateTimeUtils.now()), answer.trim { it <= ' ' })
} catch (ex: PortException) {
ex.printStackTrace()
}
System.out.printf("INPUT > ")
nextString = reader.readLine()
}
handler.close()
}
}

View File

@ -0,0 +1,63 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package inr.numass.control.magnet.test
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import hep.dataforge.context.Global
import hep.dataforge.control.ports.GenericPortController
import hep.dataforge.control.ports.Port
import hep.dataforge.control.ports.PortFactory
import hep.dataforge.exceptions.PortException
import hep.dataforge.utils.DateTimeUtils
import org.slf4j.LoggerFactory
import java.io.BufferedReader
import java.io.InputStreamReader
import java.time.Duration
import java.util.*
/**
* @param args the command line arguments
*/
fun main(args: Array<String>) {
Locale.setDefault(Locale.US)
val rootLogger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as ch.qos.logback.classic.Logger
rootLogger.level = Level.INFO
var portName = "/dev/ttyr00"
if (args.isNotEmpty()) {
portName = args[0]
}
val handler: Port
handler = PortFactory.build(portName)
val controller = GenericPortController(Global, handler, "\r")
// LambdaMagnet controller = new LambdaMagnet(handler, 1);
val reader = BufferedReader(InputStreamReader(System.`in`))
System.out.printf("INPUT > ")
var nextString = reader.readLine()
while ("exit" != nextString) {
try {
val start = DateTimeUtils.now()
val answer = controller.sendAndWait(nextString + "\r", Duration.ofSeconds(1))
//String answer = controller.request(nextString);
System.out.printf("ANSWER (latency = %s): %s;%n", Duration.between(start, DateTimeUtils.now()), answer.trim { it <= ' ' })
} catch (ex: PortException) {
ex.printStackTrace()
}
System.out.printf("INPUT > ")
nextString = reader.readLine()
}
handler.close()
}

View File

@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package inr.numass.control.magnet
package inr.numass.control.magnet.test
import hep.dataforge.context.Global
import hep.dataforge.control.ports.ComPort
import inr.numass.control.magnet.LambdaMagnet
import jssc.SerialPortException
/**
@ -34,14 +36,15 @@ object SetCurrent {
throw IllegalArgumentException("Wrong number of parameters")
}
val comName = args[0]
val lambdaaddress = Integer.valueOf(args[1])!!
val current = java.lang.Double.valueOf(args[2])
val lambdaaddress = args[1].toInt()
val current = args[2].toDouble()
val handler = ComPort(comName)
val port = ComPort.create(comName)
val controller = LambdaMagnet(handler, lambdaaddress)
val magnet = LambdaMagnet(Global, port, lambdaaddress)
magnet.target.set(current)
magnet.output.set(true)
controller.startUpdateTask(current, 500)
}
}

View File

@ -1,7 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<magnets port="virtual">
<config>
<device name = "numass.magnets">
<magnet name="SOURCE" address="1"/>
<magnet name="PINCH" address="2"/>
<magnet name="CONUS" address="3"/>
<magnet name="DETECTOR" address="4"/>
<port type="debug">
<magnet name="SOURCE" address="1" resistance="0.02"/>
<magnet name="PINCH" address="2" resistance="0.01"/>
<magnet name="CONUS" address="3" resistance="0.005"/>
<magnet name="DETECTOR" address="4" resistance="0.007"/>
</magnets>
</port>
</device>
</config>

View File

@ -16,6 +16,7 @@
package inr.numass.control.msp
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaUtils
import inr.numass.control.NumassControlApplication
import javafx.stage.Stage
@ -33,9 +34,8 @@ class MspApp : NumassControlApplication<MspDevice>() {
stage.minWidth = 600.0
}
override fun acceptDevice(meta: Meta): Boolean {
return meta.getString("name") == "msp"
override fun getDeviceMeta(config: Meta): Meta {
return MetaUtils.findNode(config,"device"){it.getString("name") == "numass.msp"}.orElseThrow{RuntimeException("Mass-spectrometer configuration not found")}
}
}

View File

@ -132,12 +132,12 @@ class MspDisplay() : DeviceDisplayFX<MspDevice>(), DeviceListener, NamedValueLis
cellFormat {
text = "Filament $it"
}
disableProperty().bind(getBooleanBinding(PortSensor.CONNECTED_STATE).not())
disableProperty().bind(booleanBinding(PortSensor.CONNECTED_STATE).not())
}
switch {
padding = Insets(5.0, 0.0, 0.0, 0.0)
disableProperty()
.bind(getBooleanBinding(PortSensor.CONNECTED_STATE))
.bind(booleanBinding(PortSensor.CONNECTED_STATE))
bindBooleanToState("filamentOn", selectedProperty())
}
deviceStateIndicator(this@MspDisplay, "filamentStatus", false) {
@ -152,13 +152,13 @@ class MspDisplay() : DeviceDisplayFX<MspDevice>(), DeviceListener, NamedValueLis
togglebutton("Measure") {
isSelected = false
disableProperty().bind(getBooleanBinding(PortSensor.CONNECTED_STATE).not())
disableProperty().bind(booleanBinding(PortSensor.CONNECTED_STATE).not())
bindBooleanToState(Sensor.MEASURING_STATE, selectedProperty())
}
togglebutton("Store") {
isSelected = false
disableProperty().bind(getBooleanBinding(Sensor.MEASURING_STATE).not())
disableProperty().bind(booleanBinding(Sensor.MEASURING_STATE).not())
bindBooleanToState("storing", selectedProperty())
}
separator(Orientation.VERTICAL)

View File

@ -1,159 +0,0 @@
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 DeviceDisplay<*>>)
/**
* Get existing view connection or create a new one
*/
fun Device.getDisplay(): DeviceDisplay<*> {
val type = (this::class.annotations.find { it is DeviceView } as DeviceView?)?.value ?: DefaultDisplay::class
return optConnection(Roles.VIEW_ROLE, DeviceDisplay::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 DeviceDisplay<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)
}
}
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 getValueBinding(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 bindState(state)
}
fun getBooleanBinding(stateName: String): BooleanBinding{
return getValueBinding(stateName).booleanBinding{it?.booleanValue()?:false}
}
/**
* Bind existing boolean property to writable device state
* @param state
* @param property
*/
protected fun bindBooleanToState(state: String, property: BooleanProperty) {
getValueBinding(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@DeviceDisplay, Device.INITIALIZED_STATE)
deviceStateIndicator(this@DeviceDisplay, PortSensor.CONNECTED_STATE)
deviceStateIndicator(this@DeviceDisplay, Sensor.MEASURING_STATE)
deviceStateIndicator(this@DeviceDisplay, "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() : DeviceDisplay<Device>() {
override fun buildView(device: Device): UIComponent? = null
}

View File

@ -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) {
tooltip(state)
if (transform != null) {
bind(connection.getValueBinding(state), transform);
bind(connection.valueBinding(state), transform);
} else {
bind(connection.getValueBinding(state)) {
bind(connection.valueBinding(state)) {
when {
it.isNull -> Color.GRAY
it.booleanValue() -> Color.GREEN
@ -117,7 +117,7 @@ fun Node.deviceStateToggle(connection: DeviceDisplayFX<*>, state: String, title:
connection.device.states[state] = newValue
}
}
connection.getValueBinding(state).onChange {
connection.valueBinding(state).onChange {
isSelected = it?.booleanValue() ?: false
}
}

View File

@ -17,24 +17,24 @@ import java.util.*
* Created by darksnake on 14-May-17.
*/
abstract class NumassControlApplication<in D : Device> : App() {
private var device: D by singleAssign()
private var device: D? = null
override fun start(stage: Stage) {
Locale.setDefault(Locale.US)// чтобы отделение десятичных знаков было точкой
val rootLogger = LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME) as ch.qos.logback.classic.Logger
rootLogger.level = Level.INFO
device = setupDevice()
val controller = device.getDisplay()
device.connect(controller, Roles.VIEW_ROLE, Roles.DEVICE_LISTENER_ROLE)
device = setupDevice().also {
val controller = it.getDisplay()
it.connect(controller, Roles.VIEW_ROLE, Roles.DEVICE_LISTENER_ROLE)
val scene = Scene(controller.view?.root ?: controller.getBoardView())
stage.scene = scene
stage.show()
setupStage(stage, device)
setupStage(stage, it)
setDFStageIcon(stage)
}
}
/**
* Get a device factory for given device
@ -45,14 +45,14 @@ abstract class NumassControlApplication<in D : Device> : App() {
protected abstract fun setupStage(stage: Stage, device: D)
protected abstract fun acceptDevice(meta: Meta): Boolean
abstract fun getDeviceMeta(config: Meta): Meta
private fun setupDevice(): D {
val config = getConfig(this).optional.orElseGet { readResourceMeta("/config/devices.xml") }
val ctx = setupContext(config)
val deviceConfig = findDeviceMeta(config) { this.acceptDevice(it) }
?: throw RuntimeException("Device configuration not found")
val deviceConfig = getDeviceMeta(config)
try {
@ -70,11 +70,10 @@ abstract class NumassControlApplication<in D : Device> : App() {
override fun stop() {
try {
device.shutdown()
device?.shutdown()
} catch (ex: Exception) {
LoggerFactory.getLogger(javaClass).error("Failed to shutdown application", ex);
} finally {
device.context.close()
super.stop()
}
}

View File

@ -16,10 +16,8 @@ import inr.numass.client.ClientUtils
import javafx.application.Application
import javafx.stage.Stage
import org.slf4j.LoggerFactory
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Paths
import java.text.ParseException
/**
* Created by darksnake on 08-May-17.
@ -59,15 +57,14 @@ fun connectStorage(device: Device, config: Meta) {
}
fun readResourceMeta(path: String): Meta {
try {
return XMLMetaReader().read(Global::class.java.getResourceAsStream(path))
} catch (e: IOException) {
throw RuntimeException(e)
} catch (e: ParseException) {
throw RuntimeException(e)
val resource = Global.io.optResource(path).nullable
if (resource != null) {
return XMLMetaReader().read(resource.stream)
} else {
throw RuntimeException("Resource $path not found")
}
}
}
fun getConfig(app: Application): Meta? {
val debugConfig = app.parameters.named["config.resource"]

View File

@ -6,6 +6,7 @@
package inr.numass.control.readvac
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaUtils
import inr.numass.control.NumassControlApplication
import javafx.stage.Stage
@ -20,8 +21,8 @@ class ReadVac : NumassControlApplication<VacCollectorDevice>() {
stage.title = "Numass vacuum measurements"
}
override fun acceptDevice(meta: Meta): Boolean {
return meta.getString("type", "") == "numass:vac"
override fun getDeviceMeta(config: Meta): Meta {
return MetaUtils.findNode(config,"device"){it.getString("name") == "numass.vac"}.orElseThrow{RuntimeException("Vacuum measurement configuration not found")}
}
}