diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DeviceBySpec.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DeviceBySpec.kt index 4a77084..7f7f478 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DeviceBySpec.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DeviceBySpec.kt @@ -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> : Device { +public open class DeviceBySpec> : Device { override var context: Context = Global internal set + public var meta: Meta = Meta.EMPTY internal set + public var properties: Map> = emptyMap() internal set + public var actions: Map> = emptyMap() internal set @@ -36,16 +42,27 @@ public open class DeviceBySpec> : Device { private val logicalState: HashMap = HashMap() - private val _propertyFlow: MutableSharedFlow>> = MutableSharedFlow() + private val _propertyFlow: MutableSharedFlow> = MutableSharedFlow() override val propertyFlow: SharedFlow> 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?) { - logicalState[propertyName] = value + 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) + } + } } /** @@ -54,10 +71,7 @@ public open class DeviceBySpec> : 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) - } + setLogicalState(propertyName, newValue) return newValue } @@ -65,7 +79,9 @@ public open class DeviceBySpec> : Device { logicalState[propertyName] ?: readProperty(propertyName) override suspend fun invalidateProperty(propertyName: String) { - logicalState.remove(propertyName) + stateLock.withLock { + logicalState.remove(propertyName) + } } override suspend fun setProperty(propertyName: String, value: MetaItem) { @@ -80,4 +96,20 @@ public open class DeviceBySpec> : Device { override suspend fun execute(action: String, argument: MetaItem?): MetaItem? = actions[action]?.executeItem(self, argument) +} + + +public operator fun , T : Any> D.get( + propertySpec: DevicePropertySpec +): Deferred = scope.async { + propertySpec.read(this@get).also { + setLogicalState(propertySpec.name, propertySpec.converter.objectToMetaItem(it)) + } +} + +public operator fun , T : Any> D.set(propertySpec: WritableDevicePropertySpec, value: T) { + scope.launch { + propertySpec.write(this@set, value) + invalidateProperty(propertySpec.name) + } } \ No newline at end of file diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DeviceSpec.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DeviceSpec.kt index fce0a81..f1cd375 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DeviceSpec.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DeviceSpec.kt @@ -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>( private val buildDevice: () -> D @@ -44,7 +46,7 @@ public abstract class DeviceSpec>( read: suspend D.() -> T, write: suspend D.(T) -> Unit ): PropertyDelegateProvider, ReadOnlyProperty, WritableDevicePropertySpec>> = - PropertyDelegateProvider { _: DeviceSpec, property -> + PropertyDelegateProvider { _: DeviceSpec, property: KProperty<*> -> val propertyName = name ?: property.name val deviceProperty = object : WritableDevicePropertySpec { override val name: String = propertyName