Implemented external configuration support through ExternalConfigurationProvider and ExternalConfigApplier, and expanded error handling in AbstractDeviceHubManager with CUSTOM strategy support. Improved DeviceLifecycleConfig, added HealthChecker support for device health checks. Implemented hot-swappable device functionality.
This commit is contained in:
parent
76fa751e25
commit
7e286ca111
@ -8,6 +8,11 @@ import kotlinx.serialization.Serializable
|
||||
@Serializable
|
||||
public enum class LifecycleState {
|
||||
|
||||
/**
|
||||
* The device is newly created and has not started yet.
|
||||
*/
|
||||
INITIAL,
|
||||
|
||||
/**
|
||||
* Device is initializing
|
||||
*/
|
||||
@ -18,6 +23,11 @@ public enum class LifecycleState {
|
||||
*/
|
||||
STARTED,
|
||||
|
||||
/**
|
||||
* The Device is stopping
|
||||
*/
|
||||
STOPPING,
|
||||
|
||||
/**
|
||||
* The Device is closed
|
||||
*/
|
||||
@ -50,8 +60,10 @@ public interface WithLifeCycle {
|
||||
public fun WithLifeCycle.bindToDeviceLifecycle(device: Device){
|
||||
device.onLifecycleEvent {
|
||||
when(it){
|
||||
LifecycleState.INITIAL -> {/*ignore*/}
|
||||
LifecycleState.STARTING -> start()
|
||||
LifecycleState.STARTED -> {/*ignore*/}
|
||||
LifecycleState.STOPPING -> stop()
|
||||
LifecycleState.STOPPED -> stop()
|
||||
LifecycleState.ERROR -> stop()
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,384 +1,248 @@
|
||||
package space.kscience.controls.spec
|
||||
|
||||
import kotlinx.coroutines.Deferred
|
||||
import space.kscience.controls.api.ActionDescriptorBuilder
|
||||
import space.kscience.controls.api.Device
|
||||
import space.kscience.controls.api.PropertyDescriptorBuilder
|
||||
import space.kscience.controls.api.id
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaConverter
|
||||
import space.kscience.dataforge.meta.ValueType
|
||||
import space.kscience.controls.api.*
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.string
|
||||
import kotlin.properties.PropertyDelegateProvider
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KMutableProperty1
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
/**
|
||||
* Create a [MetaConverter] for enum values
|
||||
* Create a [MetaConverter] for enum values using [reified] type with an option to ignore case.
|
||||
*/
|
||||
public fun <E : Enum<E>> createEnumConverter(enumValues: Array<E>): MetaConverter<E> = object : MetaConverter<E> {
|
||||
override val descriptor: MetaDescriptor = MetaDescriptor {
|
||||
valueType(ValueType.STRING)
|
||||
allowedValues(enumValues.map { it.name })
|
||||
}
|
||||
public inline fun <reified E : Enum<E>> createEnumConverter(ignoreCase: Boolean = false): MetaConverter<E> {
|
||||
val allValues = enumValues<E>()
|
||||
return object : MetaConverter<E> {
|
||||
override val descriptor: MetaDescriptor = MetaDescriptor {
|
||||
valueType(ValueType.STRING)
|
||||
allowedValues(allValues.map { it.name })
|
||||
}
|
||||
|
||||
override fun readOrNull(source: Meta): E? {
|
||||
val value = source.value ?: return null
|
||||
return enumValues.firstOrNull { it.name == value.string }
|
||||
}
|
||||
override fun readOrNull(source: Meta): E? {
|
||||
val stringVal = source.value?.string ?: return null
|
||||
return allValues.firstOrNull { it.name.equals(stringVal, ignoreCase) }
|
||||
}
|
||||
|
||||
override fun convert(obj: E): Meta = Meta(obj.name)
|
||||
override fun convert(obj: E): Meta = Meta(obj.name)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A read-only device property that delegates reading to a device [KProperty1]
|
||||
* Unified function: if [write] == null -> read-only property, else -> mutable property.
|
||||
*/
|
||||
public fun <T, D : Device> CompositeControlComponentSpec<D>.property(
|
||||
public fun <T, D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.typedProperty(
|
||||
converter: MetaConverter<T>,
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> T?,
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
read: suspend D.(String) -> T?,
|
||||
write: (suspend D.(String, T) -> Unit)? = null,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DevicePropertySpec<D, T>>> {
|
||||
return property(converter, descriptorBuilder, name, read)
|
||||
return if (write == null) {
|
||||
property(converter, descriptorBuilder, name, read)
|
||||
} else {
|
||||
mutableProperty(converter, descriptorBuilder, name, read, write)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutable property that delegates reading and writing to a device [KMutableProperty1]
|
||||
* Boolean property: read-only or mutable (if [write] is not null).
|
||||
*/
|
||||
public fun <T, D : Device> CompositeControlComponentSpec<D>.mutableProperty(
|
||||
converter: MetaConverter<T>,
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
public fun <D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.booleanProperty(
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> T?,
|
||||
write: suspend D.(propertyName: String, value: T) -> Unit,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, MutableDevicePropertySpec<D, T>>> {
|
||||
return mutableProperty(converter, descriptorBuilder, name, read, write)
|
||||
}
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
read: suspend D.(String) -> Boolean?,
|
||||
write: (suspend D.(String, Boolean) -> Unit)? = null,
|
||||
) = typedProperty(MetaConverter.boolean, name, descriptorBuilder, read, write)
|
||||
|
||||
/**
|
||||
* Register a mutable logical property (without a corresponding physical state) for a device
|
||||
* Int property: read-only or mutable.
|
||||
*/
|
||||
public fun <T, D : DeviceBase<D>> CompositeControlComponentSpec<D>.logical(
|
||||
converter: MetaConverter<T>,
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
public fun <D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.intProperty(
|
||||
name: String? = null,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, MutableDevicePropertySpec<D, T>>> =
|
||||
mutableProperty(
|
||||
converter,
|
||||
descriptorBuilder,
|
||||
name,
|
||||
read = { propertyName -> getProperty(propertyName)?.let(converter::readOrNull) },
|
||||
write = { propertyName, value -> writeProperty(propertyName, converter.convert(value)) }
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
read: suspend D.(String) -> Int?,
|
||||
write: (suspend D.(String, Int) -> Unit)? = null,
|
||||
) = typedProperty(MetaConverter.int, name, descriptorBuilder, read, write)
|
||||
|
||||
/**
|
||||
* Double property: read-only or mutable.
|
||||
*/
|
||||
public fun <D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.doubleProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
read: suspend D.(String) -> Double?,
|
||||
write: (suspend D.(String, Double) -> Unit)? = null,
|
||||
) = typedProperty(MetaConverter.double, name, descriptorBuilder, read, write)
|
||||
|
||||
/**
|
||||
* Long property: read-only or mutable.
|
||||
*/
|
||||
public fun <D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.longProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
read: suspend D.(String) -> Long?,
|
||||
write: (suspend D.(String, Long) -> Unit)? = null,
|
||||
) = typedProperty(MetaConverter.long, name, descriptorBuilder, read, write)
|
||||
|
||||
/**
|
||||
* Float property: read-only or mutable.
|
||||
*/
|
||||
public fun <D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.floatProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
read: suspend D.(String) -> Float?,
|
||||
write: (suspend D.(String, Float) -> Unit)? = null,
|
||||
) = typedProperty(MetaConverter.float, name, descriptorBuilder, read, write)
|
||||
|
||||
/**
|
||||
* Number property: read-only or mutable.
|
||||
*/
|
||||
public fun <D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.numberProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
read: suspend D.(String) -> Number?,
|
||||
write: (suspend D.(String, Number) -> Unit)? = null,
|
||||
) = typedProperty(MetaConverter.number, name, descriptorBuilder, read, write)
|
||||
|
||||
/**
|
||||
* String property: read-only or mutable.
|
||||
*/
|
||||
public fun <D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.stringProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
read: suspend D.(String) -> String?,
|
||||
write: (suspend D.(String, String) -> Unit)? = null,
|
||||
) = typedProperty(MetaConverter.string, name, descriptorBuilder, read, write)
|
||||
|
||||
/**
|
||||
* Meta property: read-only or mutable.
|
||||
*/
|
||||
public fun <D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.metaProperty(
|
||||
name: String? = null,
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
read: suspend D.(String) -> Meta?,
|
||||
write: (suspend D.(String, Meta) -> Unit)? = null,
|
||||
) = typedProperty(MetaConverter.meta, name, descriptorBuilder, read, write)
|
||||
|
||||
|
||||
/**
|
||||
* Enum property (read-only or mutable).
|
||||
* [ignoreCase] controls case sensitivity when reading the enum value from Meta.
|
||||
* If [write] is null, the property is read-only; otherwise it's read-write.
|
||||
*/
|
||||
public inline fun <reified E : Enum<E>, D : ConfigurableCompositeControlComponent<D>>
|
||||
CompositeControlComponentSpec<D>.enumProperty(
|
||||
name: String? = null,
|
||||
ignoreCase: Boolean = false,
|
||||
noinline descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
noinline read: suspend D.(String) -> E?,
|
||||
noinline write: (suspend D.(String, E) -> Unit)? = null,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DevicePropertySpec<D, E>>> =
|
||||
typedProperty(
|
||||
converter = createEnumConverter<E>(ignoreCase),
|
||||
name = name,
|
||||
descriptorBuilder = descriptorBuilder,
|
||||
read = read,
|
||||
write = write
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a boolean property for a device.
|
||||
* List property: read-only or mutable.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.boolean(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
public fun <T, D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.listProperty(
|
||||
listConverter: MetaConverter<List<T>>,
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Boolean?,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DevicePropertySpec<D, Boolean>>> =
|
||||
property(MetaConverter.boolean, descriptorBuilder, name, read)
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
read: suspend D.(String) -> List<T>?,
|
||||
write: (suspend D.(String, List<T>) -> Unit)? = null,
|
||||
) = typedProperty(listConverter, name, descriptorBuilder, read, write)
|
||||
|
||||
/**
|
||||
* Creates a mutable boolean property for a device.
|
||||
* Logical property (no real hardware I/O).
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.booleanMutable(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
public fun <T, D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.logicalProperty(
|
||||
converter: MetaConverter<T>,
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Boolean?,
|
||||
write: suspend D.(propertyName: String, value: Boolean) -> Unit
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, MutableDevicePropertySpec<D, Boolean>>> =
|
||||
mutableProperty(MetaConverter.boolean, descriptorBuilder, name, read, write)
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
) = typedProperty(
|
||||
converter = converter,
|
||||
name = name,
|
||||
descriptorBuilder = descriptorBuilder,
|
||||
read = { propertyName -> getProperty(propertyName)?.let(converter::readOrNull) },
|
||||
write = { propertyName, value -> writeProperty(propertyName, converter.convert(value)) }
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a read-only number property for a device.
|
||||
* Creates an action with optional input/output converters.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.number(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Number?,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DevicePropertySpec<D, Number>>> =
|
||||
property(MetaConverter.number, descriptorBuilder, name, read)
|
||||
|
||||
/**
|
||||
* Creates a mutable number property for a device.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.numberMutable(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Number?,
|
||||
write: suspend D.(propertyName: String, value: Number) -> Unit
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, MutableDevicePropertySpec<D, Number>>> =
|
||||
mutableProperty(MetaConverter.number, descriptorBuilder, name, read, write)
|
||||
|
||||
|
||||
/**
|
||||
* Creates a read-only double property for a device.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.double(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Double?,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DevicePropertySpec<D, Double>>> =
|
||||
property(MetaConverter.double, descriptorBuilder, name, read)
|
||||
|
||||
/**
|
||||
* Creates a mutable double property for a device.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.doubleMutable(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Double?,
|
||||
write: suspend D.(propertyName: String, value: Double) -> Unit
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, MutableDevicePropertySpec<D, Double>>> =
|
||||
mutableProperty(MetaConverter.double, descriptorBuilder, name, read, write)
|
||||
|
||||
/**
|
||||
* Creates a read-only string property for a device.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.string(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> String?
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DevicePropertySpec<D, String>>> =
|
||||
property(MetaConverter.string, descriptorBuilder, name, read)
|
||||
|
||||
/**
|
||||
* Creates a mutable string property for a device.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.stringMutableProperty(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> String?,
|
||||
write: suspend D.(propertyName: String, value: String) -> Unit
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, MutableDevicePropertySpec<D, String>>> =
|
||||
mutableProperty(MetaConverter.string, descriptorBuilder, name, read, write)
|
||||
|
||||
/**
|
||||
* Creates a read-only meta property for a device.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.meta(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Meta?,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DevicePropertySpec<D, Meta>>> =
|
||||
property(MetaConverter.meta, descriptorBuilder, name, read)
|
||||
|
||||
/**
|
||||
* Creates a mutable meta property for a device.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.metaMutable(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Meta?,
|
||||
write: suspend D.(propertyName: String, value: Meta) -> Unit
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, MutableDevicePropertySpec<D, Meta>>> =
|
||||
mutableProperty(MetaConverter.meta, descriptorBuilder, name, read, write)
|
||||
|
||||
/**
|
||||
* Creates a read-only enum property for a device.
|
||||
*/
|
||||
public fun <E : Enum<E>, D : Device> CompositeControlComponentSpec<D>.enum(
|
||||
enumValues: Array<E>,
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> E?,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DevicePropertySpec<D, E>>> {
|
||||
val converter = createEnumConverter(enumValues)
|
||||
return property(converter, descriptorBuilder, name, read)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mutable enum property for a device.
|
||||
*/
|
||||
public fun <E : Enum<E>, D : Device> CompositeControlComponentSpec<D>.enumMutable(
|
||||
enumValues: Array<E>,
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> E?,
|
||||
write: suspend D.(propertyName: String, value: E) -> Unit
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, MutableDevicePropertySpec<D, E>>> {
|
||||
val converter = createEnumConverter(enumValues)
|
||||
return mutableProperty(converter, descriptorBuilder, name, read, write)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a read-only float property for a device.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.float(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Float?,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DevicePropertySpec<D, Float>>> =
|
||||
property(MetaConverter.float, descriptorBuilder, name, read)
|
||||
|
||||
/**
|
||||
* Creates a mutable float property for a device.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.floatMutable(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Float?,
|
||||
write: suspend D.(propertyName: String, value: Float) -> Unit
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, MutableDevicePropertySpec<D, Float>>> =
|
||||
mutableProperty(MetaConverter.float, descriptorBuilder, name, read, write)
|
||||
|
||||
/**
|
||||
* Creates a read-only long property for a device.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.long(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Long?,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DevicePropertySpec<D, Long>>> =
|
||||
property(MetaConverter.long, descriptorBuilder, name, read)
|
||||
|
||||
/**
|
||||
* Creates a mutable long property for a device.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.longMutable(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Long?,
|
||||
write: suspend D.(propertyName: String, value: Long) -> Unit
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, MutableDevicePropertySpec<D, Long>>> =
|
||||
mutableProperty(MetaConverter.long, descriptorBuilder, name, read, write)
|
||||
|
||||
/**
|
||||
* Creates a read-only int property for a device.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.int(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Int?,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DevicePropertySpec<D, Int>>> =
|
||||
property(MetaConverter.int, descriptorBuilder, name, read)
|
||||
|
||||
/**
|
||||
* Creates a mutable int property for a device.
|
||||
*/
|
||||
public fun <D : Device> CompositeControlComponentSpec<D>.intMutable(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Int?,
|
||||
write: suspend D.(propertyName: String, value: Int) -> Unit
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, MutableDevicePropertySpec<D, Int>>> =
|
||||
mutableProperty(MetaConverter.int, descriptorBuilder, name, read, write)
|
||||
|
||||
/**
|
||||
* Creates a read-only list property for a device.
|
||||
*/
|
||||
public fun <T, D : Device> CompositeControlComponentSpec<D>.list(
|
||||
converter: MetaConverter<List<T>>,
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> List<T>?,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DevicePropertySpec<D, List<T>>>> =
|
||||
property(converter, descriptorBuilder, name, read)
|
||||
|
||||
/**
|
||||
* Creates a mutable list property for a device.
|
||||
*/
|
||||
public fun <T, D : Device> CompositeControlComponentSpec<D>.listMutable(
|
||||
converter: MetaConverter<List<T>>,
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> List<T>?,
|
||||
write: suspend D.(propertyName: String, value: List<T>) -> Unit
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, MutableDevicePropertySpec<D, List<T>>>> =
|
||||
mutableProperty(converter, descriptorBuilder, name, read, write)
|
||||
|
||||
|
||||
public fun <I, O, D : Device> CompositeControlComponentSpec<D>.asyncActionProperty(
|
||||
public fun <I, O, D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.typedAction(
|
||||
inputConverter: MetaConverter<I>,
|
||||
outputConverter: MetaConverter<O>,
|
||||
name: String? = null,
|
||||
descriptorBuilder: ActionDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
execute: suspend D.(I) -> Deferred<O>,
|
||||
execute: suspend D.(I) -> O,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DeviceActionSpec<D, I, O>>> =
|
||||
action(inputConverter, outputConverter, descriptorBuilder, name) { input ->
|
||||
execute(input).await()
|
||||
}
|
||||
|
||||
public fun <T, D : Device> CompositeControlComponentSpec<D>.metaProperty(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Meta?,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DevicePropertySpec<D, Meta>>> =
|
||||
property(MetaConverter.meta, descriptorBuilder, name, read)
|
||||
|
||||
|
||||
public fun <T, D : Device> CompositeControlComponentSpec<D>.mutableMetaProperty(
|
||||
descriptorBuilder: PropertyDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
read: suspend D.(propertyName: String) -> Meta?,
|
||||
write: suspend D.(propertyName: String, value: Meta) -> Unit
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, MutableDevicePropertySpec<D, Meta>>> =
|
||||
mutableProperty(MetaConverter.meta, descriptorBuilder, name, read, write)
|
||||
action(
|
||||
inputConverter = inputConverter,
|
||||
outputConverter = outputConverter,
|
||||
descriptorBuilder = descriptorBuilder,
|
||||
name = name,
|
||||
execute = execute
|
||||
)
|
||||
|
||||
/**
|
||||
* An action that takes no parameters and returns no values
|
||||
* Action with no parameters and no return values.
|
||||
*/
|
||||
public fun <T, D : Device> CompositeControlComponentSpec<D>.unitAction(
|
||||
descriptorBuilder: ActionDescriptorBuilder.() -> Unit = {},
|
||||
public fun <D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.unitAction(
|
||||
name: String? = null,
|
||||
descriptorBuilder: ActionDescriptorBuilder.() -> Unit = {},
|
||||
execute: suspend D.() -> Unit,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DeviceActionSpec<D, Unit, Unit>>> =
|
||||
action(
|
||||
MetaConverter.unit,
|
||||
MetaConverter.unit,
|
||||
descriptorBuilder,
|
||||
name
|
||||
typedAction(
|
||||
inputConverter = MetaConverter.unit,
|
||||
outputConverter = MetaConverter.unit,
|
||||
name = name,
|
||||
descriptorBuilder = descriptorBuilder,
|
||||
) {
|
||||
execute()
|
||||
}
|
||||
|
||||
public fun <I, O, D : Device> CompositeControlComponentSpec<D>.asyncAction(
|
||||
/**
|
||||
* Action with async result. The result is awaited.
|
||||
*/
|
||||
public fun <I, O, D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.asyncAction(
|
||||
inputConverter: MetaConverter<I>,
|
||||
outputConverter: MetaConverter<O>,
|
||||
descriptorBuilder: ActionDescriptorBuilder.() -> Unit = {},
|
||||
name: String? = null,
|
||||
descriptorBuilder: ActionDescriptorBuilder.() -> Unit = {},
|
||||
execute: suspend D.(I) -> Deferred<O>,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DeviceActionSpec<D, I, O>>> =
|
||||
action(
|
||||
inputConverter,
|
||||
outputConverter,
|
||||
descriptorBuilder,
|
||||
name
|
||||
) {
|
||||
execute(it).await()
|
||||
typedAction(inputConverter, outputConverter, name, descriptorBuilder) { input ->
|
||||
execute(input).await()
|
||||
}
|
||||
|
||||
/**
|
||||
* An action that takes [Meta] and returns [Meta]. No conversions are done
|
||||
* Action that takes and returns [Meta].
|
||||
*/
|
||||
public fun <T, D : Device> CompositeControlComponentSpec<D>.metaAction(
|
||||
descriptorBuilder: ActionDescriptorBuilder.() -> Unit = {},
|
||||
public fun <D : ConfigurableCompositeControlComponent<D>> CompositeControlComponentSpec<D>.metaAction(
|
||||
name: String? = null,
|
||||
descriptorBuilder: ActionDescriptorBuilder.() -> Unit = {},
|
||||
execute: suspend D.(Meta) -> Meta,
|
||||
): PropertyDelegateProvider<CompositeControlComponentSpec<D>, ReadOnlyProperty<CompositeControlComponentSpec<D>, DeviceActionSpec<D, Meta, Meta>>> =
|
||||
action(
|
||||
MetaConverter.meta,
|
||||
MetaConverter.meta,
|
||||
descriptorBuilder,
|
||||
name
|
||||
) {
|
||||
execute(it)
|
||||
}
|
||||
typedAction(MetaConverter.meta, MetaConverter.meta, name, descriptorBuilder, execute)
|
||||
|
||||
/**
|
||||
* Throw an exception if device does not have all properties and actions defined by this specification
|
||||
* Validates that [device] has all properties and actions defined by this spec.
|
||||
*/
|
||||
public fun CompositeControlComponentSpec<*>.validate(device: Device) {
|
||||
properties.map { it.value.descriptor }.forEach { specProperty ->
|
||||
check(specProperty in device.propertyDescriptors) { "Property ${specProperty.name} not registered in ${device.id}" }
|
||||
public fun CompositeControlComponentSpec<*>.validateSpec(device: Device) {
|
||||
properties.values.forEach { propSpec ->
|
||||
check(propSpec.descriptor in device.propertyDescriptors) {
|
||||
"Property ${propSpec.descriptor.name} not registered in ${device.id}"
|
||||
}
|
||||
}
|
||||
|
||||
actions.map { it.value.descriptor }.forEach { specAction ->
|
||||
check(specAction in device.actionDescriptors) { "Action ${specAction.name} not registered in ${device.id}" }
|
||||
actions.values.forEach { actSpec ->
|
||||
check(actSpec.descriptor in device.actionDescriptors) {
|
||||
"Action ${actSpec.descriptor.name} not registered in ${device.id}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,98 +18,92 @@ class CompositeControlTest {
|
||||
// ---------------------- Device Specifications ----------------------------------
|
||||
|
||||
public object StepperMotorSpec : CompositeControlComponentSpec<StepperMotorDevice>() {
|
||||
public val position by intMutable(
|
||||
public val position by intProperty(
|
||||
name = "position",
|
||||
read = { getPosition() },
|
||||
write = { _, value -> setPosition(value) }
|
||||
)
|
||||
|
||||
public val maxPosition by int(
|
||||
public val maxPosition by intProperty(
|
||||
name = "maxPosition",
|
||||
read = { maxPosition }
|
||||
)
|
||||
}
|
||||
|
||||
public object ValveSpec : CompositeControlComponentSpec<ValveDevice>() {
|
||||
public val state by booleanMutable(
|
||||
public val state by booleanProperty(
|
||||
read = { getState() },
|
||||
write = { _, value -> setState(value) }
|
||||
)
|
||||
}
|
||||
|
||||
public object PressureChamberSpec : CompositeControlComponentSpec<PressureChamberDevice>() {
|
||||
public val pressure by doubleMutable(
|
||||
public val pressure by doubleProperty(
|
||||
read = { getPressure() },
|
||||
write = { _, value -> setPressure(value) }
|
||||
)
|
||||
}
|
||||
|
||||
public object SyringePumpSpec : CompositeControlComponentSpec<SyringePumpDevice>() {
|
||||
public val volume by doubleMutable(
|
||||
public val volume by doubleProperty(
|
||||
read = { getVolume() },
|
||||
write = { _, value -> setVolume(value) }
|
||||
)
|
||||
}
|
||||
|
||||
public object ReagentSensorSpec : CompositeControlComponentSpec<ReagentSensorDevice>() {
|
||||
public val isPresent by boolean(
|
||||
public val isPresent by booleanProperty(
|
||||
read = { checkReagent() }
|
||||
)
|
||||
}
|
||||
|
||||
public object NeedleSpec : CompositeControlComponentSpec<NeedleDevice>() {
|
||||
public val mode by enumMutable(
|
||||
enumValues = NeedleDevice.Mode.entries.toTypedArray(),
|
||||
public val mode by enumProperty<NeedleDevice.Mode, NeedleDevice>(
|
||||
read = { getMode() },
|
||||
write = { _, value -> setMode(value) }
|
||||
)
|
||||
|
||||
public val position by doubleMutable(
|
||||
public val position by doubleProperty(
|
||||
read = { getPosition() },
|
||||
write = { _, value -> setPosition(value) }
|
||||
)
|
||||
}
|
||||
|
||||
public object ShakerSpec : CompositeControlComponentSpec<ShakerDevice>() {
|
||||
public val verticalMotor by childSpec<StepperMotorSpec, StepperMotorDevice>()
|
||||
public val horizontalMotor by childSpec<StepperMotorSpec, StepperMotorDevice>()
|
||||
public val verticalMotor by childSpec<StepperMotorSpec, StepperMotorDevice>(StepperMotorSpec)
|
||||
public val horizontalMotor by childSpec<StepperMotorSpec, StepperMotorDevice>(StepperMotorSpec)
|
||||
}
|
||||
|
||||
public object TransportationSystemSpec : CompositeControlComponentSpec<TransportationSystem>() {
|
||||
public val slideMotor by childSpec<StepperMotorSpec, StepperMotorDevice>()
|
||||
|
||||
public val pushMotor by childSpec<StepperMotorSpec, StepperMotorDevice>()
|
||||
|
||||
public val receiveMotor by childSpec<StepperMotorSpec, StepperMotorDevice>()
|
||||
public val slideMotor by childSpec<StepperMotorSpec, StepperMotorDevice>(StepperMotorSpec)
|
||||
public val pushMotor by childSpec<StepperMotorSpec, StepperMotorDevice>(StepperMotorSpec)
|
||||
public val receiveMotor by childSpec<StepperMotorSpec, StepperMotorDevice>(StepperMotorSpec)
|
||||
}
|
||||
|
||||
|
||||
public object AnalyzerSpec : CompositeControlComponentSpec<AnalyzerDevice>() {
|
||||
public val transportationSystem by childSpec<TransportationSystemSpec, TransportationSystemSpec>()
|
||||
public val shakerDevice by childSpec<ShakerSpec, ShakerDevice>()
|
||||
public val needleDevice by childSpec<NeedleSpec, NeedleDevice>()
|
||||
public val transportationSystem by childSpec<TransportationSystemSpec, TransportationSystem>(TransportationSystemSpec)
|
||||
public val shakerDevice by childSpec<ShakerSpec, ShakerDevice>(ShakerSpec)
|
||||
public val needleDevice by childSpec<NeedleSpec, NeedleDevice>(NeedleSpec)
|
||||
|
||||
public val valveV20 by childSpec<ValveSpec, ValveDevice>(ValveSpec)
|
||||
public val valveV17 by childSpec<ValveSpec, ValveDevice>(ValveSpec)
|
||||
public val valveV18 by childSpec<ValveSpec, ValveDevice>(ValveSpec)
|
||||
public val valveV35 by childSpec<ValveSpec, ValveDevice>(ValveSpec)
|
||||
|
||||
public val valveV20 by childSpec<ValveSpec, ValveDevice>()
|
||||
public val valveV17 by childSpec<ValveSpec, ValveDevice>()
|
||||
public val valveV18 by childSpec<ValveSpec, ValveDevice>()
|
||||
public val valveV35 by childSpec<ValveSpec, ValveDevice>()
|
||||
public val pressureChamberHigh by childSpec<PressureChamberSpec, PressureChamberDevice>(PressureChamberSpec)
|
||||
public val pressureChamberLow by childSpec<PressureChamberSpec, PressureChamberDevice>(PressureChamberSpec)
|
||||
|
||||
public val syringePumpMA100 by childSpec<SyringePumpSpec, SyringePumpDevice>(SyringePumpSpec)
|
||||
public val syringePumpMA25 by childSpec<SyringePumpSpec, SyringePumpDevice>(SyringePumpSpec)
|
||||
|
||||
public val pressureChamberHigh by childSpec<PressureChamberSpec, PressureChamberDevice>()
|
||||
public val pressureChamberLow by childSpec<PressureChamberSpec, PressureChamberDevice>()
|
||||
|
||||
public val syringePumpMA100 by childSpec<SyringePumpSpec, SyringePumpDevice>()
|
||||
public val syringePumpMA25 by childSpec<SyringePumpSpec, SyringePumpDevice>()
|
||||
|
||||
public val reagentSensor1 by childSpec<ReagentSensorSpec, ReagentSensorDevice>()
|
||||
public val reagentSensor2 by childSpec<ReagentSensorSpec, ReagentSensorDevice>()
|
||||
public val reagentSensor3 by childSpec<ReagentSensorSpec, ReagentSensorDevice>()
|
||||
public val reagentSensor1 by childSpec<ReagentSensorSpec, ReagentSensorDevice>(ReagentSensorSpec)
|
||||
public val reagentSensor2 by childSpec<ReagentSensorSpec, ReagentSensorDevice>(ReagentSensorSpec)
|
||||
public val reagentSensor3 by childSpec<ReagentSensorSpec, ReagentSensorDevice>(ReagentSensorSpec)
|
||||
}
|
||||
|
||||
// ---------------------- Device Implementations ----------------------------------
|
||||
// ---------------------- Device Implementations ----------------------------------
|
||||
|
||||
// Implementation of Stepper Motor Device
|
||||
public class StepperMotorDevice(
|
||||
context: Context,
|
||||
meta: Meta = Meta.EMPTY
|
||||
@ -774,6 +768,9 @@ class CompositeControlTest {
|
||||
val context = createTestContext()
|
||||
val shaker = ShakerDevice(context)
|
||||
|
||||
shaker.initChildren()
|
||||
shaker.start()// Start the device
|
||||
|
||||
// Access properties to initialize motors and test shaking
|
||||
val verticalMotor = shaker.verticalMotor
|
||||
val horizontalMotor = shaker.horizontalMotor
|
||||
@ -791,19 +788,23 @@ class CompositeControlTest {
|
||||
val context = createTestContext()
|
||||
val transportationSystem = TransportationSystem(context)
|
||||
|
||||
transportationSystem.initChildren()
|
||||
transportationSystem.start()// Start the device
|
||||
|
||||
// Access properties to initialize motors and test existence
|
||||
assertNotNull(transportationSystem.slideMotor, "slideMotor should exist")
|
||||
assertNotNull(transportationSystem.pushMotor, "pushMotor should exist")
|
||||
assertNotNull(transportationSystem.receiveMotor, "receiveMotor should exist")
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `test AnalyzerDevice device access`() = runTest{
|
||||
fun `test AnalyzerDevice device access`() = runTest {
|
||||
val context = createTestContext()
|
||||
val analyzer = AnalyzerDevice(context)
|
||||
analyzer.initChildren()
|
||||
analyzer.start()// Start the device
|
||||
|
||||
// Access properties to initialize child devices and test existence
|
||||
// Access properties to initialize child devices
|
||||
assertNotNull(analyzer.transportationSystem, "Transportation system should exist")
|
||||
assertNotNull(analyzer.shakerDevice, "Shaker device should exist")
|
||||
assertNotNull(analyzer.needleDevice, "Needle device should exist")
|
||||
|
Loading…
Reference in New Issue
Block a user