Compare commits
2 Commits
701ea8cf57
...
34f9108ef7
Author | SHA1 | Date | |
---|---|---|---|
34f9108ef7 | |||
bec075328b |
@ -6,14 +6,14 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val dataforgeVersion: String by extra("0.7.1")
|
val dataforgeVersion: String by extra("0.7.1")
|
||||||
val visionforgeVersion by extra("0.3.0-RC")
|
val visionforgeVersion by extra("0.3.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-dev-4"
|
version = "0.3.0-dev-6"
|
||||||
repositories{
|
repositories{
|
||||||
maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev")
|
maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev")
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package space.kscience.controls.constructor
|
|||||||
|
|
||||||
import space.kscience.controls.api.Device
|
import space.kscience.controls.api.Device
|
||||||
import space.kscience.controls.api.PropertyDescriptor
|
import space.kscience.controls.api.PropertyDescriptor
|
||||||
import space.kscience.controls.manager.DeviceManager
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.context.Factory
|
import space.kscience.dataforge.context.Factory
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||||
@ -18,9 +18,9 @@ import kotlin.time.Duration
|
|||||||
* A base for strongly typed device constructor blocks. Has additional delegates for type-safe devices
|
* A base for strongly typed device constructor blocks. Has additional delegates for type-safe devices
|
||||||
*/
|
*/
|
||||||
public abstract class DeviceConstructor(
|
public abstract class DeviceConstructor(
|
||||||
deviceManager: DeviceManager,
|
context: Context,
|
||||||
meta: Meta,
|
meta: Meta,
|
||||||
) : DeviceGroup(deviceManager, meta) {
|
) : DeviceGroup(context, meta) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a device, provided by a given [factory] and
|
* Register a device, provided by a given [factory] and
|
||||||
@ -57,8 +57,8 @@ public abstract class DeviceConstructor(
|
|||||||
*/
|
*/
|
||||||
public fun <T : Any> property(
|
public fun <T : Any> property(
|
||||||
state: DeviceState<T>,
|
state: DeviceState<T>,
|
||||||
nameOverride: String? = null,
|
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
|
nameOverride: String? = null,
|
||||||
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, T>> =
|
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, T>> =
|
||||||
PropertyDelegateProvider { _: DeviceConstructor, property ->
|
PropertyDelegateProvider { _: DeviceConstructor, property ->
|
||||||
val name = nameOverride ?: property.name
|
val name = nameOverride ?: property.name
|
||||||
@ -77,11 +77,12 @@ public abstract class DeviceConstructor(
|
|||||||
reader: suspend () -> T,
|
reader: suspend () -> T,
|
||||||
readInterval: Duration,
|
readInterval: Duration,
|
||||||
initialState: T,
|
initialState: T,
|
||||||
nameOverride: String? = null,
|
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
|
nameOverride: String? = null,
|
||||||
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, T>> = property(
|
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, T>> = property(
|
||||||
DeviceState.external(this, metaConverter, readInterval, initialState, reader),
|
DeviceState.external(this, metaConverter, readInterval, initialState, reader),
|
||||||
nameOverride, descriptorBuilder
|
descriptorBuilder,
|
||||||
|
nameOverride,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -90,8 +91,8 @@ public abstract class DeviceConstructor(
|
|||||||
*/
|
*/
|
||||||
public fun <T : Any> mutableProperty(
|
public fun <T : Any> mutableProperty(
|
||||||
state: MutableDeviceState<T>,
|
state: MutableDeviceState<T>,
|
||||||
nameOverride: String? = null,
|
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
|
nameOverride: String? = null,
|
||||||
): PropertyDelegateProvider<DeviceConstructor, ReadWriteProperty<DeviceConstructor, T>> =
|
): PropertyDelegateProvider<DeviceConstructor, ReadWriteProperty<DeviceConstructor, T>> =
|
||||||
PropertyDelegateProvider { _: DeviceConstructor, property ->
|
PropertyDelegateProvider { _: DeviceConstructor, property ->
|
||||||
val name = nameOverride ?: property.name
|
val name = nameOverride ?: property.name
|
||||||
@ -116,11 +117,26 @@ public abstract class DeviceConstructor(
|
|||||||
writer: suspend (T) -> Unit,
|
writer: suspend (T) -> Unit,
|
||||||
readInterval: Duration,
|
readInterval: Duration,
|
||||||
initialState: T,
|
initialState: T,
|
||||||
nameOverride: String? = null,
|
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
|
nameOverride: String? = null,
|
||||||
): PropertyDelegateProvider<DeviceConstructor, ReadWriteProperty<DeviceConstructor, T>> = mutableProperty(
|
): PropertyDelegateProvider<DeviceConstructor, ReadWriteProperty<DeviceConstructor, T>> = mutableProperty(
|
||||||
DeviceState.external(this, metaConverter, readInterval, initialState, reader, writer),
|
DeviceState.external(this, metaConverter, readInterval, initialState, reader, writer),
|
||||||
|
descriptorBuilder,
|
||||||
|
nameOverride,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and register a virtual property with optional [callback]
|
||||||
|
*/
|
||||||
|
public fun <T : Any> state(
|
||||||
|
metaConverter: MetaConverter<T>,
|
||||||
|
initialState: T,
|
||||||
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
|
nameOverride: String? = null,
|
||||||
|
callback: (T) -> Unit = {},
|
||||||
|
): PropertyDelegateProvider<DeviceConstructor, ReadWriteProperty<DeviceConstructor, T>> = mutableProperty(
|
||||||
|
DeviceState.virtual(metaConverter, initialState, callback),
|
||||||
|
descriptorBuilder,
|
||||||
nameOverride,
|
nameOverride,
|
||||||
descriptorBuilder
|
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -27,7 +27,7 @@ import kotlin.coroutines.CoroutineContext
|
|||||||
* A mutable group of devices and properties to be used for lightweight design and simulations.
|
* A mutable group of devices and properties to be used for lightweight design and simulations.
|
||||||
*/
|
*/
|
||||||
public open class DeviceGroup(
|
public open class DeviceGroup(
|
||||||
public val deviceManager: DeviceManager,
|
final override val context: Context,
|
||||||
override val meta: Meta,
|
override val meta: Meta,
|
||||||
) : DeviceHub, CachingDevice {
|
) : DeviceHub, CachingDevice {
|
||||||
|
|
||||||
@ -42,17 +42,15 @@ public open class DeviceGroup(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
override final val context: Context get() = deviceManager.context
|
|
||||||
|
|
||||||
|
|
||||||
private val sharedMessageFlow = MutableSharedFlow<DeviceMessage>()
|
private val sharedMessageFlow = MutableSharedFlow<DeviceMessage>()
|
||||||
|
|
||||||
override val messageFlow: Flow<DeviceMessage>
|
override val messageFlow: Flow<DeviceMessage>
|
||||||
get() = sharedMessageFlow
|
get() = sharedMessageFlow
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
override val coroutineContext: CoroutineContext = context.newCoroutineContext(
|
override val coroutineContext: CoroutineContext = context.newCoroutineContext(
|
||||||
SupervisorJob(context.coroutineContext[Job]) +
|
SupervisorJob(context.coroutineContext[Job]) +
|
||||||
CoroutineName("Device $this") +
|
CoroutineName("Device $id") +
|
||||||
CoroutineExceptionHandler { _, throwable ->
|
CoroutineExceptionHandler { _, throwable ->
|
||||||
context.launch {
|
context.launch {
|
||||||
sharedMessageFlow.emit(
|
sharedMessageFlow.emit(
|
||||||
@ -78,7 +76,7 @@ public open class DeviceGroup(
|
|||||||
public fun <D : Device> install(token: NameToken, device: D): D {
|
public fun <D : Device> install(token: NameToken, device: D): D {
|
||||||
require(_devices[token] == null) { "A child device with name $token already exists" }
|
require(_devices[token] == null) { "A child device with name $token already exists" }
|
||||||
//start the child device if needed
|
//start the child device if needed
|
||||||
if(lifecycleState == STARTED || lifecycleState == STARTING) launch { device.start() }
|
if (lifecycleState == STARTED || lifecycleState == STARTING) launch { device.start() }
|
||||||
_devices[token] = device
|
_devices[token] = device
|
||||||
return device
|
return device
|
||||||
}
|
}
|
||||||
@ -175,7 +173,7 @@ public fun DeviceManager.registerDeviceGroup(
|
|||||||
meta: Meta = Meta.EMPTY,
|
meta: Meta = Meta.EMPTY,
|
||||||
block: DeviceGroup.() -> Unit,
|
block: DeviceGroup.() -> Unit,
|
||||||
): DeviceGroup {
|
): DeviceGroup {
|
||||||
val group = DeviceGroup(this, meta).apply(block)
|
val group = DeviceGroup(context, meta).apply(block)
|
||||||
install(name, group)
|
install(name, group)
|
||||||
return group
|
return group
|
||||||
}
|
}
|
||||||
@ -194,7 +192,7 @@ private fun DeviceGroup.getOrCreateGroup(name: Name): DeviceGroup {
|
|||||||
when (val d = devices[token]) {
|
when (val d = devices[token]) {
|
||||||
null -> install(
|
null -> install(
|
||||||
token,
|
token,
|
||||||
DeviceGroup(deviceManager, meta[token] ?: Meta.EMPTY)
|
DeviceGroup(context, meta[token] ?: Meta.EMPTY)
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> (d as? DeviceGroup) ?: error("Device $name is not a DeviceGroup")
|
else -> (d as? DeviceGroup) ?: error("Device $name is not a DeviceGroup")
|
||||||
@ -219,6 +217,9 @@ public fun <D : Device> DeviceGroup.install(name: Name, device: D): D {
|
|||||||
public fun <D : Device> DeviceGroup.install(name: String, device: D): D =
|
public fun <D : Device> DeviceGroup.install(name: String, device: D): D =
|
||||||
install(name.parseAsName(), device)
|
install(name.parseAsName(), device)
|
||||||
|
|
||||||
|
public fun <D : Device> DeviceGroup.install(device: D): D =
|
||||||
|
install(device.id, device)
|
||||||
|
|
||||||
public fun <D : Device> Context.install(name: String, device: D): D = request(DeviceManager).install(name, device)
|
public fun <D : Device> Context.install(name: String, device: D): D = request(DeviceManager).install(name, device)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -234,7 +235,7 @@ public fun <D : Device> DeviceGroup.install(
|
|||||||
deviceMeta: Meta? = null,
|
deviceMeta: Meta? = null,
|
||||||
metaLocation: Name = name,
|
metaLocation: Name = name,
|
||||||
): D {
|
): D {
|
||||||
val newDevice = factory.build(deviceManager.context, Laminate(deviceMeta, meta[metaLocation]))
|
val newDevice = factory.build(context, Laminate(deviceMeta, meta[metaLocation]))
|
||||||
install(name, newDevice)
|
install(name, newDevice)
|
||||||
return newDevice
|
return newDevice
|
||||||
}
|
}
|
||||||
@ -284,15 +285,6 @@ public fun <T : Any> DeviceGroup.registerMutableProperty(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a virtual [MutableDeviceState], but do not register it to a device
|
|
||||||
*/
|
|
||||||
@Suppress("UnusedReceiverParameter")
|
|
||||||
public fun <T : Any> DeviceGroup.state(
|
|
||||||
converter: MetaConverter<T>,
|
|
||||||
initialValue: T,
|
|
||||||
): MutableDeviceState<T> = VirtualDeviceState<T>(converter, initialValue)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new virtual mutable state and a property based on it.
|
* Create a new virtual mutable state and a property based on it.
|
||||||
* @return the mutable state used in property
|
* @return the mutable state used in property
|
||||||
@ -302,8 +294,9 @@ public fun <T : Any> DeviceGroup.registerVirtualProperty(
|
|||||||
initialValue: T,
|
initialValue: T,
|
||||||
converter: MetaConverter<T>,
|
converter: MetaConverter<T>,
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
|
callback: (T) -> Unit = {},
|
||||||
): MutableDeviceState<T> {
|
): MutableDeviceState<T> {
|
||||||
val state = state(converter, initialValue)
|
val state = DeviceState.virtual<T>(converter, initialValue, callback)
|
||||||
registerMutableProperty(name, state, descriptorBuilder)
|
registerMutableProperty(name, state, descriptorBuilder)
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
@ -45,17 +45,49 @@ public var <T : Any> MutableDeviceState<T>.valueAsMeta: Meta
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A [MutableDeviceState] that does not correspond to a physical state
|
* A [MutableDeviceState] that does not correspond to a physical state
|
||||||
|
*
|
||||||
|
* @param callback a synchronous callback that could be used without a scope
|
||||||
*/
|
*/
|
||||||
public class VirtualDeviceState<T>(
|
private class VirtualDeviceState<T>(
|
||||||
override val converter: MetaConverter<T>,
|
override val converter: MetaConverter<T>,
|
||||||
initialValue: T,
|
initialValue: T,
|
||||||
|
private val callback: (T) -> Unit = {},
|
||||||
) : MutableDeviceState<T> {
|
) : MutableDeviceState<T> {
|
||||||
private val flow = MutableStateFlow(initialValue)
|
private val flow = MutableStateFlow(initialValue)
|
||||||
override val valueFlow: Flow<T> get() = flow
|
override val valueFlow: Flow<T> get() = flow
|
||||||
|
|
||||||
override var value: T by flow::value
|
override var value: T
|
||||||
|
get() = flow.value
|
||||||
|
set(value) {
|
||||||
|
flow.value = value
|
||||||
|
callback(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [MutableDeviceState] that does not correspond to a physical state
|
||||||
|
*
|
||||||
|
* @param callback a synchronous callback that could be used without a scope
|
||||||
|
*/
|
||||||
|
public fun <T> DeviceState.Companion.virtual(
|
||||||
|
converter: MetaConverter<T>,
|
||||||
|
initialValue: T,
|
||||||
|
callback: (T) -> Unit = {},
|
||||||
|
): MutableDeviceState<T> = VirtualDeviceState(converter, initialValue, callback)
|
||||||
|
|
||||||
|
private class StateFlowAsState<T>(
|
||||||
|
override val converter: MetaConverter<T>,
|
||||||
|
val flow: MutableStateFlow<T>,
|
||||||
|
) : MutableDeviceState<T> {
|
||||||
|
override var value: T by flow::value
|
||||||
|
override val valueFlow: Flow<T> get() = flow
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <T> MutableStateFlow<T>.asDeviceState(converter: MetaConverter<T>): DeviceState<T> =
|
||||||
|
StateFlowAsState(converter, this)
|
||||||
|
|
||||||
|
|
||||||
private open class BoundDeviceState<T>(
|
private open class BoundDeviceState<T>(
|
||||||
override val converter: MetaConverter<T>,
|
override val converter: MetaConverter<T>,
|
||||||
val device: Device,
|
val device: Device,
|
||||||
|
@ -10,6 +10,8 @@ import space.kscience.dataforge.context.ContextAware
|
|||||||
import space.kscience.dataforge.context.info
|
import space.kscience.dataforge.context.info
|
||||||
import space.kscience.dataforge.context.logger
|
import space.kscience.dataforge.context.logger
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.get
|
||||||
|
import space.kscience.dataforge.meta.string
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.misc.DfType
|
import space.kscience.dataforge.misc.DfType
|
||||||
import space.kscience.dataforge.names.parseAsName
|
import space.kscience.dataforge.names.parseAsName
|
||||||
@ -111,6 +113,11 @@ public interface Device : ContextAware, CoroutineScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner id of a device. Not necessary corresponds to the name in the parent container
|
||||||
|
*/
|
||||||
|
public val Device.id: String get() = meta["id"].string?: "device[${hashCode().toString(16)}]"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Device that caches properties values
|
* Device that caches properties values
|
||||||
*/
|
*/
|
||||||
|
@ -4,6 +4,7 @@ import kotlinx.coroutines.launch
|
|||||||
import space.kscience.controls.api.Device
|
import space.kscience.controls.api.Device
|
||||||
import space.kscience.controls.api.DeviceHub
|
import space.kscience.controls.api.DeviceHub
|
||||||
import space.kscience.controls.api.getOrNull
|
import space.kscience.controls.api.getOrNull
|
||||||
|
import space.kscience.controls.api.id
|
||||||
import space.kscience.dataforge.context.*
|
import space.kscience.dataforge.context.*
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.MutableMeta
|
import space.kscience.dataforge.meta.MutableMeta
|
||||||
@ -45,6 +46,8 @@ public fun <D : Device> DeviceManager.install(name: String, device: D): D {
|
|||||||
return device
|
return device
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun <D : Device> DeviceManager.install(device: D): D = install(device.id, device)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register and start a device built by [factory] with current [Context] and [meta].
|
* Register and start a device built by [factory] with current [Context] and [meta].
|
||||||
|
@ -9,6 +9,8 @@ import kotlinx.io.readByteArray
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform byte fragments into complete phrases using given delimiter. Not thread safe.
|
* Transform byte fragments into complete phrases using given delimiter. Not thread safe.
|
||||||
|
*
|
||||||
|
* TODO add type wrapper for phrases
|
||||||
*/
|
*/
|
||||||
public fun Flow<ByteArray>.withDelimiter(delimiter: ByteArray): Flow<ByteArray> {
|
public fun Flow<ByteArray>.withDelimiter(delimiter: ByteArray): Flow<ByteArray> {
|
||||||
require(delimiter.isNotEmpty()) { "Delimiter must not be empty" }
|
require(delimiter.isNotEmpty()) { "Delimiter must not be empty" }
|
||||||
|
@ -81,7 +81,7 @@ public suspend fun <T, D : Device> D.read(propertySpec: DevicePropertySpec<D, T>
|
|||||||
public suspend fun <T, D : DeviceBase<D>> D.readOrNull(propertySpec: DevicePropertySpec<D, T>): T? =
|
public suspend fun <T, D : DeviceBase<D>> D.readOrNull(propertySpec: DevicePropertySpec<D, T>): T? =
|
||||||
readPropertyOrNull(propertySpec.name)?.let(propertySpec.converter::metaToObject)
|
readPropertyOrNull(propertySpec.name)?.let(propertySpec.converter::metaToObject)
|
||||||
|
|
||||||
public suspend fun <T, D : Device> D.request(propertySpec: DevicePropertySpec<D, T>): T? =
|
public suspend fun <T, D : Device> D.request(propertySpec: DevicePropertySpec<D, T>): T =
|
||||||
propertySpec.converter.metaToObject(requestProperty(propertySpec.name))
|
propertySpec.converter.metaToObject(requestProperty(propertySpec.name))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,6 +18,8 @@ import space.kscience.controls.api.propertyMessageFlow
|
|||||||
import space.kscience.controls.constructor.DeviceState
|
import space.kscience.controls.constructor.DeviceState
|
||||||
import space.kscience.controls.manager.clock
|
import space.kscience.controls.manager.clock
|
||||||
import space.kscience.controls.misc.ValueWithTime
|
import space.kscience.controls.misc.ValueWithTime
|
||||||
|
import space.kscience.controls.spec.DevicePropertySpec
|
||||||
|
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.plotly.Plot
|
import space.kscience.plotly.Plot
|
||||||
@ -28,8 +30,8 @@ import space.kscience.plotly.models.Trace
|
|||||||
import space.kscience.plotly.models.TraceValues
|
import space.kscience.plotly.models.TraceValues
|
||||||
import space.kscience.plotly.scatter
|
import space.kscience.plotly.scatter
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.hours
|
import kotlin.time.Duration.Companion.minutes
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
private var TraceValues.values: List<Value>
|
private var TraceValues.values: List<Value>
|
||||||
get() = value?.list ?: emptyList()
|
get() = value?.list ?: emptyList()
|
||||||
@ -82,6 +84,11 @@ private class TimeData(private var points: MutableList<ValueWithTime<Value>> = m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val defaultMaxAge get() = 10.minutes
|
||||||
|
private val defaultMaxPoints get() = 800
|
||||||
|
private val defaultMinPoints get() = 400
|
||||||
|
private val defaultSampling get() = 1.seconds
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a trace that shows a [Device] property change over time. Show only latest [maxPoints] .
|
* Add a trace that shows a [Device] property change over time. Show only latest [maxPoints] .
|
||||||
* @return a [Job] that handles the listener
|
* @return a [Job] that handles the listener
|
||||||
@ -90,10 +97,10 @@ public fun Plot.plotDeviceProperty(
|
|||||||
device: Device,
|
device: Device,
|
||||||
propertyName: String,
|
propertyName: String,
|
||||||
extractValue: Meta.() -> Value = { value ?: Null },
|
extractValue: Meta.() -> Value = { value ?: Null },
|
||||||
maxAge: Duration = 1.hours,
|
maxAge: Duration = defaultMaxAge,
|
||||||
maxPoints: Int = 800,
|
maxPoints: Int = defaultMaxPoints,
|
||||||
minPoints: Int = 400,
|
minPoints: Int = defaultMinPoints,
|
||||||
sampling: Duration = 10.milliseconds,
|
sampling: Duration = defaultSampling,
|
||||||
coroutineScope: CoroutineScope = device.context,
|
coroutineScope: CoroutineScope = device.context,
|
||||||
configuration: Scatter.() -> Unit = {},
|
configuration: Scatter.() -> Unit = {},
|
||||||
): Job = scatter(configuration).run {
|
): Job = scatter(configuration).run {
|
||||||
@ -108,14 +115,27 @@ public fun Plot.plotDeviceProperty(
|
|||||||
}.launchIn(coroutineScope)
|
}.launchIn(coroutineScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun Plot.plotDeviceProperty(
|
||||||
|
device: Device,
|
||||||
|
property: DevicePropertySpec<*, Number>,
|
||||||
|
maxAge: Duration = defaultMaxAge,
|
||||||
|
maxPoints: Int = defaultMaxPoints,
|
||||||
|
minPoints: Int = defaultMinPoints,
|
||||||
|
sampling: Duration = defaultSampling,
|
||||||
|
coroutineScope: CoroutineScope = device.context,
|
||||||
|
configuration: Scatter.() -> Unit = {},
|
||||||
|
): Job = plotDeviceProperty(
|
||||||
|
device, property.name, { value ?: Null }, maxAge, maxPoints, minPoints, sampling, coroutineScope, configuration
|
||||||
|
)
|
||||||
|
|
||||||
private fun <T> Trace.updateFromState(
|
private fun <T> Trace.updateFromState(
|
||||||
context: Context,
|
context: Context,
|
||||||
state: DeviceState<T>,
|
state: DeviceState<T>,
|
||||||
extractValue: T.() -> Value = { state.converter.objectToMeta(this).value ?: space.kscience.dataforge.meta.Null },
|
extractValue: T.() -> Value,
|
||||||
maxAge: Duration = 1.hours,
|
maxAge: Duration,
|
||||||
maxPoints: Int = 800,
|
maxPoints: Int,
|
||||||
minPoints: Int = 400,
|
minPoints: Int,
|
||||||
sampling: Duration = 10.milliseconds,
|
sampling: Duration,
|
||||||
): Job {
|
): Job {
|
||||||
val clock = context.clock
|
val clock = context.clock
|
||||||
val data = TimeData()
|
val data = TimeData()
|
||||||
@ -131,10 +151,10 @@ public fun <T> Plot.plotDeviceState(
|
|||||||
context: Context,
|
context: Context,
|
||||||
state: DeviceState<T>,
|
state: DeviceState<T>,
|
||||||
extractValue: T.() -> Value = { state.converter.objectToMeta(this).value ?: Null },
|
extractValue: T.() -> Value = { state.converter.objectToMeta(this).value ?: Null },
|
||||||
maxAge: Duration = 1.hours,
|
maxAge: Duration = defaultMaxAge,
|
||||||
maxPoints: Int = 800,
|
maxPoints: Int = defaultMaxPoints,
|
||||||
minPoints: Int = 400,
|
minPoints: Int = defaultMinPoints,
|
||||||
sampling: Duration = 10.milliseconds,
|
sampling: Duration = defaultSampling,
|
||||||
configuration: Scatter.() -> Unit = {},
|
configuration: Scatter.() -> Unit = {},
|
||||||
): Job = scatter(configuration).run {
|
): Job = scatter(configuration).run {
|
||||||
updateFromState(context, state, extractValue, maxAge, maxPoints, minPoints, sampling)
|
updateFromState(context, state, extractValue, maxAge, maxPoints, minPoints, sampling)
|
||||||
@ -144,10 +164,10 @@ public fun <T> Plot.plotDeviceState(
|
|||||||
public fun Plot.plotNumberState(
|
public fun Plot.plotNumberState(
|
||||||
context: Context,
|
context: Context,
|
||||||
state: DeviceState<out Number>,
|
state: DeviceState<out Number>,
|
||||||
maxAge: Duration = 1.hours,
|
maxAge: Duration = defaultMaxAge,
|
||||||
maxPoints: Int = 800,
|
maxPoints: Int = defaultMaxPoints,
|
||||||
minPoints: Int = 400,
|
minPoints: Int = defaultMinPoints,
|
||||||
sampling: Duration = 10.milliseconds,
|
sampling: Duration = defaultSampling,
|
||||||
configuration: Scatter.() -> Unit = {},
|
configuration: Scatter.() -> Unit = {},
|
||||||
): Job = scatter(configuration).run {
|
): Job = scatter(configuration).run {
|
||||||
updateFromState(context, state, { asValue() }, maxAge, maxPoints, minPoints, sampling)
|
updateFromState(context, state, { asValue() }, maxAge, maxPoints, minPoints, sampling)
|
||||||
@ -157,10 +177,10 @@ public fun Plot.plotNumberState(
|
|||||||
public fun Plot.plotBooleanState(
|
public fun Plot.plotBooleanState(
|
||||||
context: Context,
|
context: Context,
|
||||||
state: DeviceState<Boolean>,
|
state: DeviceState<Boolean>,
|
||||||
maxAge: Duration = 1.hours,
|
maxAge: Duration = defaultMaxAge,
|
||||||
maxPoints: Int = 800,
|
maxPoints: Int = defaultMaxPoints,
|
||||||
minPoints: Int = 400,
|
minPoints: Int = defaultMinPoints,
|
||||||
sampling: Duration = 10.milliseconds,
|
sampling: Duration = defaultSampling,
|
||||||
configuration: Bar.() -> Unit = {},
|
configuration: Bar.() -> Unit = {},
|
||||||
): Job = bar(configuration).run {
|
): Job = bar(configuration).run {
|
||||||
updateFromState(context, state, { asValue() }, maxAge, maxPoints, minPoints, sampling)
|
updateFromState(context, state, { asValue() }, maxAge, maxPoints, minPoints, sampling)
|
||||||
|
@ -26,7 +26,6 @@ import space.kscience.controls.vision.plotDeviceProperty
|
|||||||
import space.kscience.controls.vision.plotNumberState
|
import space.kscience.controls.vision.plotNumberState
|
||||||
import space.kscience.controls.vision.showDashboard
|
import space.kscience.controls.vision.showDashboard
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.context.request
|
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.plotly.models.ScatterMode
|
import space.kscience.plotly.models.ScatterMode
|
||||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||||
@ -44,7 +43,7 @@ class LinearDrive(
|
|||||||
mass: Double,
|
mass: Double,
|
||||||
pidParameters: PidParameters,
|
pidParameters: PidParameters,
|
||||||
meta: Meta = Meta.EMPTY,
|
meta: Meta = Meta.EMPTY,
|
||||||
) : DeviceConstructor(context.request(DeviceManager), meta) {
|
) : DeviceConstructor(context, meta) {
|
||||||
|
|
||||||
val drive by device(VirtualDrive.factory(mass, state))
|
val drive by device(VirtualDrive.factory(mass, state))
|
||||||
val pid by device(PidRegulator(drive, pidParameters))
|
val pid by device(PidRegulator(drive, pidParameters))
|
||||||
|
Loading…
Reference in New Issue
Block a user