Updates to control using coroutines

This commit is contained in:
Alexander Nozik 2018-04-01 11:05:39 +03:00
parent 30f1805513
commit 5ada5e76bc
12 changed files with 100 additions and 151 deletions

View File

@ -21,7 +21,6 @@ import hep.dataforge.context.Context
import hep.dataforge.control.collectors.RegularPointCollector
import hep.dataforge.control.connections.Roles
import hep.dataforge.control.devices.PortSensor
import hep.dataforge.control.devices.stringState
import hep.dataforge.control.ports.GenericPortController
import hep.dataforge.control.ports.Port
import hep.dataforge.control.ports.PortFactory
@ -30,6 +29,7 @@ import hep.dataforge.exceptions.ControlException
import hep.dataforge.exceptions.StorageException
import hep.dataforge.meta.Meta
import hep.dataforge.states.StateDef
import hep.dataforge.states.valueState
import hep.dataforge.storage.api.TableLoader
import hep.dataforge.storage.commons.LoaderFactory
import hep.dataforge.storage.commons.StorageConnection
@ -79,11 +79,11 @@ class PKT8Device(context: Context, meta: Meta) : PortSensor(context, meta) {
tableFormatBuilder.build()
}
val sps: String by stringState(SPS)
val sps: String by valueState(SPS).string
val pga: String by stringState(PGA)
val pga: String by valueState(PGA).string
val abuf: String by stringState(ABUF)
val abuf: String by valueState(ABUF).string
private val duration = Duration.parse(meta.getString("averagingDuration", "PT30S"))
@ -225,7 +225,7 @@ class PKT8Device(context: Context, meta: Meta) : PortSensor(context, meta) {
}
private val collector = RegularPointCollector(duration) {
notifyResult(produceResult(it))
notifyResult(it)
storageHelper?.push(it)
}
@ -245,7 +245,7 @@ class PKT8Device(context: Context, meta: Meta) : PortSensor(context, meta) {
}
}
override fun setMeasurement(oldMeta: Meta?, newMeta: Meta) {
override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) {
if (oldMeta != null) {
stopMeasurement()
}
@ -257,7 +257,7 @@ class PKT8Device(context: Context, meta: Meta) : PortSensor(context, meta) {
logger.info("Starting measurement")
connection?.also {
connection.also {
it.onPhrase("[Ss]topped\\s*", this) {
notifyMeasurementState(MeasurementState.STOPPED)
}
@ -289,8 +289,8 @@ class PKT8Device(context: Context, meta: Meta) : PortSensor(context, meta) {
} catch (ex: Exception) {
notifyError("Failed to stop measurement", ex)
} finally {
connection?.removeErrorListener(this)
connection?.removePhraseListener(this)
connection.removeErrorListener(this)
connection.removePhraseListener(this)
collector.stop()
logger.debug("Collector stopped")
}

View File

@ -22,8 +22,6 @@ import hep.dataforge.context.Context
import hep.dataforge.control.collectors.RegularPointCollector
import hep.dataforge.control.connections.Roles
import hep.dataforge.control.devices.PortSensor
import hep.dataforge.control.devices.booleanState
import hep.dataforge.control.devices.doubleState
import hep.dataforge.control.ports.GenericPortController
import hep.dataforge.control.ports.Port
import hep.dataforge.control.ports.PortFactory
@ -34,10 +32,10 @@ import hep.dataforge.exceptions.PortException
import hep.dataforge.meta.Meta
import hep.dataforge.states.StateDef
import hep.dataforge.states.StateDefs
import hep.dataforge.states.valueState
import hep.dataforge.storage.commons.StorageConnection
import hep.dataforge.tables.TableFormatBuilder
import hep.dataforge.tables.ValuesListener
import hep.dataforge.values.Value
import hep.dataforge.values.ValueType
import inr.numass.control.DeviceView
import inr.numass.control.NumassStorageConnection
@ -64,20 +62,28 @@ class MspDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
// private var measurementDelegate: Consumer<MspResponse>? = null
val selected: Boolean by booleanState()
val selected: Boolean by valueState("selected").boolean
var controlled: Boolean by booleanState()
var controlled: Boolean by valueState("controlled") { _, value ->
control(value.booleanValue())
}.boolean
var filamentOn: Boolean by booleanState()
var filament by valueState("filament") { old, value ->
selectFilament(value.intValue())
}
val peakJumpZero: Double by doubleState("peakJump.zero")
var filamentOn: Boolean by valueState("filamentOn") { _, value ->
setFilamentOn(value.booleanValue())
}.boolean
var peakJumpZero: Double by valueState("peakJump.zero").double
private val averagingDuration: Duration = Duration.parse(meta.getString("averagingDuration", "PT30S"))
private var storageHelper: NumassStorageConnection? = null
private val collector = RegularPointCollector(averagingDuration) { res ->
notifyResult(produceResult(res))
notifyResult(res)
forEachConnection(ValuesListener::class.java) {
it.accept(res)
}
@ -109,27 +115,17 @@ class MspDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
super.shutdown()
}
//TODO make actual request
override fun computeState(stateName: String): Any = when (stateName) {
"controlled" -> false
"filament" -> 1
"filamentOn" -> false//Always return false on first request
"filamentStatus" -> "UNKNOWN"
else -> super.computeState(stateName)
}
// //TODO make actual request
// override fun computeState(stateName: String): Any = when (stateName) {
// "controlled" -> false
// "filament" -> 1
// "filamentOn" -> false//Always return false on first request
// "filamentStatus" -> "UNKNOWN"
// else -> super.computeState(stateName)
// }
override fun getType(): String = MSP_DEVICE_TYPE
@Throws(ControlException::class)
override fun requestStateChange(stateName: String, value: Value) {
when (stateName) {
"controlled" -> control(value.booleanValue())
"filament" -> selectFilament(value.intValue())
"filamentOn" -> setFilamentOn(value.booleanValue())
else -> super.requestStateChange(stateName, value)
}
}
/**
* Startup MSP: get available sensors, select sensor and control.
*
@ -314,8 +310,8 @@ class MspDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
}
}
override fun setMeasurement(oldMeta: Meta?, newMeta: Meta) {
if(oldMeta!= null){
override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) {
if (oldMeta != null) {
stopMeasurement()
}
if (newMeta.getString("type", "peakJump") == "peakJump") {
@ -352,7 +348,7 @@ class MspDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
throw ControlException("Can't create measurement for msp")
}
storageHelper = NumassStorageConnection("msp"){builder.build()}
storageHelper = NumassStorageConnection("msp") { builder.build() }
connect(storageHelper)
connection.onAnyPhrase(this) {
@ -400,7 +396,7 @@ class MspDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
collector.stop()
val stop = commandAndWait("ScanStop").isOK
//Reset loaders in connections
storageHelper?.let { disconnect(it)}
storageHelper?.let { disconnect(it) }
notifyMeasurementState(MeasurementState.STOPPED)
}

View File

@ -6,10 +6,11 @@ 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.meta.Meta
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
@ -48,8 +49,7 @@ fun Device.getDisplay(): DeviceDisplay<*> {
*/
abstract class DeviceDisplay<D : Device> : Component(), Connection, DeviceListener {
private val bindings = HashMap<String, ObjectBinding<Value>>()
private val metaBindings = HashMap<String, ObjectBinding<Meta>>()
private val bindings = HashMap<String, ObjectBinding<*>>()
private val deviceProperty = SimpleObjectProperty<D>(this, "device", null)
val device: D by deviceProperty
@ -81,76 +81,47 @@ abstract class DeviceDisplay<D : Device> : Component(), Connection, DeviceListen
abstract fun buildView(device: D): UIComponent?;
/**
* Get binding for a given device state
* @param state
* *
* @return
* Create a binding for specific state and register it in update listener
*/
fun getStateBinding(state: String): ObjectBinding<Value> {
return bindings.computeIfAbsent(state) { stateName ->
object : ObjectBinding<Value>() {
override fun computeValue(): Value {
return if (isOpen) {
device.getState(stateName)
} else {
Value.NULL
}
}
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 getMetaStateBinding(state: String): ObjectBinding<Meta> {
return metaBindings.computeIfAbsent(state) { stateName ->
object : ObjectBinding<Meta>() {
override fun computeValue(): Meta {
return if (isOpen) {
device.getMetaState(stateName)
} else {
Meta.empty()
}
}
}
}
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 getBooleanStateBinding(state: String): BooleanBinding =
getStateBinding(state).booleanBinding { it?.booleanValue() ?: false }
/**
* Bind existing boolean property to writable device state
* @param state
* *
* @param property
*/
protected fun bindBooleanToState(state: String, property: BooleanProperty) {
getStateBinding(state).addListener { _, oldValue, newValue ->
if (isOpen && oldValue !== newValue) {
getValueBinding(state).addListener { _, oldValue, newValue ->
if (isOpen && oldValue != newValue) {
runLater { property.value = newValue.booleanValue() }
}
}
property.addListener { _, oldValue, newValue ->
if (isOpen && oldValue != newValue) {
runAsync {
if (!device.isInitialized) {
device.init()
}
device.setState(state, newValue)
}
device.states[state] = newValue
}
}
}
override fun notifyStateChanged(device: Device, name: String, state: Value) {
override fun notifyStateChanged(device: Device, name: String, state: Any) {
bindings[name]?.invalidate()
}
override fun notifyMetaStateChanged(device: Device, name: String, state: Meta) {
metaBindings[name]?.invalidate()
}
open fun getBoardView(): Parent {
return HBox().apply {
alignment = Pos.CENTER_LEFT

View File

@ -77,15 +77,13 @@ fun EventTarget.indicator(radius: Double = 10.0, op: (Indicator.() -> Unit) = {}
fun Indicator.bind(connection: DeviceDisplay<*>, state: String, transform: ((Value) -> Paint)? = null) {
tooltip(state)
if (transform != null) {
bind(connection.getStateBinding(state), transform);
bind(connection.getValueBinding(state), transform);
} else {
bind(connection.getStateBinding(state)) {
if (it.isNull) {
Color.GRAY
} else if (it.booleanValue()) {
Color.GREEN;
} else {
Color.RED;
bind(connection.getValueBinding(state)) {
when {
it.isNull -> Color.GRAY
it.booleanValue() -> Color.GREEN
else -> Color.RED
}
}
}
@ -95,7 +93,7 @@ fun Indicator.bind(connection: DeviceDisplay<*>, state: String, transform: ((Val
* State name + indicator
*/
fun EventTarget.deviceStateIndicator(connection: DeviceDisplay<*>, state: String, showName: Boolean = true, transform: ((Value) -> Paint)? = null) {
if (connection.device.hasState(state)) {
if (connection.device.stateNames.contains(state)) {
if (showName) {
text("${state.toUpperCase()}: ")
}
@ -112,16 +110,16 @@ fun EventTarget.deviceStateIndicator(connection: DeviceDisplay<*>, state: String
* A togglebutton + indicator for boolean state
*/
fun Node.deviceStateToggle(connection: DeviceDisplay<*>, state: String, title: String = state) {
if (connection.device.hasState(state)) {
if (connection.device.stateNames.contains(state)) {
togglebutton(title) {
isSelected = false
selectedProperty().addListener { _, oldValue, newValue ->
if (oldValue != newValue) {
connection.device.setState(state, newValue)
connection.device.states[state] = newValue
}
}
connection.getBooleanStateBinding(state).onChange {
isSelected = it
connection.getValueBinding(state).onChange {
isSelected = it?.booleanValue() ?: false
}
}
deviceStateIndicator(connection, state, false)

View File

@ -20,7 +20,7 @@ class NumassStorageConnection(private val loaderName: String? = null, private va
@Synchronized
override fun accept(point: Values) {
if (device.optBooleanState("storing").nullable == true) {
if (device.states.optBoolean("storing").nullable == true) {
val format = formatBuilder(device)
val suffix = DateTimeUtils.fileSuffix()
val loaderName = "${loaderName ?: device.name}_$suffix"

View File

@ -16,7 +16,7 @@ class StorageHelper(private val device: AbstractDevice, private val loaderFactor
private val loaderMap = HashMap<StorageConnection, TableLoader>()
fun push(point: Values) {
if (device.optBooleanState("storing").nullable == true) {
if (device.states.optBoolean("storing").nullable == true) {
device.forEachConnection("storage", StorageConnection::class.java) { connection ->
try {
val pl = loaderMap.computeIfAbsent(connection, loaderFactory)

View File

@ -32,8 +32,8 @@ class CM32Device(context: Context, meta: Meta) : PortSensor(context, meta) {
}
override fun setMeasurement(oldMeta: Meta?, newMeta: Meta) {
startMeasurement {
override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) {
measurement {
doMeasure()
}
}

View File

@ -7,7 +7,6 @@ package inr.numass.control.readvac
import hep.dataforge.context.Context
import hep.dataforge.control.devices.PortSensor
import hep.dataforge.control.devices.intState
import hep.dataforge.control.ports.GenericPortController
import hep.dataforge.control.ports.Port
import hep.dataforge.control.ports.PortFactory
@ -37,8 +36,8 @@ class MKSBaratronDevice(context: Context, meta: Meta) : PortSensor(context, meta
return GenericPortController(context, port) { it.endsWith("\r") }
}
override fun setMeasurement(oldMeta: Meta?, newMeta: Meta) {
startMeasurement {
override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) {
measurement {
doMeasure()
}
}

View File

@ -7,7 +7,6 @@ package inr.numass.control.readvac
import hep.dataforge.context.Context
import hep.dataforge.control.devices.PortSensor
import hep.dataforge.control.devices.booleanState
import hep.dataforge.control.ports.GenericPortController
import hep.dataforge.control.ports.Port
import hep.dataforge.control.ports.PortFactory
@ -17,7 +16,7 @@ import hep.dataforge.exceptions.ControlException
import hep.dataforge.meta.Meta
import hep.dataforge.states.StateDef
import hep.dataforge.states.StateDefs
import hep.dataforge.values.Value
import hep.dataforge.states.valueState
import hep.dataforge.values.ValueType.BOOLEAN
import inr.numass.control.DeviceView
import java.lang.Double.parseDouble
@ -40,7 +39,11 @@ class MKSVacDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
private val deviceAddress: String = meta.getString("address", "253")
var power: Boolean by booleanState("power")
var power by valueState("power", getter = { talk("FP?") == "ON" }) { old, value ->
if (old != value) {
setPowerOn(value.booleanValue())
}
}.boolean
@Throws(ControlException::class)
@ -61,25 +64,11 @@ class MKSVacDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
return GenericPortController(context, port) { it.endsWith(";FF") }
}
@Throws(ControlException::class)
override fun computeState(stateName: String): Any = when (stateName) {
"power" -> talk("FP?") == "ON"
else -> super.computeState(stateName)
}
@Throws(ControlException::class)
override fun requestStateChange(stateName: String, value: Value) {
when (stateName) {
"power" -> setPowerOn(value.booleanValue())
else -> super.requestStateChange(stateName, value)
}
}
@Throws(ControlException::class)
override fun shutdown() {
if (connected) {
setState("power", false)
power = false
}
super.shutdown()
}
@ -110,27 +99,24 @@ class MKSVacDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
override fun getType(): String = meta.getString("type", "numass.vac.mks")
override fun setMeasurement(oldMeta: Meta?, newMeta: Meta) {
startMeasurement {
doMeasure()
override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) {
measurement {
// if (getState("power").booleanValue()) {
val channel = meta.getInt("channel", 5)
val answer = talk("PR$channel?")
if (answer == null || answer.isEmpty()) {
updateState(PortSensor.CONNECTED_STATE, false)
notifyError("No connection")
}
val res = parseDouble(answer)
if (res <= 0) {
updateState("power", false)
notifyError("No power")
} else {
message = "OK"
notifyResult(res)
}
}
}
private fun doMeasure(): Meta {
// if (getState("power").booleanValue()) {
val channel = meta.getInt("channel", 5)
val answer = talk("PR$channel?")
if (answer == null || answer.isEmpty()) {
updateState(PortSensor.CONNECTED_STATE, false)
return produceError("No connection")
}
val res = parseDouble(answer)
return if (res <= 0) {
updateState("power", false)
produceError("No power")
} else {
this.updateMessage("OK")
produceResult(res)
}
}
}

View File

@ -7,7 +7,6 @@ package inr.numass.control.readvac
import hep.dataforge.context.Context
import hep.dataforge.control.devices.PortSensor
import hep.dataforge.control.devices.intState
import hep.dataforge.control.ports.GenericPortController
import hep.dataforge.control.ports.Port
import hep.dataforge.control.ports.PortFactory
@ -39,8 +38,8 @@ class MeradatVacDevice(context: Context, meta: Meta) : PortSensor(context, meta)
return meta.getString("type", "numass.vac.vit")
}
override fun setMeasurement(oldMeta: Meta?, newMeta: Meta) {
startMeasurement{
override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) {
measurement{
doMeasure()
}
}

View File

@ -124,7 +124,7 @@ class VacCollectorDevice(context: Context, meta: Meta, val sensors: Collection<S
}
}
override fun setMeasurement(oldMeta: Meta?, newMeta: Meta) {
override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) {
oldMeta?.let {
stopMeasurement()
}

View File

@ -114,8 +114,8 @@ val deviceInterceptor = InterceptorFactory { context, meta ->
add("type", device.type)
add("getMeta", device.meta.asJson())
add("state", jsonObject {
for (state in device.listStates()) {
add(state, device.getState(state).toString())
device.states.forEach {
add(it.name,it.toString())
}
})
}