Add alternative device syntax
This commit is contained in:
parent
b1d3ba59bc
commit
e182af403f
@ -1,8 +1,13 @@
|
||||
package ru.mipt.npm.controls.properties
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import ru.mipt.npm.controls.api.ActionDescriptor
|
||||
import ru.mipt.npm.controls.api.Device
|
||||
import ru.mipt.npm.controls.api.PropertyDescriptor
|
||||
@ -10,19 +15,20 @@ import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaItem
|
||||
import space.kscience.dataforge.meta.TypedMetaItem
|
||||
import kotlin.jvm.Synchronized
|
||||
|
||||
/**
|
||||
* @param D recursive self-type for properties and actions
|
||||
*/
|
||||
public open class DeviceBySpec<D: DeviceBySpec<D>> : Device {
|
||||
public open class DeviceBySpec<D : DeviceBySpec<D>> : Device {
|
||||
override var context: Context = Global
|
||||
internal set
|
||||
|
||||
public var meta: Meta = Meta.EMPTY
|
||||
internal set
|
||||
|
||||
public var properties: Map<String, DevicePropertySpec<D, *>> = emptyMap()
|
||||
internal set
|
||||
|
||||
public var actions: Map<String, DeviceActionSpec<D, *, *>> = emptyMap()
|
||||
internal set
|
||||
|
||||
@ -36,17 +42,28 @@ public open class DeviceBySpec<D: DeviceBySpec<D>> : Device {
|
||||
|
||||
private val logicalState: HashMap<String, MetaItem?> = HashMap()
|
||||
|
||||
private val _propertyFlow: MutableSharedFlow<Pair<String, TypedMetaItem<*>>> = MutableSharedFlow()
|
||||
private val _propertyFlow: MutableSharedFlow<Pair<String, MetaItem>> = MutableSharedFlow()
|
||||
|
||||
override val propertyFlow: SharedFlow<Pair<String, MetaItem>> get() = _propertyFlow
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal val self: D get() = this as D
|
||||
internal val self: D
|
||||
get() = this as D
|
||||
|
||||
@Synchronized
|
||||
private fun setLogicalState(propertyName: String, value: MetaItem?) {
|
||||
internal fun getLogicalState(propertyName: String): MetaItem? = logicalState[propertyName]
|
||||
|
||||
private val stateLock = Mutex()
|
||||
|
||||
internal suspend fun setLogicalState(propertyName: String, value: MetaItem?) {
|
||||
if (value != logicalState[propertyName]) {
|
||||
stateLock.withLock {
|
||||
logicalState[propertyName] = value
|
||||
}
|
||||
if (value != null) {
|
||||
_propertyFlow.emit(propertyName to value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force read physical value and push an update if it is changed
|
||||
@ -54,10 +71,7 @@ public open class DeviceBySpec<D: DeviceBySpec<D>> : Device {
|
||||
public suspend fun readProperty(propertyName: String): MetaItem {
|
||||
val newValue = properties[propertyName]?.readItem(self)
|
||||
?: error("A property with name $propertyName is not registered in $this")
|
||||
if (newValue != logicalState[propertyName]) {
|
||||
setLogicalState(propertyName, newValue)
|
||||
_propertyFlow.emit(propertyName to newValue)
|
||||
}
|
||||
return newValue
|
||||
}
|
||||
|
||||
@ -65,8 +79,10 @@ public open class DeviceBySpec<D: DeviceBySpec<D>> : Device {
|
||||
logicalState[propertyName] ?: readProperty(propertyName)
|
||||
|
||||
override suspend fun invalidateProperty(propertyName: String) {
|
||||
stateLock.withLock {
|
||||
logicalState.remove(propertyName)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun setProperty(propertyName: String, value: MetaItem) {
|
||||
//If there is a physical property with given name, invalidate logical property and write physical one
|
||||
@ -81,3 +97,19 @@ public open class DeviceBySpec<D: DeviceBySpec<D>> : Device {
|
||||
override suspend fun execute(action: String, argument: MetaItem?): MetaItem? =
|
||||
actions[action]?.executeItem(self, argument)
|
||||
}
|
||||
|
||||
|
||||
public operator fun <D : DeviceBySpec<D>, T : Any> D.get(
|
||||
propertySpec: DevicePropertySpec<D, T>
|
||||
): Deferred<T> = scope.async {
|
||||
propertySpec.read(this@get).also {
|
||||
setLogicalState(propertySpec.name, propertySpec.converter.objectToMetaItem(it))
|
||||
}
|
||||
}
|
||||
|
||||
public operator fun <D : DeviceBySpec<D>, T : Any> D.set(propertySpec: WritableDevicePropertySpec<D, T>, value: T) {
|
||||
scope.launch {
|
||||
propertySpec.write(this@set, value)
|
||||
invalidateProperty(propertySpec.name)
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package ru.mipt.npm.controls.properties
|
||||
|
||||
import kotlinx.coroutines.Deferred
|
||||
import ru.mipt.npm.controls.api.ActionDescriptor
|
||||
import ru.mipt.npm.controls.api.PropertyDescriptor
|
||||
import space.kscience.dataforge.context.Context
|
||||
@ -8,6 +9,7 @@ import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import kotlin.properties.PropertyDelegateProvider
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
public abstract class DeviceSpec<D : DeviceBySpec<D>>(
|
||||
private val buildDevice: () -> D
|
||||
@ -44,7 +46,7 @@ public abstract class DeviceSpec<D : DeviceBySpec<D>>(
|
||||
read: suspend D.() -> T,
|
||||
write: suspend D.(T) -> Unit
|
||||
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, T>>> =
|
||||
PropertyDelegateProvider { _: DeviceSpec<D>, property ->
|
||||
PropertyDelegateProvider { _: DeviceSpec<D>, property: KProperty<*> ->
|
||||
val propertyName = name ?: property.name
|
||||
val deviceProperty = object : WritableDevicePropertySpec<D, T> {
|
||||
override val name: String = propertyName
|
||||
|
Loading…
Reference in New Issue
Block a user