Refactor delegated properties
This commit is contained in:
parent
4b9f535002
commit
eb3121aed4
@ -1,10 +1,14 @@
|
|||||||
package hep.dataforge.control.base
|
package hep.dataforge.control.base
|
||||||
|
|
||||||
import hep.dataforge.control.api.ActionDescriptor
|
import hep.dataforge.control.api.ActionDescriptor
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.meta.MetaItem
|
import hep.dataforge.meta.MetaItem
|
||||||
|
import hep.dataforge.meta.asMetaItem
|
||||||
|
|
||||||
public interface DeviceAction {
|
public interface DeviceAction {
|
||||||
public val name: String
|
public val name: String
|
||||||
public val descriptor: ActionDescriptor
|
public val descriptor: ActionDescriptor
|
||||||
public suspend operator fun invoke(arg: MetaItem<*>? = null): MetaItem<*>?
|
public suspend operator fun invoke(arg: MetaItem<*>? = null): MetaItem<*>?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public suspend operator fun DeviceAction.invoke(meta: Meta): MetaItem<*>? = invoke(meta.asMetaItem())
|
@ -0,0 +1,54 @@
|
|||||||
|
package hep.dataforge.control.base
|
||||||
|
|
||||||
|
import hep.dataforge.meta.MetaItem
|
||||||
|
import hep.dataforge.meta.transformations.MetaConverter
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A type-safe wrapper on top of read-only property
|
||||||
|
*/
|
||||||
|
public open class TypedReadOnlyDeviceProperty<T : Any>(
|
||||||
|
private val property: ReadOnlyDeviceProperty,
|
||||||
|
protected val converter: MetaConverter<T>,
|
||||||
|
) : ReadOnlyDeviceProperty by property {
|
||||||
|
|
||||||
|
public fun updateLogical(obj: T) {
|
||||||
|
property.updateLogical(converter.objectToMetaItem(obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
public open val typedValue: T? get() = value?.let { converter.itemToObject(it) }
|
||||||
|
|
||||||
|
public suspend fun readTyped(force: Boolean = false): T = converter.itemToObject(read(force))
|
||||||
|
|
||||||
|
public fun flowTyped(): Flow<T?> = flow().map { it?.let { converter.itemToObject(it) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A type-safe wrapper for a read-write device property
|
||||||
|
*/
|
||||||
|
public class TypedDeviceProperty<T : Any>(
|
||||||
|
private val property: DeviceProperty,
|
||||||
|
converter: MetaConverter<T>,
|
||||||
|
) : TypedReadOnlyDeviceProperty<T>(property, converter), DeviceProperty {
|
||||||
|
|
||||||
|
// override var value: MetaItem<*>?
|
||||||
|
// get() = property.value
|
||||||
|
// set(arg) {
|
||||||
|
// property.value = arg
|
||||||
|
// }
|
||||||
|
|
||||||
|
public override var typedValue: T?
|
||||||
|
get() = value?.let { converter.itemToObject(it) }
|
||||||
|
set(arg) {
|
||||||
|
property.value = arg?.let { converter.objectToMetaItem(arg) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun write(item: MetaItem<*>) {
|
||||||
|
property.write(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
public suspend fun write(obj: T) {
|
||||||
|
property.write(converter.objectToMetaItem(obj))
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ package hep.dataforge.control.base
|
|||||||
|
|
||||||
import hep.dataforge.control.api.PropertyDescriptor
|
import hep.dataforge.control.api.PropertyDescriptor
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
|
import hep.dataforge.meta.transformations.MetaConverter
|
||||||
import hep.dataforge.values.Null
|
import hep.dataforge.values.Null
|
||||||
import hep.dataforge.values.Value
|
import hep.dataforge.values.Value
|
||||||
import hep.dataforge.values.asValue
|
import hep.dataforge.values.asValue
|
||||||
@ -9,13 +10,22 @@ import kotlin.properties.PropertyDelegateProvider
|
|||||||
import kotlin.properties.ReadOnlyProperty
|
import kotlin.properties.ReadOnlyProperty
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
private fun <D : DeviceBase> D.provideProperty(): ReadOnlyProperty<D, ReadOnlyDeviceProperty> =
|
private fun <D : DeviceBase> D.provideProperty(name: String): ReadOnlyProperty<D, ReadOnlyDeviceProperty> =
|
||||||
ReadOnlyProperty { _: D, property: KProperty<*> ->
|
ReadOnlyProperty { _: D, _: KProperty<*> ->
|
||||||
val name = property.name
|
return@ReadOnlyProperty properties.getValue(name)
|
||||||
return@ReadOnlyProperty properties[name]!!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun <D : DeviceBase, T : Any> D.provideProperty(
|
||||||
|
name: String,
|
||||||
|
converter: MetaConverter<T>,
|
||||||
|
): ReadOnlyProperty<D, TypedReadOnlyDeviceProperty<T>> =
|
||||||
|
ReadOnlyProperty { _: D, _: KProperty<*> ->
|
||||||
|
return@ReadOnlyProperty TypedReadOnlyDeviceProperty(properties.getValue(name), converter)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public typealias ReadOnlyPropertyDelegate = ReadOnlyProperty<DeviceBase, ReadOnlyDeviceProperty>
|
public typealias ReadOnlyPropertyDelegate = ReadOnlyProperty<DeviceBase, ReadOnlyDeviceProperty>
|
||||||
|
public typealias TypedReadOnlyPropertyDelegate<T> = ReadOnlyProperty<DeviceBase, TypedReadOnlyDeviceProperty<T>>
|
||||||
|
|
||||||
private class ReadOnlyDevicePropertyProvider<D : DeviceBase>(
|
private class ReadOnlyDevicePropertyProvider<D : DeviceBase>(
|
||||||
val owner: D,
|
val owner: D,
|
||||||
@ -27,7 +37,22 @@ private class ReadOnlyDevicePropertyProvider<D : DeviceBase>(
|
|||||||
override operator fun provideDelegate(thisRef: D, property: KProperty<*>): ReadOnlyPropertyDelegate {
|
override operator fun provideDelegate(thisRef: D, property: KProperty<*>): ReadOnlyPropertyDelegate {
|
||||||
val name = property.name
|
val name = property.name
|
||||||
owner.newReadOnlyProperty(name, default, descriptorBuilder, getter)
|
owner.newReadOnlyProperty(name, default, descriptorBuilder, getter)
|
||||||
return owner.provideProperty()
|
return owner.provideProperty(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TypedReadOnlyDevicePropertyProvider<D : DeviceBase, T : Any>(
|
||||||
|
val owner: D,
|
||||||
|
val default: MetaItem<*>?,
|
||||||
|
val converter: MetaConverter<T>,
|
||||||
|
val descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
|
private val getter: suspend (MetaItem<*>?) -> MetaItem<*>,
|
||||||
|
) : PropertyDelegateProvider<D, TypedReadOnlyPropertyDelegate<T>> {
|
||||||
|
|
||||||
|
override operator fun provideDelegate(thisRef: D, property: KProperty<*>): TypedReadOnlyPropertyDelegate<T> {
|
||||||
|
val name = property.name
|
||||||
|
owner.newReadOnlyProperty(name, default, descriptorBuilder, getter)
|
||||||
|
return owner.provideProperty(name, converter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,9 +82,10 @@ public fun DeviceBase.readingNumber(
|
|||||||
default: Number? = null,
|
default: Number? = null,
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
getter: suspend () -> Number,
|
getter: suspend () -> Number,
|
||||||
): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider(
|
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Number>> = TypedReadOnlyDevicePropertyProvider(
|
||||||
this,
|
this,
|
||||||
default?.let { MetaItem.ValueItem(it.asValue()) },
|
default?.let { MetaItem.ValueItem(it.asValue()) },
|
||||||
|
MetaConverter.number,
|
||||||
descriptorBuilder,
|
descriptorBuilder,
|
||||||
getter = {
|
getter = {
|
||||||
val number = getter()
|
val number = getter()
|
||||||
@ -71,9 +97,10 @@ public fun DeviceBase.readingString(
|
|||||||
default: String? = null,
|
default: String? = null,
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
getter: suspend () -> String,
|
getter: suspend () -> String,
|
||||||
): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider(
|
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<String>> = TypedReadOnlyDevicePropertyProvider(
|
||||||
this,
|
this,
|
||||||
default?.let { MetaItem.ValueItem(it.asValue()) },
|
default?.let { MetaItem.ValueItem(it.asValue()) },
|
||||||
|
MetaConverter.string,
|
||||||
descriptorBuilder,
|
descriptorBuilder,
|
||||||
getter = {
|
getter = {
|
||||||
val number = getter()
|
val number = getter()
|
||||||
@ -85,9 +112,10 @@ public fun DeviceBase.readingBoolean(
|
|||||||
default: Boolean? = null,
|
default: Boolean? = null,
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
getter: suspend () -> Boolean,
|
getter: suspend () -> Boolean,
|
||||||
): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider(
|
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Boolean>> = TypedReadOnlyDevicePropertyProvider(
|
||||||
this,
|
this,
|
||||||
default?.let { MetaItem.ValueItem(it.asValue()) },
|
default?.let { MetaItem.ValueItem(it.asValue()) },
|
||||||
|
MetaConverter.boolean,
|
||||||
descriptorBuilder,
|
descriptorBuilder,
|
||||||
getter = {
|
getter = {
|
||||||
val boolean = getter()
|
val boolean = getter()
|
||||||
@ -99,22 +127,31 @@ public fun DeviceBase.readingMeta(
|
|||||||
default: Meta? = null,
|
default: Meta? = null,
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
getter: suspend MetaBuilder.() -> Unit,
|
getter: suspend MetaBuilder.() -> Unit,
|
||||||
): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider(
|
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Meta>> = TypedReadOnlyDevicePropertyProvider(
|
||||||
this,
|
this,
|
||||||
default?.let { MetaItem.NodeItem(it) },
|
default?.let { MetaItem.NodeItem(it) },
|
||||||
|
MetaConverter.meta,
|
||||||
descriptorBuilder,
|
descriptorBuilder,
|
||||||
getter = {
|
getter = {
|
||||||
MetaItem.NodeItem(MetaBuilder().apply { getter() })
|
MetaItem.NodeItem(MetaBuilder().apply { getter() })
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun DeviceBase.provideMutableProperty(): ReadOnlyProperty<DeviceBase, DeviceProperty> =
|
private fun DeviceBase.provideMutableProperty(name: String): ReadOnlyProperty<DeviceBase, DeviceProperty> =
|
||||||
ReadOnlyProperty { _: DeviceBase, property: KProperty<*> ->
|
ReadOnlyProperty { _: DeviceBase, _: KProperty<*> ->
|
||||||
val name = property.name
|
|
||||||
return@ReadOnlyProperty properties[name] as DeviceProperty
|
return@ReadOnlyProperty properties[name] as DeviceProperty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun <T : Any> DeviceBase.provideMutableProperty(
|
||||||
|
name: String,
|
||||||
|
converter: MetaConverter<T>,
|
||||||
|
): ReadOnlyProperty<DeviceBase, TypedDeviceProperty<T>> =
|
||||||
|
ReadOnlyProperty { _: DeviceBase, _: KProperty<*> ->
|
||||||
|
return@ReadOnlyProperty TypedDeviceProperty(properties[name] as DeviceProperty, converter)
|
||||||
|
}
|
||||||
|
|
||||||
public typealias PropertyDelegate = ReadOnlyProperty<DeviceBase, DeviceProperty>
|
public typealias PropertyDelegate = ReadOnlyProperty<DeviceBase, DeviceProperty>
|
||||||
|
public typealias TypedPropertyDelegate<T> = ReadOnlyProperty<DeviceBase, TypedDeviceProperty<T>>
|
||||||
|
|
||||||
private class DevicePropertyProvider<D : DeviceBase>(
|
private class DevicePropertyProvider<D : DeviceBase>(
|
||||||
val owner: D,
|
val owner: D,
|
||||||
@ -127,7 +164,23 @@ private class DevicePropertyProvider<D : DeviceBase>(
|
|||||||
override operator fun provideDelegate(thisRef: D, property: KProperty<*>): PropertyDelegate {
|
override operator fun provideDelegate(thisRef: D, property: KProperty<*>): PropertyDelegate {
|
||||||
val name = property.name
|
val name = property.name
|
||||||
owner.newMutableProperty(name, default, descriptorBuilder, getter, setter)
|
owner.newMutableProperty(name, default, descriptorBuilder, getter, setter)
|
||||||
return owner.provideMutableProperty()
|
return owner.provideMutableProperty(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TypedDevicePropertyProvider<D : DeviceBase, T : Any>(
|
||||||
|
val owner: D,
|
||||||
|
val default: MetaItem<*>?,
|
||||||
|
val converter: MetaConverter<T>,
|
||||||
|
val descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
|
private val getter: suspend (MetaItem<*>?) -> MetaItem<*>,
|
||||||
|
private val setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?,
|
||||||
|
) : PropertyDelegateProvider<D, TypedPropertyDelegate<T>> {
|
||||||
|
|
||||||
|
override operator fun provideDelegate(thisRef: D, property: KProperty<*>): TypedPropertyDelegate<T> {
|
||||||
|
val name = property.name
|
||||||
|
owner.newMutableProperty(name, default, descriptorBuilder, getter, setter)
|
||||||
|
return owner.provideMutableProperty(name, converter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,11 +217,21 @@ public fun DeviceBase.writingVirtual(
|
|||||||
setter = { _, newItem -> newItem }
|
setter = { _, newItem -> newItem }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
public fun DeviceBase.writingVirtual(
|
||||||
|
default: Meta,
|
||||||
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
|
): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = writing(
|
||||||
|
MetaItem.NodeItem(default),
|
||||||
|
descriptorBuilder,
|
||||||
|
getter = { it ?: MetaItem.NodeItem(default) },
|
||||||
|
setter = { _, newItem -> newItem }
|
||||||
|
)
|
||||||
|
|
||||||
public fun <D : DeviceBase> D.writingDouble(
|
public fun <D : DeviceBase> D.writingDouble(
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
getter: suspend (Double) -> Double,
|
getter: suspend (Double) -> Double,
|
||||||
setter: suspend (oldValue: Double?, newValue: Double) -> Double?,
|
setter: suspend (oldValue: Double?, newValue: Double) -> Double?,
|
||||||
): PropertyDelegateProvider<D, PropertyDelegate> {
|
): PropertyDelegateProvider<D, TypedPropertyDelegate<Double>> {
|
||||||
val innerGetter: suspend (MetaItem<*>?) -> MetaItem<*> = {
|
val innerGetter: suspend (MetaItem<*>?) -> MetaItem<*> = {
|
||||||
MetaItem.ValueItem(getter(it.double ?: Double.NaN).asValue())
|
MetaItem.ValueItem(getter(it.double ?: Double.NaN).asValue())
|
||||||
}
|
}
|
||||||
@ -177,9 +240,10 @@ public fun <D : DeviceBase> D.writingDouble(
|
|||||||
setter(oldValue.double, newValue.double ?: Double.NaN)?.asMetaItem()
|
setter(oldValue.double, newValue.double ?: Double.NaN)?.asMetaItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
return DevicePropertyProvider(
|
return TypedDevicePropertyProvider(
|
||||||
this,
|
this,
|
||||||
MetaItem.ValueItem(Double.NaN.asValue()),
|
MetaItem.ValueItem(Double.NaN.asValue()),
|
||||||
|
MetaConverter.double,
|
||||||
descriptorBuilder,
|
descriptorBuilder,
|
||||||
innerGetter,
|
innerGetter,
|
||||||
innerSetter
|
innerSetter
|
||||||
@ -190,7 +254,7 @@ public fun <D : DeviceBase> D.writingBoolean(
|
|||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
getter: suspend (Boolean?) -> Boolean,
|
getter: suspend (Boolean?) -> Boolean,
|
||||||
setter: suspend (oldValue: Boolean?, newValue: Boolean) -> Boolean?,
|
setter: suspend (oldValue: Boolean?, newValue: Boolean) -> Boolean?,
|
||||||
): PropertyDelegateProvider<D, PropertyDelegate> {
|
): PropertyDelegateProvider<D, TypedPropertyDelegate<Boolean>> {
|
||||||
val innerGetter: suspend (MetaItem<*>?) -> MetaItem<*> = {
|
val innerGetter: suspend (MetaItem<*>?) -> MetaItem<*> = {
|
||||||
MetaItem.ValueItem(getter(it.boolean).asValue())
|
MetaItem.ValueItem(getter(it.boolean).asValue())
|
||||||
}
|
}
|
||||||
@ -200,9 +264,10 @@ public fun <D : DeviceBase> D.writingBoolean(
|
|||||||
?.asMetaItem()
|
?.asMetaItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
return DevicePropertyProvider(
|
return TypedDevicePropertyProvider(
|
||||||
this,
|
this,
|
||||||
MetaItem.ValueItem(Null),
|
MetaItem.ValueItem(Null),
|
||||||
|
MetaConverter.boolean,
|
||||||
descriptorBuilder,
|
descriptorBuilder,
|
||||||
innerGetter,
|
innerGetter,
|
||||||
innerSetter
|
innerSetter
|
||||||
|
@ -1,6 +1,27 @@
|
|||||||
package hep.dataforge.control.base
|
package hep.dataforge.control.base
|
||||||
|
|
||||||
import hep.dataforge.meta.MetaItem
|
import hep.dataforge.meta.*
|
||||||
|
import hep.dataforge.meta.transformations.MetaConverter
|
||||||
import hep.dataforge.values.asValue
|
import hep.dataforge.values.asValue
|
||||||
|
import hep.dataforge.values.double
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.DurationUnit
|
||||||
|
import kotlin.time.toDuration
|
||||||
|
|
||||||
public fun Double.asMetaItem(): MetaItem.ValueItem = MetaItem.ValueItem(asValue())
|
public fun Double.asMetaItem(): MetaItem.ValueItem = MetaItem.ValueItem(asValue())
|
||||||
|
|
||||||
|
//TODO to be moved to DF
|
||||||
|
public object DurationConverter : MetaConverter<Duration> {
|
||||||
|
override fun itemToObject(item: MetaItem<*>): Duration = when (item) {
|
||||||
|
is MetaItem.NodeItem -> {
|
||||||
|
val unit: DurationUnit = item.node["unit"].enum<DurationUnit>() ?: DurationUnit.SECONDS
|
||||||
|
val value = item.node[Meta.VALUE_KEY].double ?: error("No value present for Duration")
|
||||||
|
value.toDuration(unit)
|
||||||
|
}
|
||||||
|
is MetaItem.ValueItem -> item.value.double.toDuration(DurationUnit.SECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun objectToMetaItem(obj: Duration): MetaItem<*> = obj.toDouble(DurationUnit.SECONDS).asMetaItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
public val MetaConverter.Companion.duration: MetaConverter<Duration> get() = DurationConverter
|
@ -1,14 +1,13 @@
|
|||||||
package hep.dataforge.control.controllers
|
package hep.dataforge.control.controllers
|
||||||
|
|
||||||
import hep.dataforge.context.AbstractPlugin
|
import hep.dataforge.context.*
|
||||||
import hep.dataforge.context.Context
|
|
||||||
import hep.dataforge.context.PluginFactory
|
|
||||||
import hep.dataforge.context.PluginTag
|
|
||||||
import hep.dataforge.control.api.Device
|
import hep.dataforge.control.api.Device
|
||||||
import hep.dataforge.control.api.DeviceHub
|
import hep.dataforge.control.api.DeviceHub
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.MetaBuilder
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.NameToken
|
import hep.dataforge.names.NameToken
|
||||||
|
import kotlin.properties.ReadOnlyProperty
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
public class DeviceManager : AbstractPlugin(), DeviceHub {
|
public class DeviceManager : AbstractPlugin(), DeviceHub {
|
||||||
@ -38,6 +37,21 @@ public class DeviceManager : AbstractPlugin(), DeviceHub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface DeviceFactory<D : Device> : Factory<D>
|
||||||
|
|
||||||
public val Context.devices: DeviceManager get() = plugins.fetch(DeviceManager)
|
public val Context.devices: DeviceManager get() = plugins.fetch(DeviceManager)
|
||||||
|
|
||||||
|
public fun <D : Device> DeviceManager.install(name: String, factory: DeviceFactory<D>, meta: Meta = Meta.EMPTY): D {
|
||||||
|
val device = factory(meta, context)
|
||||||
|
registerDevice(NameToken(name), device)
|
||||||
|
return device
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <D : Device> DeviceManager.installing(
|
||||||
|
factory: DeviceFactory<D>,
|
||||||
|
metaBuilder: MetaBuilder.() -> Unit = {},
|
||||||
|
): ReadOnlyProperty<Any?, D> = ReadOnlyProperty { _, property ->
|
||||||
|
val name = property.name
|
||||||
|
install(name, factory, Meta(metaBuilder))
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
package hep.dataforge.control.controllers
|
|
||||||
|
|
||||||
import hep.dataforge.control.base.DeviceProperty
|
|
||||||
import hep.dataforge.control.base.ReadOnlyDeviceProperty
|
|
||||||
import hep.dataforge.control.base.asMetaItem
|
|
||||||
import hep.dataforge.meta.*
|
|
||||||
import hep.dataforge.meta.transformations.MetaConverter
|
|
||||||
import hep.dataforge.values.Null
|
|
||||||
import hep.dataforge.values.double
|
|
||||||
import kotlin.properties.ReadOnlyProperty
|
|
||||||
import kotlin.properties.ReadWriteProperty
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
import kotlin.time.Duration
|
|
||||||
import kotlin.time.DurationUnit
|
|
||||||
import kotlin.time.toDuration
|
|
||||||
|
|
||||||
public operator fun ReadOnlyDeviceProperty.getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*> =
|
|
||||||
value ?: MetaItem.ValueItem(Null)
|
|
||||||
|
|
||||||
public operator fun DeviceProperty.setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>) {
|
|
||||||
this.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun <T : Any> ReadOnlyDeviceProperty.convert(metaConverter: MetaConverter<T>): ReadOnlyProperty<Any?, T> {
|
|
||||||
return ReadOnlyProperty { thisRef, property ->
|
|
||||||
getValue(thisRef, property).let { metaConverter.itemToObject(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun <T : Any> DeviceProperty.convert(metaConverter: MetaConverter<T>): ReadWriteProperty<Any?, T> {
|
|
||||||
return object : ReadWriteProperty<Any?, T> {
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
|
||||||
return this@convert.getValue(thisRef, property).let { metaConverter.itemToObject(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
|
||||||
this@convert.setValue(thisRef, property, value.let { metaConverter.objectToMetaItem(it) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun ReadOnlyDeviceProperty.double(): ReadOnlyProperty<Any?, Double> = convert(MetaConverter.double)
|
|
||||||
public fun DeviceProperty.double(): ReadWriteProperty<Any?, Double> = convert(MetaConverter.double)
|
|
||||||
|
|
||||||
public fun ReadOnlyDeviceProperty.int(): ReadOnlyProperty<Any?, Int> = convert(MetaConverter.int)
|
|
||||||
public fun DeviceProperty.int(): ReadWriteProperty<Any?, Int> = convert(MetaConverter.int)
|
|
||||||
|
|
||||||
public fun ReadOnlyDeviceProperty.string(): ReadOnlyProperty<Any?, String> = convert(MetaConverter.string)
|
|
||||||
public fun DeviceProperty.string(): ReadWriteProperty<Any?, String> = convert(MetaConverter.string)
|
|
||||||
|
|
||||||
public fun ReadOnlyDeviceProperty.boolean(): ReadOnlyProperty<Any?, Boolean> = convert(MetaConverter.boolean)
|
|
||||||
public fun DeviceProperty.boolean(): ReadWriteProperty<Any?, Boolean> = convert(MetaConverter.boolean)
|
|
||||||
|
|
||||||
//TODO to be moved to DF
|
|
||||||
private object DurationConverter : MetaConverter<Duration> {
|
|
||||||
override fun itemToObject(item: MetaItem<*>): Duration = when (item) {
|
|
||||||
is MetaItem.NodeItem -> {
|
|
||||||
val unit: DurationUnit = item.node["unit"].enum<DurationUnit>() ?: DurationUnit.SECONDS
|
|
||||||
val value = item.node[Meta.VALUE_KEY].double ?: error("No value present for Duration")
|
|
||||||
value.toDuration(unit)
|
|
||||||
}
|
|
||||||
is MetaItem.ValueItem -> item.value.double.toDuration(DurationUnit.SECONDS)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun objectToMetaItem(obj: Duration): MetaItem<*> = obj.toDouble(DurationUnit.SECONDS).asMetaItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
public val MetaConverter.Companion.duration: MetaConverter<Duration> get() = DurationConverter
|
|
||||||
|
|
||||||
public fun ReadOnlyDeviceProperty.duration(): ReadOnlyProperty<Any?, Duration> = convert(DurationConverter)
|
|
||||||
public fun DeviceProperty.duration(): ReadWriteProperty<Any?, Duration> = convert(DurationConverter)
|
|
@ -0,0 +1,87 @@
|
|||||||
|
package hep.dataforge.control.controllers
|
||||||
|
|
||||||
|
import hep.dataforge.control.base.*
|
||||||
|
import hep.dataforge.meta.*
|
||||||
|
import hep.dataforge.meta.transformations.MetaConverter
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
import kotlin.time.Duration
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocking read of the value
|
||||||
|
*/
|
||||||
|
public operator fun ReadOnlyDeviceProperty.getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*> =
|
||||||
|
runBlocking(scope.coroutineContext) {
|
||||||
|
read()
|
||||||
|
}
|
||||||
|
|
||||||
|
public operator fun <T: Any> TypedReadOnlyDeviceProperty<T>.getValue(thisRef: Any?, property: KProperty<*>): T =
|
||||||
|
runBlocking(scope.coroutineContext) {
|
||||||
|
readTyped()
|
||||||
|
}
|
||||||
|
|
||||||
|
public operator fun DeviceProperty.setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
public operator fun <T: Any> TypedDeviceProperty<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||||
|
this.typedValue = value
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <T : Any> ReadOnlyDeviceProperty.convert(
|
||||||
|
metaConverter: MetaConverter<T>,
|
||||||
|
forceRead: Boolean,
|
||||||
|
): ReadOnlyProperty<Any?, T> {
|
||||||
|
return ReadOnlyProperty { thisRef, property ->
|
||||||
|
runBlocking(scope.coroutineContext) {
|
||||||
|
read(forceRead).let { metaConverter.itemToObject(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <T : Any> DeviceProperty.convert(
|
||||||
|
metaConverter: MetaConverter<T>,
|
||||||
|
forceRead: Boolean,
|
||||||
|
): ReadWriteProperty<Any?, T> {
|
||||||
|
return object : ReadWriteProperty<Any?, T> {
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): T = runBlocking(scope.coroutineContext) {
|
||||||
|
read(forceRead).let { metaConverter.itemToObject(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||||
|
this@convert.setValue(thisRef, property, value.let { metaConverter.objectToMetaItem(it) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun ReadOnlyDeviceProperty.double(forceRead: Boolean = false): ReadOnlyProperty<Any?, Double> =
|
||||||
|
convert(MetaConverter.double, forceRead)
|
||||||
|
|
||||||
|
public fun DeviceProperty.double(forceRead: Boolean = false): ReadWriteProperty<Any?, Double> =
|
||||||
|
convert(MetaConverter.double, forceRead)
|
||||||
|
|
||||||
|
public fun ReadOnlyDeviceProperty.int(forceRead: Boolean = false): ReadOnlyProperty<Any?, Int> =
|
||||||
|
convert(MetaConverter.int, forceRead)
|
||||||
|
|
||||||
|
public fun DeviceProperty.int(forceRead: Boolean = false): ReadWriteProperty<Any?, Int> =
|
||||||
|
convert(MetaConverter.int, forceRead)
|
||||||
|
|
||||||
|
public fun ReadOnlyDeviceProperty.string(forceRead: Boolean = false): ReadOnlyProperty<Any?, String> =
|
||||||
|
convert(MetaConverter.string, forceRead)
|
||||||
|
|
||||||
|
public fun DeviceProperty.string(forceRead: Boolean = false): ReadWriteProperty<Any?, String> =
|
||||||
|
convert(MetaConverter.string, forceRead)
|
||||||
|
|
||||||
|
public fun ReadOnlyDeviceProperty.boolean(forceRead: Boolean = false): ReadOnlyProperty<Any?, Boolean> =
|
||||||
|
convert(MetaConverter.boolean, forceRead)
|
||||||
|
|
||||||
|
public fun DeviceProperty.boolean(forceRead: Boolean = false): ReadWriteProperty<Any?, Boolean> =
|
||||||
|
convert(MetaConverter.boolean, forceRead)
|
||||||
|
|
||||||
|
public fun ReadOnlyDeviceProperty.duration(forceRead: Boolean = false): ReadOnlyProperty<Any?, Duration> =
|
||||||
|
convert(DurationConverter, forceRead)
|
||||||
|
|
||||||
|
public fun DeviceProperty.duration(forceRead: Boolean = false): ReadWriteProperty<Any?, Duration> =
|
||||||
|
convert(DurationConverter, forceRead)
|
@ -19,7 +19,7 @@ repositories{
|
|||||||
dependencies{
|
dependencies{
|
||||||
implementation(project(":dataforge-device-core"))
|
implementation(project(":dataforge-device-core"))
|
||||||
implementation(project(":dataforge-device-server"))
|
implementation(project(":dataforge-device-server"))
|
||||||
implementation(project(":dataforge-device-client"))
|
implementation(project(":dataforge-magix-client"))
|
||||||
implementation("no.tornado:tornadofx:1.7.20")
|
implementation("no.tornado:tornadofx:1.7.20")
|
||||||
implementation(kotlin("stdlib-jdk8"))
|
implementation(kotlin("stdlib-jdk8"))
|
||||||
implementation("kscience.plotlykt:plotlykt-server:0.3.0-dev-2")
|
implementation("kscience.plotlykt:plotlykt-server:0.3.0-dev-2")
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import ru.mipt.npm.gradle.useFx
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.jvm")
|
id("ru.mipt.npm.jvm")
|
||||||
id("ru.mipt.npm.publish")
|
id("ru.mipt.npm.publish")
|
||||||
@ -7,6 +9,7 @@ plugins {
|
|||||||
|
|
||||||
kotlin{
|
kotlin{
|
||||||
explicitApi = null
|
explicitApi = null
|
||||||
|
useFx(ru.mipt.npm.gradle.FXModule.CONTROLS)
|
||||||
}
|
}
|
||||||
|
|
||||||
val ktorVersion: String by rootProject.extra
|
val ktorVersion: String by rootProject.extra
|
||||||
@ -14,4 +17,5 @@ val ktorVersion: String by rootProject.extra
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":dataforge-device-core"))
|
implementation(project(":dataforge-device-core"))
|
||||||
implementation(project(":dataforge-magix-client"))
|
implementation(project(":dataforge-magix-client"))
|
||||||
|
implementation("no.tornado:tornadofx:1.7.20")
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package ru.mipt.npm.devices.pimotionmaster
|
||||||
|
|
||||||
|
import hep.dataforge.context.Global
|
||||||
|
import hep.dataforge.control.controllers.DeviceManager
|
||||||
|
import hep.dataforge.control.controllers.installing
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty
|
||||||
|
import javafx.beans.property.SimpleStringProperty
|
||||||
|
import javafx.scene.Parent
|
||||||
|
import tornadofx.*
|
||||||
|
|
||||||
|
class PiMotionMasterApp : App(PiMotionMasterView::class)
|
||||||
|
|
||||||
|
class PiMotionMasterController : Controller() {
|
||||||
|
//initialize context
|
||||||
|
val context = Global.context("piMotionMaster")
|
||||||
|
|
||||||
|
//initialize deviceManager plugin
|
||||||
|
val deviceManager: DeviceManager = context.plugins.load(DeviceManager)
|
||||||
|
|
||||||
|
// install device
|
||||||
|
val motionMaster: PiMotionMasterDevice by deviceManager.installing(PiMotionMasterDevice)
|
||||||
|
}
|
||||||
|
|
||||||
|
class PiMotionMasterView : View() {
|
||||||
|
|
||||||
|
private val controller: PiMotionMasterController by inject()
|
||||||
|
|
||||||
|
override val root: Parent = borderpane {
|
||||||
|
top {
|
||||||
|
hbox {
|
||||||
|
val host = SimpleStringProperty("127.0.0.1")
|
||||||
|
val port = SimpleIntegerProperty(10024)
|
||||||
|
fieldset("Address:") {
|
||||||
|
field("Host:") {
|
||||||
|
textfield(host)
|
||||||
|
}
|
||||||
|
field("Port:") {
|
||||||
|
textfield(port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button("Connect") {
|
||||||
|
action {
|
||||||
|
controller.motionMaster.connect(host.get(), port.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
launch<PiMotionMasterApp>()
|
||||||
|
}
|
@ -1,18 +1,14 @@
|
|||||||
|
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||||
|
|
||||||
package ru.mipt.npm.devices.pimotionmaster
|
package ru.mipt.npm.devices.pimotionmaster
|
||||||
|
|
||||||
import hep.dataforge.context.Context
|
import hep.dataforge.context.Context
|
||||||
import hep.dataforge.control.api.DeviceHub
|
import hep.dataforge.control.api.DeviceHub
|
||||||
import hep.dataforge.control.api.PropertyDescriptor
|
import hep.dataforge.control.api.PropertyDescriptor
|
||||||
import hep.dataforge.control.base.*
|
import hep.dataforge.control.base.*
|
||||||
import hep.dataforge.control.controllers.boolean
|
import hep.dataforge.control.controllers.*
|
||||||
import hep.dataforge.control.controllers.double
|
import hep.dataforge.control.ports.*
|
||||||
import hep.dataforge.control.controllers.duration
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.control.ports.Port
|
|
||||||
import hep.dataforge.control.ports.PortProxy
|
|
||||||
import hep.dataforge.control.ports.send
|
|
||||||
import hep.dataforge.control.ports.withDelimiter
|
|
||||||
import hep.dataforge.meta.MetaItem
|
|
||||||
import hep.dataforge.meta.asMetaItem
|
|
||||||
import hep.dataforge.names.NameToken
|
import hep.dataforge.names.NameToken
|
||||||
import hep.dataforge.values.Null
|
import hep.dataforge.values.Null
|
||||||
import hep.dataforge.values.asValue
|
import hep.dataforge.values.asValue
|
||||||
@ -22,30 +18,80 @@ import kotlinx.coroutines.flow.takeWhile
|
|||||||
import kotlinx.coroutines.flow.toList
|
import kotlinx.coroutines.flow.toList
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import tornadofx.*
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.error
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
|
class PiMotionMasterDevice(
|
||||||
public class PiMotionMasterDevice(
|
|
||||||
context: Context,
|
context: Context,
|
||||||
axes: List<String>,
|
private val portFactory: PortFactory = TcpPort,
|
||||||
private val portFactory: suspend (MetaItem<*>?) -> Port,
|
|
||||||
) : DeviceBase(context), DeviceHub {
|
) : DeviceBase(context), DeviceHub {
|
||||||
|
|
||||||
override val scope: CoroutineScope = CoroutineScope(
|
override val scope: CoroutineScope = CoroutineScope(
|
||||||
context.coroutineContext + SupervisorJob(context.coroutineContext[Job])
|
context.coroutineContext + SupervisorJob(context.coroutineContext[Job])
|
||||||
)
|
)
|
||||||
|
|
||||||
public val port: DeviceProperty by writingVirtual(Null) {
|
val address: DeviceProperty by writingVirtual(Null) {
|
||||||
info = "The port for TCP connector"
|
info = "The port for TCP connector"
|
||||||
}
|
}
|
||||||
|
|
||||||
public val timeout: DeviceProperty by writingVirtual(Null) {
|
|
||||||
|
val timeout: DeviceProperty by writingVirtual(200.asValue()) {
|
||||||
info = "Timeout"
|
info = "Timeout"
|
||||||
}
|
}
|
||||||
|
|
||||||
public var timeoutValue: Duration by timeout.duration()
|
var timeoutValue: Duration by timeout.duration()
|
||||||
|
|
||||||
private val connector = PortProxy { portFactory(port.value) }
|
private val connector = PortProxy { portFactory(address.value.node ?: Meta.EMPTY, context) }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name-friendly accessor for axis
|
||||||
|
*/
|
||||||
|
var axes: Map<String, Axis> = emptyMap()
|
||||||
|
private set
|
||||||
|
|
||||||
|
override val devices: Map<NameToken, Axis> = axes.mapKeys { (key, _) -> NameToken(key) }
|
||||||
|
|
||||||
|
private suspend fun failIfError(message: (Int) -> String = { "Failed with error code $it" }) {
|
||||||
|
val errorCode = getErrorCode()
|
||||||
|
if (errorCode != 0) error(message(errorCode))
|
||||||
|
}
|
||||||
|
|
||||||
|
val connect: DeviceAction by acting({
|
||||||
|
info = "Connect to specific port and initialize axis"
|
||||||
|
}) { portSpec ->
|
||||||
|
//Clear current actions if present
|
||||||
|
if (address.value != null) {
|
||||||
|
stop()
|
||||||
|
}
|
||||||
|
//Update port
|
||||||
|
address.value = portSpec
|
||||||
|
//Initialize axes
|
||||||
|
if (portSpec != null) {
|
||||||
|
val idn = identity.read()
|
||||||
|
failIfError { "Can't connect to $portSpec. Error code: $it" }
|
||||||
|
logger.info { "Connected to $idn on $portSpec" }
|
||||||
|
val ids = request("SAI?")
|
||||||
|
if (ids != axes.keys.toList()) {
|
||||||
|
//re-define axes if needed
|
||||||
|
axes = ids.associateWith { Axis(it) }
|
||||||
|
}
|
||||||
|
ids.map { it.asValue() }.asValue().asMetaItem()
|
||||||
|
initialize()
|
||||||
|
failIfError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun connect(host: String, port: Int) {
|
||||||
|
scope.launch {
|
||||||
|
connect(Meta {
|
||||||
|
"host" put host
|
||||||
|
"port" put port
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val mutex = Mutex()
|
private val mutex = Mutex()
|
||||||
|
|
||||||
@ -64,7 +110,7 @@ public class PiMotionMasterDevice(
|
|||||||
connector.send(stringToSend)
|
connector.send(stringToSend)
|
||||||
}
|
}
|
||||||
|
|
||||||
public suspend fun getErrorCode(): Int = mutex.withLock {
|
suspend fun getErrorCode(): Int = mutex.withLock {
|
||||||
withTimeout(timeoutValue) {
|
withTimeout(timeoutValue) {
|
||||||
sendCommandInternal("ERR?")
|
sendCommandInternal("ERR?")
|
||||||
val errorString = connector.receiving().withDelimiter("\n").first()
|
val errorString = connector.receiving().withDelimiter("\n").first()
|
||||||
@ -72,7 +118,6 @@ public class PiMotionMasterDevice(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a synchronous request and receive a list of lines as a response
|
* Send a synchronous request and receive a list of lines as a response
|
||||||
*/
|
*/
|
||||||
@ -110,23 +155,26 @@ public class PiMotionMasterDevice(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val initialize: DeviceAction by acting {
|
||||||
public val initialize: DeviceAction by acting {
|
|
||||||
send("INI")
|
send("INI")
|
||||||
}
|
}
|
||||||
|
|
||||||
public val firmwareVersion: ReadOnlyDeviceProperty by readingString {
|
val identity: ReadOnlyDeviceProperty by readingString {
|
||||||
|
request("*IDN?").first()
|
||||||
|
}
|
||||||
|
|
||||||
|
val firmwareVersion: ReadOnlyDeviceProperty by readingString {
|
||||||
request("VER?").first()
|
request("VER?").first()
|
||||||
}
|
}
|
||||||
|
|
||||||
public val stop: DeviceAction by acting(
|
val stop: DeviceAction by acting(
|
||||||
descriptorBuilder = {
|
descriptorBuilder = {
|
||||||
info = "Stop all axis"
|
info = "Stop all axis"
|
||||||
},
|
},
|
||||||
action = { send("STP") }
|
action = { send("STP") }
|
||||||
)
|
)
|
||||||
|
|
||||||
public inner class Axis(public val axisId: String) : DeviceBase(context) {
|
inner class Axis(val axisId: String) : DeviceBase(context) {
|
||||||
override val scope: CoroutineScope get() = this@PiMotionMasterDevice.scope
|
override val scope: CoroutineScope get() = this@PiMotionMasterDevice.scope
|
||||||
|
|
||||||
private suspend fun readAxisBoolean(command: String): Boolean =
|
private suspend fun readAxisBoolean(command: String): Boolean =
|
||||||
@ -140,45 +188,49 @@ public class PiMotionMasterDevice(
|
|||||||
"0"
|
"0"
|
||||||
}
|
}
|
||||||
send(command, axisId, boolean)
|
send(command, axisId, boolean)
|
||||||
|
failIfError()
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun axisBooleanProperty(command: String, descriptorBuilder: PropertyDescriptor.() -> Unit = {}) =
|
private fun axisBooleanProperty(command: String, descriptorBuilder: PropertyDescriptor.() -> Unit = {}) =
|
||||||
writingBoolean<Axis>(
|
writingBoolean(
|
||||||
getter = { readAxisBoolean("$command?") },
|
getter = { readAxisBoolean("$command?") },
|
||||||
setter = { _, newValue -> writeAxisBoolean(command, newValue) },
|
setter = { _, newValue ->
|
||||||
|
writeAxisBoolean(command, newValue)
|
||||||
|
},
|
||||||
descriptorBuilder = descriptorBuilder
|
descriptorBuilder = descriptorBuilder
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun axisNumberProperty(command: String, descriptorBuilder: PropertyDescriptor.() -> Unit = {}) =
|
private fun axisNumberProperty(command: String, descriptorBuilder: PropertyDescriptor.() -> Unit = {}) =
|
||||||
writingDouble<Axis>(
|
writingDouble(
|
||||||
getter = {
|
getter = {
|
||||||
requestAndParse("$command?", axisId)[axisId]?.toDoubleOrNull()
|
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")
|
||||||
},
|
},
|
||||||
setter = { _, newValue ->
|
setter = { _, newValue ->
|
||||||
send(command, axisId, newValue.toString())
|
send(command, axisId, newValue.toString())
|
||||||
|
failIfError()
|
||||||
newValue
|
newValue
|
||||||
},
|
},
|
||||||
descriptorBuilder = descriptorBuilder
|
descriptorBuilder = descriptorBuilder
|
||||||
)
|
)
|
||||||
|
|
||||||
public val enabled: DeviceProperty by axisBooleanProperty("EAX") {
|
val enabled by axisBooleanProperty("EAX") {
|
||||||
info = "Motor enable state."
|
info = "Motor enable state."
|
||||||
}
|
}
|
||||||
|
|
||||||
public val halt: DeviceAction by acting {
|
val halt: DeviceAction by acting {
|
||||||
send("HLT", axisId)
|
send("HLT", axisId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public val targetPosition: DeviceProperty by axisNumberProperty("MOV") {
|
val targetPosition by axisNumberProperty("MOV") {
|
||||||
info = """
|
info = """
|
||||||
Sets a new absolute target position for the specified axis.
|
Sets a new absolute target position for the specified axis.
|
||||||
Servo mode must be switched on for the commanded axis prior to using this command (closed-loop operation).
|
Servo mode must be switched on for the commanded axis prior to using this command (closed-loop operation).
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
}
|
}
|
||||||
|
|
||||||
public val onTarget: ReadOnlyDeviceProperty by readingBoolean(
|
val onTarget: TypedReadOnlyDeviceProperty<Boolean> by readingBoolean(
|
||||||
descriptorBuilder = {
|
descriptorBuilder = {
|
||||||
info = "Queries the on-target state of the specified axis."
|
info = "Queries the on-target state of the specified axis."
|
||||||
},
|
},
|
||||||
@ -187,7 +239,7 @@ public class PiMotionMasterDevice(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
public val reference: ReadOnlyDeviceProperty by readingBoolean(
|
val reference: ReadOnlyDeviceProperty by readingBoolean(
|
||||||
descriptorBuilder = {
|
descriptorBuilder = {
|
||||||
info = "Get Referencing Result"
|
info = "Get Referencing Result"
|
||||||
},
|
},
|
||||||
@ -200,36 +252,40 @@ public class PiMotionMasterDevice(
|
|||||||
send("FRF", axisId)
|
send("FRF", axisId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public val position: DeviceProperty by axisNumberProperty("POS") {
|
val position: TypedDeviceProperty<Double> by axisNumberProperty("POS") {
|
||||||
info = "The current axis position."
|
info = "The current axis position."
|
||||||
}
|
}
|
||||||
|
|
||||||
var positionValue by position.double()
|
val openLoopTarget: DeviceProperty by axisNumberProperty("OMA") {
|
||||||
|
|
||||||
public val openLoopTarget: DeviceProperty by axisNumberProperty("OMA") {
|
|
||||||
info = "Position for open-loop operation."
|
info = "Position for open-loop operation."
|
||||||
}
|
}
|
||||||
|
|
||||||
public val closedLoop: DeviceProperty by axisBooleanProperty("SVO") {
|
val closedLoop: TypedDeviceProperty<Boolean> by axisBooleanProperty("SVO") {
|
||||||
info = "Servo closed loop mode"
|
info = "Servo closed loop mode"
|
||||||
}
|
}
|
||||||
|
|
||||||
var closedLoopValue by closedLoop.boolean()
|
val velocity: TypedDeviceProperty<Double> by axisNumberProperty("VEL") {
|
||||||
|
|
||||||
public val velocity: DeviceProperty by axisNumberProperty("VEL") {
|
|
||||||
info = "Velocity value for closed-loop operation"
|
info = "Velocity value for closed-loop operation"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val move by acting {
|
||||||
|
val target = it.double ?: it.node["target"].double ?: error("Unacceptable target value $it")
|
||||||
|
closedLoop.write(true)
|
||||||
|
//optionally set velocity
|
||||||
|
it.node["velocity"].double?.let { v ->
|
||||||
|
velocity.write(v)
|
||||||
|
}
|
||||||
|
position.write(target)
|
||||||
|
//read `onTarget` and `position` properties in a cycle until movement is complete
|
||||||
|
while (!onTarget.readTyped(true)) {
|
||||||
|
position.read(true)
|
||||||
|
delay(200)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val axisIds: ReadOnlyDeviceProperty by reading {
|
companion object : DeviceFactory<PiMotionMasterDevice> {
|
||||||
request("SAI?").map { it.asValue() }.asValue().asMetaItem()
|
override fun invoke(meta: Meta, context: Context): PiMotionMasterDevice = PiMotionMasterDevice(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val devices: Map<NameToken, Axis> = axes.associate { NameToken(it) to Axis(it) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Name-friendly accessor for axis
|
|
||||||
*/
|
|
||||||
val axes: Map<String, Axis> get() = devices.mapKeys { it.toString() }
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user