Update constructor api
This commit is contained in:
parent
29af4dfb2c
commit
f28e9dc226
@ -6,14 +6,14 @@ plugins {
|
||||
}
|
||||
|
||||
val dataforgeVersion: String by extra("0.8.0")
|
||||
val visionforgeVersion by extra("0.4.0")
|
||||
val visionforgeVersion by extra("0.4.1")
|
||||
val ktorVersion: String by extra(space.kscience.gradle.KScienceVersions.ktorVersion)
|
||||
val rsocketVersion by extra("0.15.4")
|
||||
val xodusVersion by extra("2.0.1")
|
||||
|
||||
allprojects {
|
||||
group = "space.kscience"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1-dev-1"
|
||||
repositories{
|
||||
maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev")
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import kotlin.properties.PropertyDelegateProvider
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.time.Duration
|
||||
|
||||
@ -55,17 +54,17 @@ public abstract class DeviceConstructor(
|
||||
/**
|
||||
* Register a property and provide a direct reader for it
|
||||
*/
|
||||
public fun <T : Any> property(
|
||||
state: DeviceState<T>,
|
||||
public fun <T : Any, S: DeviceState<T>> property(
|
||||
state: S,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
nameOverride: String? = null,
|
||||
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, T>> =
|
||||
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, S>> =
|
||||
PropertyDelegateProvider { _: DeviceConstructor, property ->
|
||||
val name = nameOverride ?: property.name
|
||||
val descriptor = PropertyDescriptor(name).apply(descriptorBuilder)
|
||||
registerProperty(descriptor, state)
|
||||
ReadOnlyProperty { _: DeviceConstructor, _ ->
|
||||
state.value
|
||||
state
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,37 +78,14 @@ public abstract class DeviceConstructor(
|
||||
initialState: T,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
nameOverride: String? = null,
|
||||
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, T>> = property(
|
||||
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, DeviceState<T>>> = property(
|
||||
DeviceState.external(this, metaConverter, readInterval, initialState, reader),
|
||||
descriptorBuilder,
|
||||
nameOverride,
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* Register a mutable property and provide a direct reader for it
|
||||
*/
|
||||
public fun <T : Any> mutableProperty(
|
||||
state: MutableDeviceState<T>,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
nameOverride: String? = null,
|
||||
): PropertyDelegateProvider<DeviceConstructor, ReadWriteProperty<DeviceConstructor, T>> =
|
||||
PropertyDelegateProvider { _: DeviceConstructor, property ->
|
||||
val name = nameOverride ?: property.name
|
||||
val descriptor = PropertyDescriptor(name).apply(descriptorBuilder)
|
||||
registerProperty(descriptor, state)
|
||||
object : ReadWriteProperty<DeviceConstructor, T> {
|
||||
override fun getValue(thisRef: DeviceConstructor, property: KProperty<*>): T = state.value
|
||||
|
||||
override fun setValue(thisRef: DeviceConstructor, property: KProperty<*>, value: T) {
|
||||
state.value = value
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register external state as a property
|
||||
* Register a mutable external state as a property
|
||||
*/
|
||||
public fun <T : Any> mutableProperty(
|
||||
metaConverter: MetaConverter<T>,
|
||||
@ -119,22 +95,22 @@ public abstract class DeviceConstructor(
|
||||
initialState: T,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
nameOverride: String? = null,
|
||||
): PropertyDelegateProvider<DeviceConstructor, ReadWriteProperty<DeviceConstructor, T>> = mutableProperty(
|
||||
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, MutableDeviceState<T>>> = property(
|
||||
DeviceState.external(this, metaConverter, readInterval, initialState, reader, writer),
|
||||
descriptorBuilder,
|
||||
nameOverride,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create and register a virtual property with optional [callback]
|
||||
* Create and register a virtual mutable property with optional [callback]
|
||||
*/
|
||||
public fun <T : Any> state(
|
||||
public fun <T : Any> virtualProperty(
|
||||
metaConverter: MetaConverter<T>,
|
||||
initialState: T,
|
||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||
nameOverride: String? = null,
|
||||
callback: (T) -> Unit = {},
|
||||
): PropertyDelegateProvider<DeviceConstructor, ReadWriteProperty<DeviceConstructor, T>> = mutableProperty(
|
||||
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, MutableDeviceState<T>>> = property(
|
||||
DeviceState.virtual(metaConverter, initialState, callback),
|
||||
descriptorBuilder,
|
||||
nameOverride,
|
||||
|
@ -1,6 +1,7 @@
|
||||
package space.kscience.controls.constructor
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
@ -11,6 +12,7 @@ import space.kscience.controls.spec.MutableDevicePropertySpec
|
||||
import space.kscience.controls.spec.name
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaConverter
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.time.Duration
|
||||
|
||||
/**
|
||||
@ -29,6 +31,13 @@ public val <T> DeviceState<T>.metaFlow: Flow<Meta> get() = valueFlow.map(convert
|
||||
|
||||
public val <T> DeviceState<T>.valueAsMeta: Meta get() = converter.convert(value)
|
||||
|
||||
public operator fun <T> DeviceState<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value
|
||||
|
||||
/**
|
||||
* Collect values in a given [scope]
|
||||
*/
|
||||
public fun <T> DeviceState<T>.collectValuesIn(scope: CoroutineScope, block: suspend (T)->Unit): Job =
|
||||
valueFlow.onEach(block).launchIn(scope)
|
||||
|
||||
/**
|
||||
* A mutable state of a device
|
||||
@ -37,6 +46,10 @@ public interface MutableDeviceState<T> : DeviceState<T> {
|
||||
override var value: T
|
||||
}
|
||||
|
||||
public operator fun <T> MutableDeviceState<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
this.value = value
|
||||
}
|
||||
|
||||
public var <T : Any> MutableDeviceState<T>.valueAsMeta: Meta
|
||||
get() = converter.convert(value)
|
||||
set(arg) {
|
||||
@ -216,6 +229,9 @@ private class MutableExternalState<T>(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a [DeviceState] that regularly reads and caches an external value
|
||||
*/
|
||||
public fun <T> DeviceState.Companion.external(
|
||||
scope: CoroutineScope,
|
||||
converter: MetaConverter<T>,
|
||||
|
@ -75,7 +75,7 @@ public class DeviceNameSpace(
|
||||
|
||||
browseName = newQualifiedName(propertyName)
|
||||
displayName = LocalizedText.english(propertyName)
|
||||
dataType = if (descriptor.metaDescriptor.children.isNotEmpty()) {
|
||||
dataType = if (descriptor.metaDescriptor.nodes.isNotEmpty()) {
|
||||
Identifiers.String
|
||||
} else when (descriptor.metaDescriptor.valueTypes?.first()) {
|
||||
null, ValueType.STRING, ValueType.NULL -> Identifiers.String
|
||||
|
@ -2,13 +2,8 @@
|
||||
|
||||
package space.kscience.controls.vision
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.flow.transform
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.datetime.Clock
|
||||
@ -22,6 +17,7 @@ import space.kscience.controls.spec.DevicePropertySpec
|
||||
import space.kscience.controls.spec.name
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.plotly.Plot
|
||||
import space.kscience.plotly.bar
|
||||
import space.kscience.plotly.models.Bar
|
||||
@ -183,4 +179,61 @@ public fun Plot.plotBooleanState(
|
||||
configuration: Bar.() -> Unit = {},
|
||||
): Job = bar(configuration).run {
|
||||
updateFromState(context, state, { asValue() }, maxAge, maxPoints, minPoints, sampling)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> Flow<T>.chunkedByPeriod(duration: Duration): Flow<List<T>> {
|
||||
val collector: ArrayDeque<T> = ArrayDeque<T>()
|
||||
return channelFlow {
|
||||
coroutineScope {
|
||||
launch {
|
||||
while (isActive) {
|
||||
delay(duration)
|
||||
send(ArrayList(collector))
|
||||
collector.clear()
|
||||
}
|
||||
}
|
||||
this@chunkedByPeriod.collect {
|
||||
collector.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<Instant>.averageTime(): Instant {
|
||||
val min = min()
|
||||
val max = max()
|
||||
val duration = max - min
|
||||
return min + duration / 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Average property value by [averagingInterval]. Return [missingValue] on each sample interval if no events arrived.
|
||||
*/
|
||||
@DFExperimental
|
||||
public fun Plot.plotAveragedDeviceProperty(
|
||||
device: Device,
|
||||
propertyName: String,
|
||||
missingValue: Double = 0.0,
|
||||
extractValue: Meta.() -> Double = { value?.double ?: missingValue },
|
||||
maxAge: Duration = defaultMaxAge,
|
||||
maxPoints: Int = defaultMaxPoints,
|
||||
minPoints: Int = defaultMinPoints,
|
||||
averagingInterval: Duration = defaultSampling,
|
||||
coroutineScope: CoroutineScope = device.context,
|
||||
configuration: Scatter.() -> Unit = {},
|
||||
): Job = scatter(configuration).run {
|
||||
val data = TimeData()
|
||||
device.propertyMessageFlow(propertyName).chunkedByPeriod(averagingInterval).transform { eventList ->
|
||||
if(eventList.isEmpty()){
|
||||
data.append(Clock.System.now(), missingValue.asValue())
|
||||
} else {
|
||||
val time = eventList.map { it.time }.averageTime()
|
||||
val value = eventList.map { extractValue(it.value) }.average()
|
||||
data.append(time, value.asValue())
|
||||
}
|
||||
data.trim(maxAge, maxPoints, minPoints)
|
||||
emit(data)
|
||||
}.onEach {
|
||||
it.fillPlot(x, y)
|
||||
}.launchIn(coroutineScope)
|
||||
}
|
||||
|
@ -1,29 +1,17 @@
|
||||
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.Vision
|
||||
import space.kscience.visionforge.VisionPlugin
|
||||
import space.kscience.visionforge.html.ElementVisionRenderer
|
||||
|
||||
public actual class ControlVisionPlugin : VisionPlugin(), ElementVisionRenderer {
|
||||
public actual class ControlVisionPlugin : VisionPlugin() {
|
||||
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")
|
||||
|
||||
|
@ -14,7 +14,7 @@ public final class space/kscience/controls/demo/constructor/LinearDrive : space/
|
||||
public final fun getDrive ()Lspace/kscience/controls/constructor/Drive;
|
||||
public final fun getEnd ()Lspace/kscience/controls/constructor/LimitSwitch;
|
||||
public final fun getPid ()Lspace/kscience/controls/constructor/PidRegulator;
|
||||
public final fun getPosition ()D
|
||||
public final fun getPositionState ()Lspace/kscience/controls/constructor/DoubleRangeState;
|
||||
public final fun getStart ()Lspace/kscience/controls/constructor/LimitSwitch;
|
||||
public final fun getTarget ()D
|
||||
public final fun setTarget (D)V
|
||||
|
@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
|
||||
|
||||
plugins {
|
||||
id("space.kscience.gradle.mpp")
|
||||
id("org.jetbrains.compose") version "1.5.11"
|
||||
alias(spclibs.plugins.compose)
|
||||
}
|
||||
|
||||
kscience {
|
||||
|
@ -52,8 +52,9 @@ class LinearDrive(
|
||||
val end by device(LimitSwitch.factory(state.atEndState))
|
||||
|
||||
|
||||
val position by property(state)
|
||||
var target by mutableProperty(pid.mutablePropertyAsState(Regulator.target, 0.0))
|
||||
val positionState: DoubleRangeState by property(state)
|
||||
private val targetState: MutableDeviceState<Double> by property(pid.mutablePropertyAsState(Regulator.target, 0.0))
|
||||
var target by targetState
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,4 +7,4 @@ org.gradle.parallel=true
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.jvmargs=-Xmx4096m
|
||||
|
||||
toolsVersion=0.15.2-kotlin-1.9.21
|
||||
toolsVersion=0.15.2-kotlin-1.9.22
|
@ -15,6 +15,7 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
useSerialization()
|
||||
commonMain {
|
||||
api(projects.magix.magixApi)
|
||||
api("space.kscience:dataforge-meta:$dataforgeVersion")
|
||||
|
Loading…
Reference in New Issue
Block a user