Add alternative device syntax
This commit is contained in:
parent
e182af403f
commit
fe3958fd08
@ -12,10 +12,11 @@ import space.kscience.dataforge.misc.Type
|
||||
|
||||
|
||||
/**
|
||||
* General interface describing a managed Device
|
||||
* General interface describing a managed Device.
|
||||
* Device is a supervisor scope encompassing all operations on a device. When canceled, cancels all running processes.
|
||||
*/
|
||||
@Type(DEVICE_TARGET)
|
||||
public interface Device : Closeable, ContextAware {
|
||||
public interface Device : Closeable, ContextAware, CoroutineScope {
|
||||
/**
|
||||
* List of supported property descriptors
|
||||
*/
|
||||
@ -27,11 +28,6 @@ public interface Device : Closeable, ContextAware {
|
||||
*/
|
||||
public val actionDescriptors: Collection<ActionDescriptor>
|
||||
|
||||
/**
|
||||
* The supervisor scope encompassing all operations on a device. When canceled, cancels all running processes.
|
||||
*/
|
||||
public val scope: CoroutineScope
|
||||
|
||||
/**
|
||||
* Get the value of the property or throw error if property in not defined.
|
||||
* Suspend if property value is not available
|
||||
@ -61,7 +57,7 @@ public interface Device : Closeable, ContextAware {
|
||||
public suspend fun execute(action: String, argument: MetaItem? = null): MetaItem?
|
||||
|
||||
override fun close() {
|
||||
scope.cancel("The device is closed")
|
||||
cancel("The device is closed")
|
||||
}
|
||||
|
||||
public companion object {
|
||||
|
@ -13,6 +13,7 @@ import ru.mipt.npm.controls.api.PropertyDescriptor
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.meta.MetaItem
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
//TODO move to DataForge-core
|
||||
@DFExperimental
|
||||
@ -28,7 +29,7 @@ private open class BasicReadOnlyDeviceProperty(
|
||||
private val getter: suspend (before: MetaItem?) -> MetaItem,
|
||||
) : ReadOnlyDeviceProperty {
|
||||
|
||||
override val scope: CoroutineScope get() = device.scope
|
||||
override val scope: CoroutineScope get() = device
|
||||
|
||||
private val state: MutableStateFlow<MetaItem?> = MutableStateFlow(default)
|
||||
override val value: MetaItem? get() = state.value
|
||||
@ -107,11 +108,11 @@ private class BasicDeviceProperty(
|
||||
* Baseline implementation of [Device] interface
|
||||
*/
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
public abstract class DeviceBase(override val context: Context) : Device {
|
||||
public abstract class DeviceBase(final override val context: Context) : Device {
|
||||
|
||||
override val coroutineContext: CoroutineContext =
|
||||
context.coroutineContext + SupervisorJob(context.coroutineContext[Job])
|
||||
|
||||
override val scope: CoroutineScope by lazy {
|
||||
CoroutineScope(context.coroutineContext + Job(context.coroutineContext[Job]))
|
||||
}
|
||||
|
||||
private val _properties = HashMap<String, ReadOnlyDeviceProperty>()
|
||||
public val properties: Map<String, ReadOnlyDeviceProperty> get() = _properties
|
||||
@ -219,7 +220,7 @@ public abstract class DeviceBase(override val context: Context) : Device {
|
||||
private val block: suspend (MetaItem?) -> MetaItem?,
|
||||
) : DeviceAction {
|
||||
override suspend fun invoke(arg: MetaItem?): MetaItem? =
|
||||
withContext(scope.coroutineContext + SupervisorJob(scope.coroutineContext[Job])) {
|
||||
withContext(coroutineContext) {
|
||||
block(arg)
|
||||
}
|
||||
}
|
||||
|
@ -40,16 +40,15 @@ public class DeviceManager(override val deviceName: String = "") : AbstractPlugi
|
||||
}
|
||||
}
|
||||
|
||||
public interface DeviceSpec<D : Device> : Factory<D>
|
||||
|
||||
public fun <D : Device> DeviceManager.install(name: String, factory: DeviceSpec<D>, meta: Meta = Meta.EMPTY): D {
|
||||
public fun <D : Device> DeviceManager.install(name: String, factory: Factory<D>, meta: Meta = Meta.EMPTY): D {
|
||||
val device = factory(meta, context)
|
||||
registerDevice(NameToken(name), device)
|
||||
return device
|
||||
}
|
||||
|
||||
public fun <D : Device> DeviceManager.installing(
|
||||
factory: DeviceSpec<D>,
|
||||
factory: Factory<D>,
|
||||
metaBuilder: MetaBuilder.() -> Unit = {},
|
||||
): ReadOnlyProperty<Any?, D> = ReadOnlyProperty { _, property ->
|
||||
val name = property.name
|
||||
|
@ -1,11 +1,8 @@
|
||||
package ru.mipt.npm.controls.properties
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.*
|
||||
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
|
||||
@ -15,22 +12,28 @@ 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.transformations.MetaConverter
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.properties.Delegates.observable
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* @param D recursive self-type for properties and actions
|
||||
*/
|
||||
public open class DeviceBySpec<D : DeviceBySpec<D>> : Device {
|
||||
override var context: Context = Global
|
||||
public open class DeviceBySpec<D : DeviceBySpec<D>>(
|
||||
public val spec: DeviceSpec<D>,
|
||||
context: Context = Global,
|
||||
meta: Meta = Meta.EMPTY
|
||||
) : Device {
|
||||
override var context: Context = context
|
||||
internal set
|
||||
|
||||
public var meta: Meta = Meta.EMPTY
|
||||
public var meta: Meta = meta
|
||||
internal set
|
||||
|
||||
public var properties: Map<String, DevicePropertySpec<D, *>> = emptyMap()
|
||||
internal set
|
||||
|
||||
public var actions: Map<String, DeviceActionSpec<D, *, *>> = emptyMap()
|
||||
internal set
|
||||
public val properties: Map<String, DevicePropertySpec<D, *>> get() = spec.properties
|
||||
public val actions: Map<String, DeviceActionSpec<D, *, *>> get() = spec.actions
|
||||
|
||||
override val propertyDescriptors: Collection<PropertyDescriptor>
|
||||
get() = properties.values.map { it.descriptor }
|
||||
@ -38,7 +41,9 @@ public open class DeviceBySpec<D : DeviceBySpec<D>> : Device {
|
||||
override val actionDescriptors: Collection<ActionDescriptor>
|
||||
get() = actions.values.map { it.descriptor }
|
||||
|
||||
override val scope: CoroutineScope get() = context
|
||||
override val coroutineContext: CoroutineContext by lazy {
|
||||
context.coroutineContext + SupervisorJob(context.coroutineContext[Job])
|
||||
}
|
||||
|
||||
private val logicalState: HashMap<String, MetaItem?> = HashMap()
|
||||
|
||||
@ -54,7 +59,7 @@ public open class DeviceBySpec<D : DeviceBySpec<D>> : Device {
|
||||
|
||||
private val stateLock = Mutex()
|
||||
|
||||
internal suspend fun setLogicalState(propertyName: String, value: MetaItem?) {
|
||||
internal suspend fun updateLogical(propertyName: String, value: MetaItem?) {
|
||||
if (value != logicalState[propertyName]) {
|
||||
stateLock.withLock {
|
||||
logicalState[propertyName] = value
|
||||
@ -66,12 +71,13 @@ public open class DeviceBySpec<D : DeviceBySpec<D>> : Device {
|
||||
}
|
||||
|
||||
/**
|
||||
* Force read physical value and push an update if it is changed
|
||||
* Force read physical value and push an update if it is changed. It does not matter if logical state is present.
|
||||
* The logical state is updated after read
|
||||
*/
|
||||
public suspend fun readProperty(propertyName: String): MetaItem {
|
||||
val newValue = properties[propertyName]?.readItem(self)
|
||||
?: error("A property with name $propertyName is not registered in $this")
|
||||
setLogicalState(propertyName, newValue)
|
||||
updateLogical(propertyName, newValue)
|
||||
return newValue
|
||||
}
|
||||
|
||||
@ -84,31 +90,58 @@ public open class DeviceBySpec<D : DeviceBySpec<D>> : Device {
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun setProperty(propertyName: String, value: MetaItem) {
|
||||
override suspend fun setProperty(propertyName: String, value: MetaItem): Unit {
|
||||
//If there is a physical property with given name, invalidate logical property and write physical one
|
||||
(properties[propertyName] as? WritableDevicePropertySpec<D, out Any>)?.let {
|
||||
it.writeItem(self, value)
|
||||
invalidateProperty(propertyName)
|
||||
} ?: run {
|
||||
setLogicalState(propertyName, value)
|
||||
updateLogical(propertyName, value)
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
/**
|
||||
* A delegate that represents the logical-only state of the device
|
||||
*/
|
||||
public fun <T : Any> state(
|
||||
converter: MetaConverter<T>,
|
||||
initialValue: T,
|
||||
): ReadWriteProperty<D, T> = observable(initialValue) { property: KProperty<*>, oldValue: T, newValue: T ->
|
||||
if (oldValue != newValue) {
|
||||
launch {
|
||||
invalidateProperty(property.name)
|
||||
_propertyFlow.emit(property.name to converter.objectToMetaItem(newValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public suspend fun <T : Any> DevicePropertySpec<D, T>.read(): T = read(self)
|
||||
|
||||
override fun close() {
|
||||
with(spec){ self.onShutdown() }
|
||||
super.close()
|
||||
}
|
||||
}
|
||||
|
||||
public suspend fun <D : DeviceBySpec<D>, T : Any> D.getSuspend(
|
||||
propertySpec: DevicePropertySpec<D, T>
|
||||
): T = propertySpec.read(this@getSuspend).also {
|
||||
updateLogical(propertySpec.name, propertySpec.converter.objectToMetaItem(it))
|
||||
}
|
||||
|
||||
|
||||
public fun <D : DeviceBySpec<D>, T : Any> D.getAsync(
|
||||
propertySpec: DevicePropertySpec<D, T>
|
||||
): Deferred<T> = async {
|
||||
getSuspend(propertySpec)
|
||||
}
|
||||
|
||||
public operator fun <D : DeviceBySpec<D>, T : Any> D.set(propertySpec: WritableDevicePropertySpec<D, T>, value: T) {
|
||||
scope.launch {
|
||||
launch {
|
||||
propertySpec.write(this@set, value)
|
||||
invalidateProperty(propertySpec.name)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package ru.mipt.npm.controls.properties
|
||||
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.withContext
|
||||
import ru.mipt.npm.controls.api.ActionDescriptor
|
||||
import ru.mipt.npm.controls.api.PropertyDescriptor
|
||||
import space.kscience.dataforge.context.Context
|
||||
@ -9,13 +9,58 @@ import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import kotlin.properties.PropertyDelegateProvider
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KMutableProperty1
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
public abstract class DeviceSpec<D : DeviceBySpec<D>>(
|
||||
private val buildDevice: () -> D
|
||||
) : Factory<D> {
|
||||
private val deviceProperties = HashMap<String, DevicePropertySpec<D, *>>()
|
||||
private val deviceActions = HashMap<String, DeviceActionSpec<D, *, *>>()
|
||||
private val _properties = HashMap<String, DevicePropertySpec<D, *>>()
|
||||
public val properties: Map<String, DevicePropertySpec<D, *>> get() = _properties
|
||||
|
||||
private val _actions = HashMap<String, DeviceActionSpec<D, *, *>>()
|
||||
public val actions: Map<String, DeviceActionSpec<D, *, *>> get() = _actions
|
||||
|
||||
public fun <T : Any> registerProperty(deviceProperty: DevicePropertySpec<D, T>): DevicePropertySpec<D, T> {
|
||||
_properties[deviceProperty.name] = deviceProperty
|
||||
return deviceProperty
|
||||
}
|
||||
|
||||
public fun <T : Any> registerProperty(
|
||||
converter: MetaConverter<T>,
|
||||
readOnlyProperty: KProperty1<D, T>,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {}
|
||||
): DevicePropertySpec<D, T> {
|
||||
val deviceProperty = object : DevicePropertySpec<D, T> {
|
||||
override val name: String = readOnlyProperty.name
|
||||
override val descriptor: PropertyDescriptor = PropertyDescriptor(this.name).apply(descriptorBuilder)
|
||||
override val converter: MetaConverter<T> = converter
|
||||
override suspend fun read(device: D): T =
|
||||
withContext(device.coroutineContext) { readOnlyProperty.get(device) }
|
||||
}
|
||||
return registerProperty(deviceProperty)
|
||||
}
|
||||
|
||||
public fun <T : Any> registerProperty(
|
||||
converter: MetaConverter<T>,
|
||||
readWriteProperty: KMutableProperty1<D, T>,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {}
|
||||
): WritableDevicePropertySpec<D, T> {
|
||||
val deviceProperty = object : WritableDevicePropertySpec<D, T> {
|
||||
override val name: String = readWriteProperty.name
|
||||
override val descriptor: PropertyDescriptor = PropertyDescriptor(this.name).apply(descriptorBuilder)
|
||||
override val converter: MetaConverter<T> = converter
|
||||
override suspend fun read(device: D): T =
|
||||
withContext(device.coroutineContext) { readWriteProperty.get(device) }
|
||||
|
||||
override suspend fun write(device: D, value: T) = withContext(device.coroutineContext) {
|
||||
readWriteProperty.set(device, value)
|
||||
}
|
||||
}
|
||||
registerProperty(deviceProperty)
|
||||
return deviceProperty
|
||||
}
|
||||
|
||||
public fun <T : Any> property(
|
||||
converter: MetaConverter<T>,
|
||||
@ -30,15 +75,14 @@ public abstract class DeviceSpec<D : DeviceBySpec<D>>(
|
||||
override val descriptor: PropertyDescriptor = PropertyDescriptor(this.name).apply(descriptorBuilder)
|
||||
override val converter: MetaConverter<T> = converter
|
||||
|
||||
override suspend fun read(device: D): T = device.read()
|
||||
override suspend fun read(device: D): T = withContext(device.coroutineContext) { device.read() }
|
||||
}
|
||||
deviceProperties[propertyName] = deviceProperty
|
||||
_properties[propertyName] = deviceProperty
|
||||
ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, T>> { _, _ ->
|
||||
deviceProperty
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public fun <T : Any> property(
|
||||
converter: MetaConverter<T>,
|
||||
name: String? = null,
|
||||
@ -53,19 +97,24 @@ public abstract class DeviceSpec<D : DeviceBySpec<D>>(
|
||||
override val descriptor: PropertyDescriptor = PropertyDescriptor(this.name).apply(descriptorBuilder)
|
||||
override val converter: MetaConverter<T> = converter
|
||||
|
||||
override suspend fun read(device: D): T = device.read()
|
||||
override suspend fun read(device: D): T = withContext(device.coroutineContext) { device.read() }
|
||||
|
||||
override suspend fun write(device: D, value: T) {
|
||||
override suspend fun write(device: D, value: T) = withContext(device.coroutineContext) {
|
||||
device.write(value)
|
||||
}
|
||||
}
|
||||
deviceProperties[propertyName] = deviceProperty
|
||||
_properties[propertyName] = deviceProperty
|
||||
ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, T>> { _, _ ->
|
||||
deviceProperty
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public fun <I : Any, O : Any> registerAction(deviceAction: DeviceActionSpec<D, I, O>): DeviceActionSpec<D, I, O> {
|
||||
_actions[deviceAction.name] = deviceAction
|
||||
return deviceAction
|
||||
}
|
||||
|
||||
public fun <I : Any, O : Any> action(
|
||||
inputConverter: MetaConverter<I>,
|
||||
outputConverter: MetaConverter<O>,
|
||||
@ -82,21 +131,30 @@ public abstract class DeviceSpec<D : DeviceBySpec<D>>(
|
||||
override val inputConverter: MetaConverter<I> = inputConverter
|
||||
override val outputConverter: MetaConverter<O> = outputConverter
|
||||
|
||||
override suspend fun execute(device: D, input: I?): O? {
|
||||
return device.execute(input)
|
||||
override suspend fun execute(device: D, input: I?): O? = withContext(device.coroutineContext) {
|
||||
device.execute(input)
|
||||
}
|
||||
}
|
||||
deviceActions[actionName] = deviceAction
|
||||
_actions[actionName] = deviceAction
|
||||
ReadOnlyProperty<DeviceSpec<D>, DeviceActionSpec<D, I, O>> { _, _ ->
|
||||
deviceAction
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The function is executed right after device initialization is finished
|
||||
*/
|
||||
public open fun D.onStartup(){}
|
||||
|
||||
/**
|
||||
* The function is executed before device is shut down
|
||||
*/
|
||||
public open fun D.onShutdown(){}
|
||||
|
||||
|
||||
override fun invoke(meta: Meta, context: Context): D = buildDevice().apply {
|
||||
this.context = context
|
||||
this.meta = meta
|
||||
this.properties = deviceProperties
|
||||
this.actions = deviceActions
|
||||
onStartup()
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package ru.mipt.npm.controls.properties
|
||||
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
|
||||
public fun <D : DeviceBySpec<D>> D.state(
|
||||
initialValue: Double,
|
||||
): ReadWriteProperty<D, Double> = state(MetaConverter.double, initialValue)
|
||||
|
||||
public fun <D : DeviceBySpec<D>> D.state(
|
||||
initialValue: Number,
|
||||
): ReadWriteProperty<D, Number> = state(MetaConverter.number, initialValue)
|
@ -0,0 +1,105 @@
|
||||
package ru.mipt.npm.controls.properties
|
||||
|
||||
import ru.mipt.npm.controls.api.PropertyDescriptor
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaItem
|
||||
import space.kscience.dataforge.meta.TypedMetaItem
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import kotlin.properties.PropertyDelegateProvider
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
//read only delegates
|
||||
|
||||
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.booleanProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
read: suspend D.() -> Boolean
|
||||
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Boolean>>> =
|
||||
property(MetaConverter.boolean, name, descriptorBuilder, read)
|
||||
|
||||
|
||||
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.numberProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
read: suspend D.() -> Number
|
||||
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Number>>> =
|
||||
property(MetaConverter.number, name, descriptorBuilder, read)
|
||||
|
||||
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.doubleProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
read: suspend D.() -> Double
|
||||
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Double>>> =
|
||||
property(MetaConverter.double, name, descriptorBuilder, read)
|
||||
|
||||
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.stringProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
read: suspend D.() -> String
|
||||
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, String>>> =
|
||||
property(MetaConverter.string, name, descriptorBuilder, read)
|
||||
|
||||
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.itemProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
read: suspend D.() -> MetaItem
|
||||
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, MetaItem>>> =
|
||||
property(MetaConverter.item, name, descriptorBuilder, read)
|
||||
|
||||
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.metaProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
read: suspend D.() -> Meta
|
||||
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Meta>>> =
|
||||
property(MetaConverter.meta, name, descriptorBuilder, read)
|
||||
|
||||
//read-write delegates
|
||||
|
||||
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.booleanProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
read: suspend D.() -> Boolean,
|
||||
write: suspend D.(Boolean) -> Unit
|
||||
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, Boolean>>> =
|
||||
property(MetaConverter.boolean, name, descriptorBuilder, read, write)
|
||||
|
||||
|
||||
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.numberProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
read: suspend D.() -> Number,
|
||||
write: suspend D.(Number) -> Unit
|
||||
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, Number>>> =
|
||||
property(MetaConverter.number, name, descriptorBuilder, read, write)
|
||||
|
||||
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.doubleProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
read: suspend D.() -> Double,
|
||||
write: suspend D.(Double) -> Unit
|
||||
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, Double>>> =
|
||||
property(MetaConverter.double, name, descriptorBuilder, read, write)
|
||||
|
||||
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.stringProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
read: suspend D.() -> String,
|
||||
write: suspend D.(String) -> Unit
|
||||
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, String>>> =
|
||||
property(MetaConverter.string, name, descriptorBuilder, read, write)
|
||||
|
||||
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.itemProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
read: suspend D.() -> MetaItem,
|
||||
write: suspend D.(MetaItem) -> Unit
|
||||
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, TypedMetaItem<*>>>> =
|
||||
property(MetaConverter.item, name, descriptorBuilder, read, write)
|
||||
|
||||
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.metaProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
read: suspend D.() -> Meta,
|
||||
write: suspend D.(Meta) -> Unit
|
||||
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, Meta>>> =
|
||||
property(MetaConverter.meta, name, descriptorBuilder, read, write)
|
@ -0,0 +1,13 @@
|
||||
package ru.mipt.npm.controls.properties
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import ru.mipt.npm.controls.api.PropertyDescriptor
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import kotlin.reflect.KFunction
|
||||
|
||||
/**
|
||||
* Blocking property get call
|
||||
*/
|
||||
public operator fun <D : DeviceBySpec<D>, T : Any> D.get(
|
||||
propertySpec: DevicePropertySpec<D, T>
|
||||
): T = runBlocking { getAsync(propertySpec).await() }
|
@ -95,9 +95,9 @@ class DemoControllerView : View(title = " Demo controller remote") {
|
||||
useMaxWidth = true
|
||||
action {
|
||||
controller.device?.apply {
|
||||
timeScaleValue = timeScaleSlider.value
|
||||
sinScaleValue = xScaleSlider.value
|
||||
cosScaleValue = yScaleSlider.value
|
||||
timeScale = timeScaleSlider.value
|
||||
sinScale = xScaleSlider.value
|
||||
cosScale = yScaleSlider.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,73 +1,59 @@
|
||||
package ru.mipt.npm.controls.demo
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import ru.mipt.npm.controls.base.*
|
||||
import ru.mipt.npm.controls.controllers.DeviceSpec
|
||||
import ru.mipt.npm.controls.controllers.double
|
||||
import space.kscience.dataforge.context.Context
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.mipt.npm.controls.properties.*
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.values.asValue
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import java.time.Instant
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.ExperimentalTime
|
||||
|
||||
@OptIn(ExperimentalTime::class)
|
||||
class DemoDevice(context: Context) : DeviceBase(context) {
|
||||
|
||||
private val executor = Executors.newSingleThreadExecutor()
|
||||
class DemoDevice : DeviceBySpec<DemoDevice>(DemoDevice) {
|
||||
var timeScale by state(5000.0)
|
||||
var sinScale by state( 1.0)
|
||||
var cosScale by state(1.0)
|
||||
|
||||
override val scope: CoroutineScope = CoroutineScope(
|
||||
context.coroutineContext + executor.asCoroutineDispatcher() + Job(context.coroutineContext[Job])
|
||||
)
|
||||
companion object : DeviceSpec<DemoDevice>(::DemoDevice) {
|
||||
// register virtual properties based on actual object state
|
||||
val timeScaleProperty = registerProperty(MetaConverter.double, DemoDevice::timeScale)
|
||||
val sinScaleProperty = registerProperty(MetaConverter.double, DemoDevice::sinScale)
|
||||
val cosScaleProperty = registerProperty(MetaConverter.double, DemoDevice::cosScale)
|
||||
|
||||
val timeScale: DeviceProperty by writingVirtual(5000.0.asValue())
|
||||
var timeScaleValue by timeScale.double()
|
||||
|
||||
val sinScale by writingVirtual(1.0.asValue())
|
||||
var sinScaleValue by sinScale.double()
|
||||
val sin: TypedReadOnlyDeviceProperty<Number> by readingNumber {
|
||||
val sin by doubleProperty {
|
||||
val time = Instant.now()
|
||||
sin(time.toEpochMilli().toDouble() / timeScaleValue) * sinScaleValue
|
||||
kotlin.math.sin(time.toEpochMilli().toDouble() / timeScale) * sinScale
|
||||
}
|
||||
|
||||
val cosScale by writingVirtual(1.0.asValue())
|
||||
var cosScaleValue by cosScale.double()
|
||||
val cos by readingNumber {
|
||||
val cos by doubleProperty {
|
||||
val time = Instant.now()
|
||||
cos(time.toEpochMilli().toDouble() / timeScaleValue) * cosScaleValue
|
||||
kotlin.math.cos(time.toEpochMilli().toDouble() / timeScale) * sinScale
|
||||
}
|
||||
|
||||
val coordinates by readingMeta {
|
||||
val coordinates by metaProperty {
|
||||
Meta {
|
||||
val time = Instant.now()
|
||||
"time" put time.toEpochMilli()
|
||||
"x" put sin(time.toEpochMilli().toDouble() / timeScaleValue) * sinScaleValue
|
||||
"y" put cos(time.toEpochMilli().toDouble() / timeScaleValue) * cosScaleValue
|
||||
"x" put getSuspend(sin)
|
||||
"y" put getSuspend(cos)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val resetScale: DeviceAction by acting {
|
||||
timeScaleValue = 5000.0
|
||||
sinScaleValue = 1.0
|
||||
cosScaleValue = 1.0
|
||||
val resetScale by action(MetaConverter.meta, MetaConverter.meta) {
|
||||
timeScale = 5000.0
|
||||
sinScale = 1.0
|
||||
cosScale = 1.0
|
||||
null
|
||||
}
|
||||
|
||||
init {
|
||||
sin.readEvery(Duration.seconds(0.2))
|
||||
cos.readEvery(Duration.seconds(0.2))
|
||||
coordinates.readEvery(Duration.seconds(0.3))
|
||||
override fun DemoDevice.onStartup() {
|
||||
launch {
|
||||
while(isActive){
|
||||
delay(50)
|
||||
sin.read()
|
||||
cos.read()
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
super.close()
|
||||
executor.shutdown()
|
||||
}
|
||||
|
||||
companion object : DeviceSpec<DemoDevice> {
|
||||
override fun invoke(meta: Meta, context: Context): DemoDevice = DemoDevice(context)
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ import kotlinx.coroutines.sync.withLock
|
||||
import ru.mipt.npm.controls.api.DeviceHub
|
||||
import ru.mipt.npm.controls.api.PropertyDescriptor
|
||||
import ru.mipt.npm.controls.base.*
|
||||
import ru.mipt.npm.controls.controllers.DeviceSpec
|
||||
import ru.mipt.npm.controls.controllers.duration
|
||||
import ru.mipt.npm.controls.ports.*
|
||||
import space.kscience.dataforge.context.*
|
||||
@ -28,10 +27,6 @@ class PiMotionMasterDevice(
|
||||
private val portFactory: PortFactory = KtorTcpPort,
|
||||
) : DeviceBase(context), DeviceHub {
|
||||
|
||||
override val scope: CoroutineScope = CoroutineScope(
|
||||
context.coroutineContext + SupervisorJob(context.coroutineContext[Job])
|
||||
)
|
||||
|
||||
private var port: Port? = null
|
||||
//TODO make proxy work
|
||||
//PortProxy { portFactory(address ?: error("The device is not connected"), context) }
|
||||
@ -151,11 +146,10 @@ class PiMotionMasterDevice(
|
||||
withTimeout(timeoutValue) {
|
||||
sendCommandInternal(command, *arguments)
|
||||
val phrases = port?.receiving()?.withDelimiter("\n") ?: error("Not connected to device")
|
||||
val list = phrases.transformWhile { line ->
|
||||
phrases.transformWhile { line ->
|
||||
emit(line)
|
||||
line.endsWith(" \n")
|
||||
}.toList()
|
||||
list
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
logger.warn { "Error during PIMotionMaster request. Requesting error code." }
|
||||
@ -204,7 +198,6 @@ class PiMotionMasterDevice(
|
||||
)
|
||||
|
||||
inner class Axis(val axisId: String) : DeviceBase(context) {
|
||||
override val scope: CoroutineScope get() = this@PiMotionMasterDevice.scope
|
||||
|
||||
private suspend fun readAxisBoolean(command: String): Boolean =
|
||||
requestAndParse(command, axisId)[axisId]?.toIntOrNull()
|
||||
@ -343,7 +336,7 @@ class PiMotionMasterDevice(
|
||||
}
|
||||
}
|
||||
|
||||
companion object : DeviceSpec<PiMotionMasterDevice> {
|
||||
companion object : Factory<PiMotionMasterDevice> {
|
||||
override fun invoke(meta: Meta, context: Context): PiMotionMasterDevice = PiMotionMasterDevice(context)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user