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 package inr.numass.control.cryotemp
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaUtils
import inr.numass.control.NumassControlApplication import inr.numass.control.NumassControlApplication
import javafx.stage.Stage import javafx.stage.Stage
@ -32,9 +33,7 @@ class PKT8App : NumassControlApplication<PKT8Device>() {
stage.minWidth = 400.0 stage.minWidth = 400.0
} }
override fun acceptDevice(meta: Meta): Boolean { override fun getDeviceMeta(config: Meta): Meta {
return meta.getString("type") == "PKT8" 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 //send start signal
it.send("s") it.send("s")
notifyMeasurementState(MeasurementState.IN_PROGRESS) notifyMeasurementState(MeasurementState.IN_PROGRESS)
} ?: notifyError("Not connected") }
} }

View File

@ -3,7 +3,7 @@ apply plugin: 'application'
version = "0.3.0" version = "0.3.0"
if (!hasProperty('mainClass')) { if (!hasProperty('mainClass')) {
ext.mainClass = 'inr.numass.control.magnet.fx.MagnetControllerApp' ext.mainClass = 'inr.numass.control.magnet.fx.MagnetApp'
} }
mainClassName = mainClass mainClassName = mainClass
@ -22,3 +22,11 @@ task talkToServer(type: JavaExec) {
standardInput = System.in standardInput = System.in
standardOutput = System.out 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 { init {
meta.useEachMeta("magnet") { meta.useEachMeta("magnet") {
magnets.add(LambdaMagnet(context, it, controller)) magnets.add(LambdaMagnet(controller, it))
}
meta.useEachMeta("bind") {
TODO("add binding")
} }
} }
private fun buildPort(): Port{ private fun buildPort(): Port {
val portName = meta.getString("port"); val portMeta = meta.getMetaOrEmpty("port")
return if(portName.startsWith("virtual")){ return if(portMeta.getString("type") == "debug"){
VirtualLambdaPort(meta) VirtualLambdaPort(portMeta)
} else{ } 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.description.ValueDef
import hep.dataforge.exceptions.ControlException import hep.dataforge.exceptions.ControlException
import hep.dataforge.exceptions.PortException import hep.dataforge.exceptions.PortException
import hep.dataforge.kodex.buildMeta
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.states.StateDef import hep.dataforge.states.StateDef
import hep.dataforge.states.StateDefs import hep.dataforge.states.StateDefs
@ -29,9 +30,6 @@ import hep.dataforge.states.valueState
import hep.dataforge.utils.DateTimeUtils import hep.dataforge.utils.DateTimeUtils
import hep.dataforge.values.ValueType.* import hep.dataforge.values.ValueType.*
import kotlinx.coroutines.experimental.runBlocking import kotlinx.coroutines.experimental.runBlocking
import org.slf4j.LoggerFactory
import java.text.DecimalFormat
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
@ -41,7 +39,7 @@ import java.util.concurrent.TimeUnit
/** /**
* @author Polina * @author Polina
*/ */
@ValueDef(name = "timeout", type = [NUMBER], def = "400", info = "A timeout for port response")
@StateDefs( @StateDefs(
StateDef(value = ValueDef(name = "current", type = arrayOf(NUMBER), def = "0", info = "Current current")), StateDef(value = ValueDef(name = "current", type = arrayOf(NUMBER), def = "0", info = "Current current")),
StateDef(value = ValueDef(name = "voltage", type = arrayOf(NUMBER), def = "0", info = "Current voltage")), StateDef(value = ValueDef(name = "voltage", type = arrayOf(NUMBER), def = "0", info = "Current voltage")),
@ -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 = "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 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 private var closePortOnShutDown = false
@ -67,25 +66,23 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
//var listener: MagnetStateListener? = null //var listener: MagnetStateListener? = null
// private volatile double current = 0; // private volatile double current = 0;
private val timeout: Duration = meta.optString("timeout").map<Duration> { Duration.parse(it) }.orElse(Duration.ofMillis(200))
private var monitorTask: Future<*>? = null private var monitorTask: Future<*>? = null
private var updateTask: Future<*>? = null private var updateTask: Future<*>? = null
var lastUpdate by valueState("lastUpdate", getter = {0}).timeDelegate var lastUpdate by valueState("lastUpdate", getter = { 0 }).timeDelegate
private set private set
// read-only values of current output // read-only values of current output
val current = valueState("current", getter = { controller.talk(address, timeout) { s2d(getParameter("MC")) } }) val current = valueState("current", getter = { s2d(controller.getParameter(address, "MC")) })
// val current by current.double
val voltage = valueState("voltage", getter = { controller.talk(address, timeout) { s2d(getParameter("MV")) } }) val voltage = valueState("voltage", getter = { s2d(controller.getParameter(address, "MV")) })
// val voltage by voltage.double
var target = valueState("target") var target = valueState("target")
//output values of current and voltage //output values of current and voltage
private var outCurrent by valueState("outCurrent", getter = { controller.talk(address, timeout) { s2d(getParameter("PC")) } }) { _, value -> private var outCurrent by valueState("outCurrent", getter = { s2d(controller.getParameter(address, "PC")) }) { _, value ->
if (setParameter("PC", value.doubleValue())) { if (controller.setParameter(address, "PC", value.doubleValue())) {
lastUpdate = DateTimeUtils.now() lastUpdate = DateTimeUtils.now()
} else { } else {
notifyError("Can't set the target current") notifyError("Can't set the target current")
@ -93,18 +90,21 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
return@valueState value return@valueState value
}.doubleDelegate }.doubleDelegate
private val outVoltage = valueState("outVoltage", getter = { controller.talk(address, timeout) { s2d(getParameter("PV")) } }) { _, value -> private val outVoltage = valueState("outVoltage", getter = { s2d(controller.getParameter(address, "PV")) }) { _, value ->
if (!setParameter("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
} }
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()) setOutputMode(value.booleanValue())
if (!value.booleanValue()) {
state = MagnetState.OFF
}
} }
var monitoring =valueState("monitoring", getter = { monitorTask != null }) { _, value -> var monitoring = valueState("monitoring", getter = { monitorTask != null }) { _, value ->
if (value.booleanValue()) { if (value.booleanValue()) {
startMonitorTask() startMonitorTask()
} else { } else {
@ -113,6 +113,9 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
return@valueState value return@valueState value
} }
/**
*
*/
var updating = valueState("updating", getter = { updateTask != null }) { _, value -> var updating = valueState("updating", getter = { updateTask != null }) { _, value ->
if (value.booleanValue()) { if (value.booleanValue()) {
startUpdateTask() 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 * @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 * 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
*/ */
@Throws(ControlException::class) @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 closePortOnShutDown = true
} }
constructor(context: Context, port: Port, address: Int) : this(LambdaPortController(context, port), buildMeta { "address" to address })
@Throws(ControlException::class) @Throws(ControlException::class)
override fun init() { override fun init() {
super.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 * Extract number from LAMBDA response
* *
@ -232,10 +206,15 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
try { try {
val measuredI = current.doubleValue val measuredI = current.doubleValue
val targetI = target.doubleValue val targetI = target.doubleValue
updateState("current",measuredI) updateState("current", measuredI)
if (Math.abs(measuredI - targetI) > CURRENT_PRECISION) { if (Math.abs(measuredI - targetI) > CURRENT_PRECISION) {
val nextI = nextI(measuredI, targetI) val nextI = nextI(measuredI, targetI)
if (bound(nextI)) {
outCurrent = nextI outCurrent = nextI
state = MagnetState.OK
} else {
state = MagnetState.BOUND
}
} else { } else {
stopUpdateTask() stopUpdateTask()
} }
@ -253,7 +232,7 @@ open class LambdaMagnet(context: Context, meta: Meta, private val controller: La
@Throws(PortException::class) @Throws(PortException::class)
private fun setOutputMode(out: Boolean) { private fun setOutputMode(out: Boolean) {
val outState: Int = if (out) 1 else 0 val outState: Int = if (out) 1 else 0
if (!setParameter("OUT", outState)) { if (!controller.setParameter(address, "OUT", outState)) {
notifyError("Can't set output mode") notifyError("Can't set output mode")
} else { } else {
updateState("output", out) 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 { private fun nextI(measuredI: Double, targetI: Double): Double {
// assert(measuredI != target)
var step = if (lastUpdate == Instant.EPOCH) { var step = if (lastUpdate == Instant.EPOCH) {
MIN_UP_STEP_SIZE 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 { companion object {
private val LAMBDA_FORMAT = DecimalFormat("###.##")
const val CURRENT_PRECISION = 0.05 const val CURRENT_PRECISION = 0.05
const val DEFAULT_DELAY = 1 const val DEFAULT_DELAY = 1
const val DEFAULT_MONITOR_DELAY = 2000 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 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
/**
* 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.context.Context
import hep.dataforge.control.ports.GenericPortController import hep.dataforge.control.ports.GenericPortController
import hep.dataforge.control.ports.Port import hep.dataforge.control.ports.Port
import hep.dataforge.exceptions.PortException
import org.slf4j.LoggerFactory
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")
class LambdaPortController(context: Context, port: Port) : GenericPortController(context, port) { class LambdaPortController(context: Context, port: Port) : GenericPortController(context, port) {
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 fun setAddress(address: Int, timeout: Duration) { private fun setAddress(address: Int, timeout: Duration) {
val response = sendAndWait("ADR $address\r", timeout) { true }.trim() val response = sendAndWait("ADR $address\r", timeout) { true }.trim()
if (response == "OK") { 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 * 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) { synchronized(this) {
setAddress(address, timeout) 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 package inr.numass.control.magnet
import hep.dataforge.control.ports.VirtualPort import hep.dataforge.control.ports.VirtualPort
import hep.dataforge.exceptions.PortException import hep.dataforge.kodex.useEachMeta
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.time.Duration import java.time.Duration
@ -30,29 +30,17 @@ class VirtualLambdaPort(meta: Meta) : VirtualPort(meta) {
@Volatile private var currentAddress = -1 @Volatile private var currentAddress = -1
private val magnets = HashMap<Int, VirtualMagnetStatus>() private val magnets = HashMap<Int, VirtualMagnetStatus>()
private val virtualPortName: String = meta.getString("name", "virtual::numass.lambda") override val name: 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))
// }
// }
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.put(num, VirtualMagnetStatus(resistance)) magnets[num] = VirtualMagnetStatus(resistance)
} }
} }
override fun toString(): String = virtualPortName override fun toString(): String = name
override fun evaluateRequest(request: String) { override fun evaluateRequest(request: String) {
val command: String val command: String
@ -67,8 +55,7 @@ class VirtualLambdaPort(meta: Meta) : VirtualPort(meta) {
try { try {
evaluateRequest(command.trim { it <= ' ' }, value.trim { it <= ' ' }) evaluateRequest(command.trim { it <= ' ' }, value.trim { it <= ' ' })
} catch (ex: RuntimeException) { } catch (ex: RuntimeException) {
receive("FAIL".toByteArray())//TODO какая команда правильная?
receivePhrase("FAIL")//TODO какая команда правильная?
LoggerFactory.getLogger(javaClass).error("Request evaluation failure", ex) LoggerFactory.getLogger(javaClass).error("Request evaluation failure", ex)
} }
@ -125,14 +112,14 @@ class VirtualLambdaPort(meta: Meta) : VirtualPort(meta) {
return return
} }
"PV?" -> { "PV?" -> {
planResponse(java.lang.Double.toString(currentMagnet().getVoltage()), latency) planResponse(java.lang.Double.toString(currentMagnet().voltage), latency)
return return
} }
"MV?" -> { "MV?" -> {
planResponse(java.lang.Double.toString(currentMagnet().getVoltage()), latency) planResponse(java.lang.Double.toString(currentMagnet().voltage), latency)
return 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]!! 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, private 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) {
fun getVoltage() = current * resistance val voltage get() = current * resistance
} }
companion object { companion object {
private val latency = Duration.ofMillis(50) 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 package inr.numass.control.magnet.fx
import ch.qos.logback.classic.Level import tornadofx.*
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.*
/** /**
* *
* @author Alexander Nozik * @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") { // private lateinit var device: LambdaHub
// handler = VirtualLambdaPort("COM12", 1, 2, 3, 4) //
// } else { // @Throws(IOException::class, ControlException::class)
// handler = PortFactory.getPort(portName) // override fun start(stage: Stage) {
// //TODO add meta reader here // 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) //// val portName = (parameters.named as java.util.Map<String, String>).getOrDefault("port", "virtual")
// sourceController.speed = 4.0 ////
// controllers.add(pinchController) //// if (portName == "virtual") {
// controllers.add(conusController) //// handler = VirtualLambdaPort("COM12", 1, 2, 3, 4)
// controllers.add(detectorController) //// } else {
//// handler = PortFactory.getPort(portName)
//// //TODO add meta reader here
val showConfirmation = java.lang.Boolean.parseBoolean((parameters.named as java.util.Map<String, String>).getOrDefault("confirmOut", "false")) //// }
////
val vbox = VBox(5.0) //// sourceController = SafeLambdaMagnet("SOURCE", handler, 1)
var height = 0.0 //// pinchController = SafeLambdaMagnet("PINCH", handler, 2)
var width = 0.0 //// conusController = SafeLambdaMagnet("CONUS", handler, 3)
for (controller in controllers) { //// detectorController = SafeLambdaMagnet("DETECTOR", handler, 4)
val comp = MagnetControllerComponent.build(controller) ////
width = Math.max(width, comp.prefWidth) //// conusController.bindTo(pinchController, 30.0)
height += comp.prefHeight + 5 ////
if (!showConfirmation) { //// controllers.add(sourceController)
comp.setShowConfirmation(showConfirmation) //// sourceController.speed = 4.0
} //// controllers.add(pinchController)
vbox.children.add(comp) //// controllers.add(conusController)
} //// controllers.add(detectorController)
//
val scene = Scene(vbox, width, height) //
// val showConfirmation = java.lang.Boolean.parseBoolean((parameters.named as java.util.Map<String, String>).getOrDefault("confirmOut", "false"))
//
stage.title = "Numass magnet view" // val vbox = VBox(5.0)
stage.scene = scene // var height = 0.0
stage.isResizable = false // var width = 0.0
stage.show() // for (controller in controllers) {
} // val comp = MagnetControllerComponent.build(controller)
// width = Math.max(width, comp.prefWidth)
@Throws(Exception::class) // height += comp.prefHeight + 5
override fun stop() { // if (!showConfirmation) {
super.stop() //To change body of generated methods, choose Tools | Templates. // comp.setShowConfirmation(showConfirmation)
for (magnet in controllers) { // }
magnet.stopMonitorTask() // vbox.children.add(comp)
magnet.stopUpdateTask() // }
} //
if (handler.isOpen) { // val scene = Scene(vbox, width, height)
handler.close() //
} //
System.exit(0) // stage.title = "Numass magnet view"
} // stage.scene = scene
// stage.isResizable = false
companion object { // stage.show()
// }
/** //
* @param args the command line arguments // @Throws(Exception::class)
*/ // override fun stop() {
@JvmStatic // super.stop() //To change body of generated methods, choose Tools | Templates.
fun main(args: Array<String>) { // for (magnet in controllers) {
Application.launch(*args) // 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 inr.numass.control.magnet.LambdaMagnet
import javafx.application.Platform import javafx.application.Platform
import javafx.beans.value.ObservableValue import javafx.beans.value.ObservableValue
import javafx.event.ActionEvent
import javafx.fxml.FXML
import javafx.fxml.Initializable import javafx.fxml.Initializable
import javafx.scene.control.* import javafx.scene.control.*
import javafx.scene.layout.AnchorPane import javafx.scene.layout.AnchorPane
@ -40,9 +38,9 @@ class MagnetDisplay : DeviceDisplayFX<LambdaMagnet>() {
return MagnetControllerComponent(device) return MagnetControllerComponent(device)
} }
val current by lazy { valueBinding(device.voltage)} val current by lazy { valueBinding(device.voltage) }
val voltage by lazy { valueBinding(device.current)} val voltage by lazy { valueBinding(device.current) }
var target by device.target.doubleDelegate var target by device.target.doubleDelegate
@ -103,7 +101,7 @@ class MagnetDisplay : DeviceDisplayFX<LambdaMagnet>() {
} }
} }
valueBinding(device.output).onChange{ valueBinding(device.output).onChange {
Platform.runLater { Platform.runLater {
if (it?.booleanValue() == true) { if (it?.booleanValue() == true) {
this.statusLabel.text = "OK" this.statusLabel.text = "OK"
@ -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) { fun setShowConfirmation(showConfirmation: Boolean) {
this.showConfirmation = showConfirmation 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) @Throws(PortException::class)
private fun setOutput(outputOn: Boolean) { private fun setOutput(outputOn: Boolean) {
if (outputOn) { 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) { fun displayError(name: String, errorMessage: String?, throwable: Throwable) {
Platform.runLater { Platform.runLater {
this.statusLabel.text = "ERROR" 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. // MagnetStateListener.super.error(address, errorMessage, throwable); //To change body of generated methods, choose Tools | Templates.
} }
// /** fun displayState(state: String) {
// * @param lambdaMagnet the device to set
// */
// private fun setLambdaMagnet(lambdaMagnet: LambdaMagnet) {
// this.device = lambdaMagnet
// lambdaMagnet.listener = this
// magnetName.text = lambdaMagnet.name
//
// magnetSpeedField.text = java.lang.Double.toString(this.device.speed)
//
// }
override fun updateTaskStateChanged(name: String, updateTaskRunning: Boolean) {
this.setButton.isSelected = updateTaskRunning
targetIField.isDisable = updateTaskRunning
}
override fun monitorTaskStateChanged(name: String, monitorTaskRunning: Boolean) {
this.monitorButton.isScaleShape = monitorTaskRunning
}
// /**
// * @param logger the logger to set
// */
// public void setLogger(PrintStream logger) {
// this.logger = logger;
// }
override fun displayState(state: String) {
Platform.runLater { this.statusLabel.text = state } 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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 hep.dataforge.control.ports.ComPort
import inr.numass.control.magnet.LambdaMagnet
import jssc.SerialPortException import jssc.SerialPortException
/** /**
@ -34,14 +36,15 @@ object SetCurrent {
throw IllegalArgumentException("Wrong number of parameters") throw IllegalArgumentException("Wrong number of parameters")
} }
val comName = args[0] val comName = args[0]
val lambdaaddress = Integer.valueOf(args[1])!! val lambdaaddress = args[1].toInt()
val current = java.lang.Double.valueOf(args[2]) 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" ?> <?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="SOURCE" address="1" resistance="0.02"/>
<magnet name="PINCH" address="2" resistance="0.01"/> <magnet name="PINCH" address="2" resistance="0.01"/>
<magnet name="CONUS" address="3" resistance="0.005"/> <magnet name="CONUS" address="3" resistance="0.005"/>
<magnet name="DETECTOR" address="4" resistance="0.007"/> <magnet name="DETECTOR" address="4" resistance="0.007"/>
</magnets> </port>
</device>
</config>

View File

@ -16,6 +16,7 @@
package inr.numass.control.msp package inr.numass.control.msp
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaUtils
import inr.numass.control.NumassControlApplication import inr.numass.control.NumassControlApplication
import javafx.stage.Stage import javafx.stage.Stage
@ -33,9 +34,8 @@ class MspApp : NumassControlApplication<MspDevice>() {
stage.minWidth = 600.0 stage.minWidth = 600.0
} }
override fun acceptDevice(meta: Meta): Boolean { override fun getDeviceMeta(config: Meta): Meta {
return meta.getString("name") == "msp" 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 { cellFormat {
text = "Filament $it" text = "Filament $it"
} }
disableProperty().bind(getBooleanBinding(PortSensor.CONNECTED_STATE).not()) disableProperty().bind(booleanBinding(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(getBooleanBinding(PortSensor.CONNECTED_STATE)) .bind(booleanBinding(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(getBooleanBinding(PortSensor.CONNECTED_STATE).not()) disableProperty().bind(booleanBinding(PortSensor.CONNECTED_STATE).not())
bindBooleanToState(Sensor.MEASURING_STATE, selectedProperty()) bindBooleanToState(Sensor.MEASURING_STATE, selectedProperty())
} }
togglebutton("Store") { togglebutton("Store") {
isSelected = false isSelected = false
disableProperty().bind(getBooleanBinding(Sensor.MEASURING_STATE).not()) disableProperty().bind(booleanBinding(Sensor.MEASURING_STATE).not())
bindBooleanToState("storing", selectedProperty()) bindBooleanToState("storing", selectedProperty())
} }
separator(Orientation.VERTICAL) 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) { fun Indicator.bind(connection: DeviceDisplayFX<*>, state: String, transform: ((Value) -> Paint)? = null) {
tooltip(state) tooltip(state)
if (transform != null) { if (transform != null) {
bind(connection.getValueBinding(state), transform); bind(connection.valueBinding(state), transform);
} else { } else {
bind(connection.getValueBinding(state)) { bind(connection.valueBinding(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.getValueBinding(state).onChange { connection.valueBinding(state).onChange {
isSelected = it?.booleanValue() ?: false isSelected = it?.booleanValue() ?: false
} }
} }

View File

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

View File

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

View File

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