Compare commits

...

3 Commits

19 changed files with 281 additions and 159 deletions

View File

@ -5,9 +5,11 @@
### Added ### Added
- Device lifecycle message - Device lifecycle message
- Low-code constructor - Low-code constructor
- Automatic description generation for spec properties (JVM only)
### Changed ### Changed
- Property caching moved from core `Device` to the `CachingDevice` - Property caching moved from core `Device` to the `CachingDevice`
- `DeviceSpec` properties no explicitly pass property name to getters and setters.
### Deprecated ### Deprecated

View File

@ -1,10 +1,7 @@
package space.kscience.controls.constructor package space.kscience.controls.constructor
import space.kscience.controls.api.Device import space.kscience.controls.api.Device
import space.kscience.controls.spec.DevicePropertySpec import space.kscience.controls.spec.*
import space.kscience.controls.spec.DeviceSpec
import space.kscience.controls.spec.MutableDevicePropertySpec
import space.kscience.controls.spec.doubleProperty
import space.kscience.dataforge.meta.transformations.MetaConverter import space.kscience.dataforge.meta.transformations.MetaConverter

View File

@ -27,6 +27,6 @@ public fun PropertyDescriptor.metaDescriptor(block: MetaDescriptorBuilder.()->Un
*/ */
@Serializable @Serializable
public class ActionDescriptor(public val name: String) { public class ActionDescriptor(public val name: String) {
public var info: String? = null public var description: String? = null
} }

View File

@ -8,9 +8,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.KMutableProperty1
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1
public object UnitMetaConverter : MetaConverter<Unit> { public object UnitMetaConverter : MetaConverter<Unit> {
override fun metaToObject(meta: Meta): Unit = Unit override fun metaToObject(meta: Meta): Unit = Unit
@ -44,72 +42,25 @@ public abstract class DeviceSpec<D : Device> {
return deviceProperty return deviceProperty
} }
public fun <T> property(
converter: MetaConverter<T>,
readOnlyProperty: KProperty1<D, T>,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<Any?, DevicePropertySpec<D, T>>> =
PropertyDelegateProvider { _, property ->
val deviceProperty = object : DevicePropertySpec<D, T> {
override val descriptor: PropertyDescriptor = PropertyDescriptor(property.name).apply {
//TODO add type from converter
mutable = true
}.apply(descriptorBuilder)
override val converter: MetaConverter<T> = converter
override suspend fun read(device: D): T = withContext(device.coroutineContext) {
readOnlyProperty.get(device)
}
}
registerProperty(deviceProperty)
ReadOnlyProperty { _, _ ->
deviceProperty
}
}
public fun <T> mutableProperty(
converter: MetaConverter<T>,
readWriteProperty: KMutableProperty1<D, T>,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<Any?, MutableDevicePropertySpec<D, T>>> =
PropertyDelegateProvider { _, property ->
val deviceProperty = object : MutableDevicePropertySpec<D, T> {
override val descriptor: PropertyDescriptor = PropertyDescriptor(property.name).apply {
//TODO add the type from converter
mutable = true
}.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): Unit = withContext(device.coroutineContext) {
readWriteProperty.set(device, value)
}
}
registerProperty(deviceProperty)
ReadOnlyProperty { _, _ ->
deviceProperty
}
}
public fun <T> property( public fun <T> property(
converter: MetaConverter<T>, converter: MetaConverter<T>,
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
read: suspend D.() -> T?, read: suspend D.(propertyName: String) -> T?,
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, T>>> = ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, T>>> =
PropertyDelegateProvider { _: DeviceSpec<D>, property -> PropertyDelegateProvider { _: DeviceSpec<D>, property ->
val propertyName = name ?: property.name val propertyName = name ?: property.name
val deviceProperty = object : DevicePropertySpec<D, T> { val deviceProperty = object : DevicePropertySpec<D, T> {
override val descriptor: PropertyDescriptor = PropertyDescriptor(propertyName).apply(descriptorBuilder)
override val descriptor: PropertyDescriptor = PropertyDescriptor(propertyName).apply {
fromSpec(property)
descriptorBuilder()
}
override val converter: MetaConverter<T> = converter override val converter: MetaConverter<T> = converter
override suspend fun read(device: D): T? = withContext(device.coroutineContext) { device.read() } override suspend fun read(device: D): T? =
withContext(device.coroutineContext) { device.read(propertyName) }
} }
registerProperty(deviceProperty) registerProperty(deviceProperty)
ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, T>> { _, _ -> ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, T>> { _, _ ->
@ -121,23 +72,29 @@ public abstract class DeviceSpec<D : Device> {
converter: MetaConverter<T>, converter: MetaConverter<T>,
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
read: suspend D.() -> T?, read: suspend D.(propertyName: String) -> T?,
write: suspend D.(T) -> Unit, write: suspend D.(propertyName: String, value: T) -> Unit,
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, T>>> = ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, T>>> =
PropertyDelegateProvider { _: DeviceSpec<D>, property: KProperty<*> -> PropertyDelegateProvider { _: DeviceSpec<D>, property: KProperty<*> ->
val propertyName = name ?: property.name val propertyName = name ?: property.name
val deviceProperty = object : MutableDevicePropertySpec<D, T> { val deviceProperty = object : MutableDevicePropertySpec<D, T> {
override val descriptor: PropertyDescriptor = PropertyDescriptor(propertyName, mutable = true) override val descriptor: PropertyDescriptor = PropertyDescriptor(
.apply(descriptorBuilder) propertyName,
mutable = true
).apply {
fromSpec(property)
descriptorBuilder()
}
override val converter: MetaConverter<T> = converter override val converter: MetaConverter<T> = converter
override suspend fun read(device: D): T? = withContext(device.coroutineContext) { device.read() } override suspend fun read(device: D): T? =
withContext(device.coroutineContext) { device.read(propertyName) }
override suspend fun write(device: D, value: T): Unit = withContext(device.coroutineContext) { override suspend fun write(device: D, value: T): Unit = withContext(device.coroutineContext) {
device.write(value) device.write(propertyName, value)
} }
} }
_properties[propertyName] = deviceProperty registerProperty(deviceProperty)
ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, T>> { _, _ -> ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, T>> { _, _ ->
deviceProperty deviceProperty
} }
@ -156,10 +113,13 @@ public abstract class DeviceSpec<D : Device> {
name: String? = null, name: String? = null,
execute: suspend D.(I) -> O, execute: suspend D.(I) -> O,
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DeviceActionSpec<D, I, O>>> = ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DeviceActionSpec<D, I, O>>> =
PropertyDelegateProvider { _: DeviceSpec<D>, property -> PropertyDelegateProvider { _: DeviceSpec<D>, property: KProperty<*> ->
val actionName = name ?: property.name val actionName = name ?: property.name
val deviceAction = object : DeviceActionSpec<D, I, O> { val deviceAction = object : DeviceActionSpec<D, I, O> {
override val descriptor: ActionDescriptor = ActionDescriptor(actionName).apply(descriptorBuilder) override val descriptor: ActionDescriptor = ActionDescriptor(actionName).apply {
fromSpec(property)
descriptorBuilder()
}
override val inputConverter: MetaConverter<I> = inputConverter override val inputConverter: MetaConverter<I> = inputConverter
override val outputConverter: MetaConverter<O> = outputConverter override val outputConverter: MetaConverter<O> = outputConverter
@ -174,68 +134,39 @@ public abstract class DeviceSpec<D : Device> {
} }
} }
/**
* An action that takes [Meta] and returns [Meta]. No conversions are done
*/
public fun metaAction(
descriptorBuilder: ActionDescriptor.() -> Unit = {},
name: String? = null,
execute: suspend D.(Meta) -> Meta,
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DeviceActionSpec<D, Meta, Meta>>> =
action(
MetaConverter.Companion.meta,
MetaConverter.Companion.meta,
descriptorBuilder,
name
) {
execute(it)
}
/**
* An action that takes no parameters and returns no values
*/
public fun unitAction(
descriptorBuilder: ActionDescriptor.() -> Unit = {},
name: String? = null,
execute: suspend D.() -> Unit,
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DeviceActionSpec<D, Unit, Unit>>> =
action(
MetaConverter.Companion.unit,
MetaConverter.Companion.unit,
descriptorBuilder,
name
) {
execute()
}
} }
/**
* An action that takes no parameters and returns no values
*/
public fun <D : Device> DeviceSpec<D>.unitAction(
descriptorBuilder: ActionDescriptor.() -> Unit = {},
name: String? = null,
execute: suspend D.() -> Unit,
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DeviceActionSpec<D, Unit, Unit>>> =
action(
MetaConverter.Companion.unit,
MetaConverter.Companion.unit,
descriptorBuilder,
name
) {
execute()
}
/** /**
* Register a mutable logical property for a device * An action that takes [Meta] and returns [Meta]. No conversions are done
*/ */
@OptIn(InternalDeviceAPI::class) public fun <D : Device> DeviceSpec<D>.metaAction(
public fun <T, D : DeviceBase<D>> DeviceSpec<D>.logicalProperty( descriptorBuilder: ActionDescriptor.() -> Unit = {},
converter: MetaConverter<T>,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<Any?, MutableDevicePropertySpec<D, T>>> = execute: suspend D.(Meta) -> Meta,
PropertyDelegateProvider { _, property -> ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DeviceActionSpec<D, Meta, Meta>>> =
val deviceProperty = object : MutableDevicePropertySpec<D, T> { action(
val propertyName = name ?: property.name MetaConverter.Companion.meta,
override val descriptor: PropertyDescriptor = PropertyDescriptor(propertyName).apply { MetaConverter.Companion.meta,
//TODO add type from converter descriptorBuilder,
mutable = true name
}.apply(descriptorBuilder) ) {
execute(it)
}
override val converter: MetaConverter<T> = converter
override suspend fun read(device: D): T? = device.getProperty(propertyName)?.let(converter::metaToObject)
override suspend fun write(device: D, value: T): Unit =
device.writeProperty(propertyName, converter.objectToMeta(value))
}
registerProperty(deviceProperty)
ReadOnlyProperty { _, _ ->
deviceProperty
}
}

View File

@ -12,7 +12,7 @@ import kotlin.time.Duration
/** /**
* Perform a recurring asynchronous read action and return a flow of results. * Perform a recurring asynchronous read action and return a flow of results.
* The flow is lazy, so action is not performed unless flow is consumed. * The flow is lazy, so action is not performed unless flow is consumed.
* The flow uses called context. In order to call it on device context, use `flowOn(coroutineContext)`. * The flow uses caller context. To call it on device context, use `flowOn(coroutineContext)`.
* *
* The flow is canceled when the device scope is canceled * The flow is canceled when the device scope is canceled
*/ */

View File

@ -0,0 +1,12 @@
package space.kscience.controls.spec
import space.kscience.controls.api.ActionDescriptor
import space.kscience.controls.api.PropertyDescriptor
import kotlin.reflect.KProperty
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FIELD)
public annotation class Description(val content: String)
internal expect fun PropertyDescriptor.fromSpec(property: KProperty<*>)
internal expect fun ActionDescriptor.fromSpec(property: KProperty<*>)

View File

@ -8,13 +8,62 @@ import space.kscience.dataforge.meta.ValueType
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.KMutableProperty1
import kotlin.reflect.KProperty1
/**
* A read-only device property that delegates reading to a device [KProperty1]
*/
public fun <T, D : Device> DeviceSpec<D>.property(
converter: MetaConverter<T>,
readOnlyProperty: KProperty1<D, T>,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, T>>> = property(
converter,
descriptorBuilder,
name = readOnlyProperty.name,
read = { readOnlyProperty.get(this) }
)
/**
* Mutable property that delegates reading and writing to a device [KMutableProperty1]
*/
public fun <T, D : Device> DeviceSpec<D>.mutableProperty(
converter: MetaConverter<T>,
readWriteProperty: KMutableProperty1<D, T>,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, T>>> =
mutableProperty(
converter,
descriptorBuilder,
readWriteProperty.name,
read = { _ -> readWriteProperty.get(this) },
write = { _, value: T -> readWriteProperty.set(this, value) }
)
/**
* Register a mutable logical property (without a corresponding physical state) for a device
*/
public fun <T, D : DeviceBase<D>> DeviceSpec<D>.logicalProperty(
converter: MetaConverter<T>,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
name: String? = null,
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, T>>> =
mutableProperty(
converter,
descriptorBuilder,
name,
read = { propertyName -> getProperty(propertyName)?.let(converter::metaToObject) },
write = { propertyName, value -> writeProperty(propertyName, converter.objectToMeta(value)) }
)
//read only delegates //read only delegates
public fun <D : Device> DeviceSpec<D>.booleanProperty( public fun <D : Device> DeviceSpec<D>.booleanProperty(
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
read: suspend D.() -> Boolean? read: suspend D.(propertyName: String) -> Boolean?
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Boolean>>> = property( ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Boolean>>> = property(
MetaConverter.boolean, MetaConverter.boolean,
{ {
@ -37,9 +86,9 @@ private inline fun numberDescriptor(
} }
public fun <D : Device> DeviceSpec<D>.numberProperty( public fun <D : Device> DeviceSpec<D>.numberProperty(
name: String? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
read: suspend D.() -> Number? name: String? = null,
read: suspend D.(propertyName: String) -> Number?
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Number>>> = property( ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Number>>> = property(
MetaConverter.number, MetaConverter.number,
numberDescriptor(descriptorBuilder), numberDescriptor(descriptorBuilder),
@ -50,7 +99,7 @@ public fun <D : Device> DeviceSpec<D>.numberProperty(
public fun <D : Device> DeviceSpec<D>.doubleProperty( public fun <D : Device> DeviceSpec<D>.doubleProperty(
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
read: suspend D.() -> Double? read: suspend D.(propertyName: String) -> Double?
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Double>>> = property( ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Double>>> = property(
MetaConverter.double, MetaConverter.double,
numberDescriptor(descriptorBuilder), numberDescriptor(descriptorBuilder),
@ -61,7 +110,7 @@ public fun <D : Device> DeviceSpec<D>.doubleProperty(
public fun <D : Device> DeviceSpec<D>.stringProperty( public fun <D : Device> DeviceSpec<D>.stringProperty(
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
read: suspend D.() -> String? read: suspend D.(propertyName: String) -> String?
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, String>>> = property( ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, String>>> = property(
MetaConverter.string, MetaConverter.string,
{ {
@ -77,7 +126,7 @@ public fun <D : Device> DeviceSpec<D>.stringProperty(
public fun <D : Device> DeviceSpec<D>.metaProperty( public fun <D : Device> DeviceSpec<D>.metaProperty(
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
read: suspend D.() -> Meta? read: suspend D.(propertyName: String) -> Meta?
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Meta>>> = property( ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Meta>>> = property(
MetaConverter.meta, MetaConverter.meta,
{ {
@ -95,8 +144,8 @@ public fun <D : Device> DeviceSpec<D>.metaProperty(
public fun <D : Device> DeviceSpec<D>.booleanProperty( public fun <D : Device> DeviceSpec<D>.booleanProperty(
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
read: suspend D.() -> Boolean?, read: suspend D.(propertyName: String) -> Boolean?,
write: suspend D.(Boolean) -> Unit write: suspend D.(propertyName: String, value: Boolean) -> Unit
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, Boolean>>> = ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, Boolean>>> =
mutableProperty( mutableProperty(
MetaConverter.boolean, MetaConverter.boolean,
@ -115,31 +164,31 @@ public fun <D : Device> DeviceSpec<D>.booleanProperty(
public fun <D : Device> DeviceSpec<D>.numberProperty( public fun <D : Device> DeviceSpec<D>.numberProperty(
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
read: suspend D.() -> Number, read: suspend D.(propertyName: String) -> Number,
write: suspend D.(Number) -> Unit write: suspend D.(propertyName: String, value: Number) -> Unit
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, Number>>> = ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, Number>>> =
mutableProperty(MetaConverter.number, numberDescriptor(descriptorBuilder), name, read, write) mutableProperty(MetaConverter.number, numberDescriptor(descriptorBuilder), name, read, write)
public fun <D : Device> DeviceSpec<D>.doubleProperty( public fun <D : Device> DeviceSpec<D>.doubleProperty(
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
read: suspend D.() -> Double, read: suspend D.(propertyName: String) -> Double,
write: suspend D.(Double) -> Unit write: suspend D.(propertyName: String, value: Double) -> Unit
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, Double>>> = ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, Double>>> =
mutableProperty(MetaConverter.double, numberDescriptor(descriptorBuilder), name, read, write) mutableProperty(MetaConverter.double, numberDescriptor(descriptorBuilder), name, read, write)
public fun <D : Device> DeviceSpec<D>.stringProperty( public fun <D : Device> DeviceSpec<D>.stringProperty(
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
read: suspend D.() -> String, read: suspend D.(propertyName: String) -> String,
write: suspend D.(String) -> Unit write: suspend D.(propertyName: String, value: String) -> Unit
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, String>>> = ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, String>>> =
mutableProperty(MetaConverter.string, descriptorBuilder, name, read, write) mutableProperty(MetaConverter.string, descriptorBuilder, name, read, write)
public fun <D : Device> DeviceSpec<D>.metaProperty( public fun <D : Device> DeviceSpec<D>.metaProperty(
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
read: suspend D.() -> Meta, read: suspend D.(propertyName: String) -> Meta,
write: suspend D.(Meta) -> Unit write: suspend D.(propertyName: String, value: Meta) -> Unit
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, Meta>>> = ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, MutableDevicePropertySpec<D, Meta>>> =
mutableProperty(MetaConverter.meta, descriptorBuilder, name, read, write) mutableProperty(MetaConverter.meta, descriptorBuilder, name, read, write)

View File

@ -0,0 +1,9 @@
package space.kscience.controls.spec
import space.kscience.controls.api.ActionDescriptor
import space.kscience.controls.api.PropertyDescriptor
import kotlin.reflect.KProperty
internal actual fun PropertyDescriptor.fromSpec(property: KProperty<*>){}
internal actual fun ActionDescriptor.fromSpec(property: KProperty<*>){}

View File

@ -0,0 +1,18 @@
package space.kscience.controls.spec
import space.kscience.controls.api.ActionDescriptor
import space.kscience.controls.api.PropertyDescriptor
import kotlin.reflect.KProperty
import kotlin.reflect.full.findAnnotation
internal actual fun PropertyDescriptor.fromSpec(property: KProperty<*>) {
property.findAnnotation<Description>()?.let {
description = it.content
}
}
internal actual fun ActionDescriptor.fromSpec(property: KProperty<*>){
property.findAnnotation<Description>()?.let {
description = it.content
}
}

View File

@ -0,0 +1,9 @@
package space.kscience.controls.spec
import space.kscience.controls.api.ActionDescriptor
import space.kscience.controls.api.PropertyDescriptor
import kotlin.reflect.KProperty
internal actual fun PropertyDescriptor.fromSpec(property: KProperty<*>) {}
internal actual fun ActionDescriptor.fromSpec(property: KProperty<*>){}

View File

@ -29,7 +29,7 @@ class OpcUaClientTest {
return DemoOpcUaDevice(config) return DemoOpcUaDevice(config)
} }
val randomDouble by doubleProperty(read = DemoOpcUaDevice::readRandomDouble) val randomDouble by doubleProperty { readRandomDouble() }
} }
@ -42,7 +42,7 @@ class OpcUaClientTest {
fun testReadDouble() = runTest { fun testReadDouble() = runTest {
val device = DemoOpcUaDevice.build() val device = DemoOpcUaDevice.build()
device.start() device.start()
println(device.read(DemoOpcUaDevice.randomDouble)) println(device.read(DemoOpcUaDevice.randomDouble))
device.stop() device.stop()
} }

View File

@ -0,0 +1,19 @@
package space.kscience.controls.vision
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass
import space.kscience.dataforge.context.PluginFactory
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionPlugin
import space.kscience.visionforge.plotly.VisionOfPlotly
public expect class ControlVisionPlugin: VisionPlugin{
public companion object: PluginFactory<ControlVisionPlugin>
}
internal val controlsVisionSerializersModule = SerializersModule {
polymorphic(Vision::class) {
subclass(VisionOfPlotly.serializer())
}
}

View File

@ -0,0 +1,20 @@
package space.kscience.controls.vision
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.node
import space.kscience.visionforge.AbstractVision
import space.kscience.visionforge.Vision
/**
* A [Vision] that shows an indicator
*/
public class IndicatorVision: AbstractVision() {
public val value: Meta? by properties.node()
}
///**
// * A [Vision] that allows both showing the value and changing it
// */
//public interface RegulatorVision: IndicatorVision{
//
//}

View File

@ -0,0 +1,33 @@
package space.kscience.controls.vision
import kotlinx.serialization.modules.SerializersModule
import org.w3c.dom.Element
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.PluginFactory
import space.kscience.dataforge.context.PluginTag
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name
import space.kscience.visionforge.ElementVisionRenderer
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionPlugin
public actual class ControlVisionPlugin : VisionPlugin(), ElementVisionRenderer {
override val tag: PluginTag get() = Companion.tag
override val visionSerializersModule: SerializersModule get() = controlsVisionSerializersModule
override fun rateVision(vision: Vision): Int {
TODO("Not yet implemented")
}
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
TODO("Not yet implemented")
}
public actual companion object : PluginFactory<ControlVisionPlugin> {
override val tag: PluginTag = PluginTag("controls.vision")
override fun build(context: Context, meta: Meta): ControlVisionPlugin = ControlVisionPlugin()
}
}

View File

@ -0,0 +1,21 @@
package space.kscience.controls.vision
import kotlinx.serialization.modules.SerializersModule
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.PluginFactory
import space.kscience.dataforge.context.PluginTag
import space.kscience.dataforge.meta.Meta
import space.kscience.visionforge.VisionPlugin
public actual class ControlVisionPlugin : VisionPlugin() {
override val tag: PluginTag get() = Companion.tag
override val visionSerializersModule: SerializersModule get() = controlsVisionSerializersModule
public actual companion object : PluginFactory<ControlVisionPlugin> {
override val tag: PluginTag = PluginTag("controls.vision")
override fun build(context: Context, meta: Meta): ControlVisionPlugin = ControlVisionPlugin()
}
}

View File

@ -16,7 +16,7 @@ import kotlin.math.sin
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
interface IDemoDevice: Device { interface IDemoDevice : Device {
var timeScaleState: Double var timeScaleState: Double
var sinScaleState: Double var sinScaleState: Double
var cosScaleState: Double var cosScaleState: Double
@ -50,8 +50,8 @@ class DemoDevice(context: Context, meta: Meta) : DeviceBySpec<IDemoDevice>(Compa
val sinScale by mutableProperty(MetaConverter.double, IDemoDevice::sinScaleState) val sinScale by mutableProperty(MetaConverter.double, IDemoDevice::sinScaleState)
val cosScale by mutableProperty(MetaConverter.double, IDemoDevice::cosScaleState) val cosScale by mutableProperty(MetaConverter.double, IDemoDevice::cosScaleState)
val sin by doubleProperty(read = IDemoDevice::sinValue) val sin by doubleProperty { sinValue() }
val cos by doubleProperty(read = IDemoDevice::cosValue) val cos by doubleProperty { cosValue() }
val coordinates by metaProperty( val coordinates by metaProperty(
descriptorBuilder = { descriptorBuilder = {

View File

@ -2,6 +2,8 @@ package space.kscience.controls.demo.car
import space.kscience.controls.api.Device import space.kscience.controls.api.Device
import space.kscience.controls.spec.DeviceSpec import space.kscience.controls.spec.DeviceSpec
import space.kscience.controls.spec.mutableProperty
import space.kscience.controls.spec.property
interface IVirtualCar : Device { interface IVirtualCar : Device {
var speedState: Vector2D var speedState: Vector2D

View File

@ -89,7 +89,7 @@ class MksPdr900Device(context: Context, meta: Meta) : DeviceBySpec<MksPdr900Devi
override fun build(context: Context, meta: Meta): MksPdr900Device = MksPdr900Device(context, meta) override fun build(context: Context, meta: Meta): MksPdr900Device = MksPdr900Device(context, meta)
val powerOn by booleanProperty(read = MksPdr900Device::readPowerOn, write = MksPdr900Device::writePowerOn) val powerOn by booleanProperty(read = { readPowerOn() }, write = { _, value -> writePowerOn(value) })
val channel by logicalProperty(MetaConverter.int) val channel by logicalProperty(MetaConverter.int)

View File

@ -157,13 +157,13 @@ class PiMotionMasterDevice(
} }
val stop by unitAction({ val stop by unitAction({
info = "Stop all axis" description = "Stop all axis"
}) { }) {
send("STP") send("STP")
} }
val connect by action(MetaConverter.meta, MetaConverter.unit, descriptorBuilder = { val connect by action(MetaConverter.meta, MetaConverter.unit, descriptorBuilder = {
info = "Connect to specific port and initialize axis" description = "Connect to specific port and initialize axis"
}) { portSpec -> }) { portSpec ->
//Clear current actions if present //Clear current actions if present
if (port != null) { if (port != null) {
@ -189,7 +189,7 @@ class PiMotionMasterDevice(
} }
val disconnect by unitAction({ val disconnect by unitAction({
info = "Disconnect the program from the device if it is connected" description = "Disconnect the program from the device if it is connected"
}) { }) {
port?.let { port?.let {
execute(stop) execute(stop)
@ -245,8 +245,8 @@ class PiMotionMasterDevice(
read = { read = {
readAxisBoolean("$command?") readAxisBoolean("$command?")
}, },
write = { write = { _, value ->
writeAxisBoolean(command, it) writeAxisBoolean(command, value)
}, },
descriptorBuilder = descriptorBuilder descriptorBuilder = descriptorBuilder
) )
@ -259,7 +259,7 @@ class PiMotionMasterDevice(
mm.requestAndParse("$command?", axisId)[axisId]?.toDoubleOrNull() mm.requestAndParse("$command?", axisId)[axisId]?.toDoubleOrNull()
?: error("Malformed $command response. Should include float value for $axisId") ?: error("Malformed $command response. Should include float value for $axisId")
}, },
write = { newValue -> write = { _, newValue ->
mm.send(command, axisId, newValue.toString()) mm.send(command, axisId, newValue.toString())
mm.failIfError() mm.failIfError()
}, },