Add alternative device syntax

This commit is contained in:
Alexander Nozik 2021-06-24 10:41:42 +03:00
parent b1d3ba59bc
commit e182af403f
2 changed files with 48 additions and 14 deletions

View File

@ -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)
}
}

View File

@ -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