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 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 ktorVersion: String by extra(space.kscience.gradle.KScienceVersions.ktorVersion)
|
||||||
val rsocketVersion by extra("0.15.4")
|
val rsocketVersion by extra("0.15.4")
|
||||||
val xodusVersion by extra("2.0.1")
|
val xodusVersion by extra("2.0.1")
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = "space.kscience"
|
group = "space.kscience"
|
||||||
version = "0.3.0"
|
version = "0.3.1-dev-1"
|
||||||
repositories{
|
repositories{
|
||||||
maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev")
|
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 space.kscience.dataforge.names.asName
|
||||||
import kotlin.properties.PropertyDelegateProvider
|
import kotlin.properties.PropertyDelegateProvider
|
||||||
import kotlin.properties.ReadOnlyProperty
|
import kotlin.properties.ReadOnlyProperty
|
||||||
import kotlin.properties.ReadWriteProperty
|
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
@ -55,17 +54,17 @@ public abstract class DeviceConstructor(
|
|||||||
/**
|
/**
|
||||||
* Register a property and provide a direct reader for it
|
* Register a property and provide a direct reader for it
|
||||||
*/
|
*/
|
||||||
public fun <T : Any> property(
|
public fun <T : Any, S: DeviceState<T>> property(
|
||||||
state: DeviceState<T>,
|
state: S,
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
nameOverride: String? = null,
|
nameOverride: String? = null,
|
||||||
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, T>> =
|
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, S>> =
|
||||||
PropertyDelegateProvider { _: DeviceConstructor, property ->
|
PropertyDelegateProvider { _: DeviceConstructor, property ->
|
||||||
val name = nameOverride ?: property.name
|
val name = nameOverride ?: property.name
|
||||||
val descriptor = PropertyDescriptor(name).apply(descriptorBuilder)
|
val descriptor = PropertyDescriptor(name).apply(descriptorBuilder)
|
||||||
registerProperty(descriptor, state)
|
registerProperty(descriptor, state)
|
||||||
ReadOnlyProperty { _: DeviceConstructor, _ ->
|
ReadOnlyProperty { _: DeviceConstructor, _ ->
|
||||||
state.value
|
state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,37 +78,14 @@ public abstract class DeviceConstructor(
|
|||||||
initialState: T,
|
initialState: T,
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
nameOverride: String? = null,
|
nameOverride: String? = null,
|
||||||
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, T>> = property(
|
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, DeviceState<T>>> = property(
|
||||||
DeviceState.external(this, metaConverter, readInterval, initialState, reader),
|
DeviceState.external(this, metaConverter, readInterval, initialState, reader),
|
||||||
descriptorBuilder,
|
descriptorBuilder,
|
||||||
nameOverride,
|
nameOverride,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a mutable property and provide a direct reader for it
|
* Register a mutable external state as a property
|
||||||
*/
|
|
||||||
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
|
|
||||||
*/
|
*/
|
||||||
public fun <T : Any> mutableProperty(
|
public fun <T : Any> mutableProperty(
|
||||||
metaConverter: MetaConverter<T>,
|
metaConverter: MetaConverter<T>,
|
||||||
@ -119,22 +95,22 @@ public abstract class DeviceConstructor(
|
|||||||
initialState: T,
|
initialState: T,
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
nameOverride: String? = null,
|
nameOverride: String? = null,
|
||||||
): PropertyDelegateProvider<DeviceConstructor, ReadWriteProperty<DeviceConstructor, T>> = mutableProperty(
|
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, MutableDeviceState<T>>> = property(
|
||||||
DeviceState.external(this, metaConverter, readInterval, initialState, reader, writer),
|
DeviceState.external(this, metaConverter, readInterval, initialState, reader, writer),
|
||||||
descriptorBuilder,
|
descriptorBuilder,
|
||||||
nameOverride,
|
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>,
|
metaConverter: MetaConverter<T>,
|
||||||
initialState: T,
|
initialState: T,
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
nameOverride: String? = null,
|
nameOverride: String? = null,
|
||||||
callback: (T) -> Unit = {},
|
callback: (T) -> Unit = {},
|
||||||
): PropertyDelegateProvider<DeviceConstructor, ReadWriteProperty<DeviceConstructor, T>> = mutableProperty(
|
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, MutableDeviceState<T>>> = property(
|
||||||
DeviceState.virtual(metaConverter, initialState, callback),
|
DeviceState.virtual(metaConverter, initialState, callback),
|
||||||
descriptorBuilder,
|
descriptorBuilder,
|
||||||
nameOverride,
|
nameOverride,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package space.kscience.controls.constructor
|
package space.kscience.controls.constructor
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -11,6 +12,7 @@ import space.kscience.controls.spec.MutableDevicePropertySpec
|
|||||||
import space.kscience.controls.spec.name
|
import space.kscience.controls.spec.name
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.MetaConverter
|
import space.kscience.dataforge.meta.MetaConverter
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
import kotlin.time.Duration
|
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 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
|
* A mutable state of a device
|
||||||
@ -37,6 +46,10 @@ public interface MutableDeviceState<T> : DeviceState<T> {
|
|||||||
override var value: 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
|
public var <T : Any> MutableDeviceState<T>.valueAsMeta: Meta
|
||||||
get() = converter.convert(value)
|
get() = converter.convert(value)
|
||||||
set(arg) {
|
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(
|
public fun <T> DeviceState.Companion.external(
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
converter: MetaConverter<T>,
|
converter: MetaConverter<T>,
|
||||||
|
@ -75,7 +75,7 @@ public class DeviceNameSpace(
|
|||||||
|
|
||||||
browseName = newQualifiedName(propertyName)
|
browseName = newQualifiedName(propertyName)
|
||||||
displayName = LocalizedText.english(propertyName)
|
displayName = LocalizedText.english(propertyName)
|
||||||
dataType = if (descriptor.metaDescriptor.children.isNotEmpty()) {
|
dataType = if (descriptor.metaDescriptor.nodes.isNotEmpty()) {
|
||||||
Identifiers.String
|
Identifiers.String
|
||||||
} else when (descriptor.metaDescriptor.valueTypes?.first()) {
|
} else when (descriptor.metaDescriptor.valueTypes?.first()) {
|
||||||
null, ValueType.STRING, ValueType.NULL -> Identifiers.String
|
null, ValueType.STRING, ValueType.NULL -> Identifiers.String
|
||||||
|
@ -2,13 +2,8 @@
|
|||||||
|
|
||||||
package space.kscience.controls.vision
|
package space.kscience.controls.vision
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.flow.*
|
||||||
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.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
@ -22,6 +17,7 @@ import space.kscience.controls.spec.DevicePropertySpec
|
|||||||
import space.kscience.controls.spec.name
|
import space.kscience.controls.spec.name
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.plotly.Plot
|
import space.kscience.plotly.Plot
|
||||||
import space.kscience.plotly.bar
|
import space.kscience.plotly.bar
|
||||||
import space.kscience.plotly.models.Bar
|
import space.kscience.plotly.models.Bar
|
||||||
@ -184,3 +180,60 @@ public fun Plot.plotBooleanState(
|
|||||||
): Job = bar(configuration).run {
|
): Job = bar(configuration).run {
|
||||||
updateFromState(context, state, { asValue() }, maxAge, maxPoints, minPoints, sampling)
|
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
|
package space.kscience.controls.vision
|
||||||
|
|
||||||
import kotlinx.serialization.modules.SerializersModule
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
import org.w3c.dom.Element
|
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.context.PluginFactory
|
import space.kscience.dataforge.context.PluginFactory
|
||||||
import space.kscience.dataforge.context.PluginTag
|
import space.kscience.dataforge.context.PluginTag
|
||||||
import space.kscience.dataforge.meta.Meta
|
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.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 tag: PluginTag get() = Companion.tag
|
||||||
|
|
||||||
override val visionSerializersModule: SerializersModule get() = controlsVisionSerializersModule
|
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> {
|
public actual companion object : PluginFactory<ControlVisionPlugin> {
|
||||||
override val tag: PluginTag = PluginTag("controls.vision")
|
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 getDrive ()Lspace/kscience/controls/constructor/Drive;
|
||||||
public final fun getEnd ()Lspace/kscience/controls/constructor/LimitSwitch;
|
public final fun getEnd ()Lspace/kscience/controls/constructor/LimitSwitch;
|
||||||
public final fun getPid ()Lspace/kscience/controls/constructor/PidRegulator;
|
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 getStart ()Lspace/kscience/controls/constructor/LimitSwitch;
|
||||||
public final fun getTarget ()D
|
public final fun getTarget ()D
|
||||||
public final fun setTarget (D)V
|
public final fun setTarget (D)V
|
||||||
|
@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("space.kscience.gradle.mpp")
|
id("space.kscience.gradle.mpp")
|
||||||
id("org.jetbrains.compose") version "1.5.11"
|
alias(spclibs.plugins.compose)
|
||||||
}
|
}
|
||||||
|
|
||||||
kscience {
|
kscience {
|
||||||
|
@ -52,8 +52,9 @@ class LinearDrive(
|
|||||||
val end by device(LimitSwitch.factory(state.atEndState))
|
val end by device(LimitSwitch.factory(state.atEndState))
|
||||||
|
|
||||||
|
|
||||||
val position by property(state)
|
val positionState: DoubleRangeState by property(state)
|
||||||
var target by mutableProperty(pid.mutablePropertyAsState(Regulator.target, 0.0))
|
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.configureondemand=true
|
||||||
org.gradle.jvmargs=-Xmx4096m
|
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()
|
jvm()
|
||||||
js()
|
js()
|
||||||
native()
|
native()
|
||||||
|
useSerialization()
|
||||||
commonMain {
|
commonMain {
|
||||||
api(projects.magix.magixApi)
|
api(projects.magix.magixApi)
|
||||||
api("space.kscience:dataforge-meta:$dataforgeVersion")
|
api("space.kscience:dataforge-meta:$dataforgeVersion")
|
||||||
|
Loading…
Reference in New Issue
Block a user