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 package ru.mipt.npm.controls.properties
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow 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.ActionDescriptor
import ru.mipt.npm.controls.api.Device import ru.mipt.npm.controls.api.Device
import ru.mipt.npm.controls.api.PropertyDescriptor 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.context.Global
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaItem 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 * @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 override var context: Context = Global
internal set internal set
public var meta: Meta = Meta.EMPTY public var meta: Meta = Meta.EMPTY
internal set internal set
public var properties: Map<String, DevicePropertySpec<D, *>> = emptyMap() public var properties: Map<String, DevicePropertySpec<D, *>> = emptyMap()
internal set internal set
public var actions: Map<String, DeviceActionSpec<D, *, *>> = emptyMap() public var actions: Map<String, DeviceActionSpec<D, *, *>> = emptyMap()
internal set internal set
@ -36,17 +42,28 @@ public open class DeviceBySpec<D: DeviceBySpec<D>> : Device {
private val logicalState: HashMap<String, MetaItem?> = HashMap() 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 override val propertyFlow: SharedFlow<Pair<String, MetaItem>> get() = _propertyFlow
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
internal val self: D get() = this as D internal val self: D
get() = this as D
@Synchronized internal fun getLogicalState(propertyName: String): MetaItem? = logicalState[propertyName]
private fun setLogicalState(propertyName: String, value: MetaItem?) {
private val stateLock = Mutex()
internal suspend fun setLogicalState(propertyName: String, value: MetaItem?) {
if (value != logicalState[propertyName]) {
stateLock.withLock {
logicalState[propertyName] = value logicalState[propertyName] = value
} }
if (value != null) {
_propertyFlow.emit(propertyName to value)
}
}
}
/** /**
* Force read physical value and push an update if it is changed * 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 { public suspend fun readProperty(propertyName: String): MetaItem {
val newValue = properties[propertyName]?.readItem(self) val newValue = properties[propertyName]?.readItem(self)
?: error("A property with name $propertyName is not registered in $this") ?: error("A property with name $propertyName is not registered in $this")
if (newValue != logicalState[propertyName]) {
setLogicalState(propertyName, newValue) setLogicalState(propertyName, newValue)
_propertyFlow.emit(propertyName to newValue)
}
return newValue return newValue
} }
@ -65,8 +79,10 @@ public open class DeviceBySpec<D: DeviceBySpec<D>> : Device {
logicalState[propertyName] ?: readProperty(propertyName) logicalState[propertyName] ?: readProperty(propertyName)
override suspend fun invalidateProperty(propertyName: String) { override suspend fun invalidateProperty(propertyName: String) {
stateLock.withLock {
logicalState.remove(propertyName) logicalState.remove(propertyName)
} }
}
override suspend fun setProperty(propertyName: String, value: MetaItem) { override suspend fun setProperty(propertyName: String, value: MetaItem) {
//If there is a physical property with given name, invalidate logical property and write physical one //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? = override suspend fun execute(action: String, argument: MetaItem?): MetaItem? =
actions[action]?.executeItem(self, argument) 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 package ru.mipt.npm.controls.properties
import kotlinx.coroutines.Deferred
import ru.mipt.npm.controls.api.ActionDescriptor import ru.mipt.npm.controls.api.ActionDescriptor
import ru.mipt.npm.controls.api.PropertyDescriptor import ru.mipt.npm.controls.api.PropertyDescriptor
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
@ -8,6 +9,7 @@ import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.transformations.MetaConverter import space.kscience.dataforge.meta.transformations.MetaConverter
import kotlin.properties.PropertyDelegateProvider import kotlin.properties.PropertyDelegateProvider
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
public abstract class DeviceSpec<D : DeviceBySpec<D>>( public abstract class DeviceSpec<D : DeviceBySpec<D>>(
private val buildDevice: () -> D private val buildDevice: () -> D
@ -44,7 +46,7 @@ public abstract class DeviceSpec<D : DeviceBySpec<D>>(
read: suspend D.() -> T, read: suspend D.() -> T,
write: suspend D.(T) -> Unit write: suspend D.(T) -> Unit
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, T>>> = ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, T>>> =
PropertyDelegateProvider { _: DeviceSpec<D>, property -> PropertyDelegateProvider { _: DeviceSpec<D>, property: KProperty<*> ->
val propertyName = name ?: property.name val propertyName = name ?: property.name
val deviceProperty = object : WritableDevicePropertySpec<D, T> { val deviceProperty = object : WritableDevicePropertySpec<D, T> {
override val name: String = propertyName override val name: String = propertyName