Test device constructor
This commit is contained in:
parent
825f1a4d04
commit
53fc240c75
@ -33,7 +33,7 @@ public abstract class DeviceConstructor(
|
|||||||
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, D>> =
|
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, D>> =
|
||||||
PropertyDelegateProvider { _: DeviceConstructor, property: KProperty<*> ->
|
PropertyDelegateProvider { _: DeviceConstructor, property: KProperty<*> ->
|
||||||
val name = nameOverride ?: property.name.asName()
|
val name = nameOverride ?: property.name.asName()
|
||||||
val device = registerDevice(name, factory, meta, metaLocation ?: name)
|
val device = install(name, factory, meta, metaLocation ?: name)
|
||||||
ReadOnlyProperty { _: DeviceConstructor, _ ->
|
ReadOnlyProperty { _: DeviceConstructor, _ ->
|
||||||
device
|
device
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ public abstract class DeviceConstructor(
|
|||||||
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, D>> =
|
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, D>> =
|
||||||
PropertyDelegateProvider { _: DeviceConstructor, property: KProperty<*> ->
|
PropertyDelegateProvider { _: DeviceConstructor, property: KProperty<*> ->
|
||||||
val name = nameOverride ?: property.name.asName()
|
val name = nameOverride ?: property.name.asName()
|
||||||
registerDevice(name, device)
|
install(name, device)
|
||||||
ReadOnlyProperty { _: DeviceConstructor, _ ->
|
ReadOnlyProperty { _: DeviceConstructor, _ ->
|
||||||
device
|
device
|
||||||
}
|
}
|
||||||
@ -58,7 +58,7 @@ public abstract class DeviceConstructor(
|
|||||||
public fun <T : Any> property(
|
public fun <T : Any> property(
|
||||||
state: DeviceState<T>,
|
state: DeviceState<T>,
|
||||||
nameOverride: String? = null,
|
nameOverride: String? = null,
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit,
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
): 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
|
||||||
@ -78,7 +78,7 @@ public abstract class DeviceConstructor(
|
|||||||
readInterval: Duration,
|
readInterval: Duration,
|
||||||
initialState: T,
|
initialState: T,
|
||||||
nameOverride: String? = null,
|
nameOverride: String? = null,
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit,
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
): 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
|
nameOverride, descriptorBuilder
|
||||||
@ -91,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,
|
nameOverride: String? = null,
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit,
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<DeviceConstructor, T>> =
|
): PropertyDelegateProvider<DeviceConstructor, ReadWriteProperty<DeviceConstructor, T>> =
|
||||||
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)
|
||||||
@ -117,8 +117,8 @@ public abstract class DeviceConstructor(
|
|||||||
readInterval: Duration,
|
readInterval: Duration,
|
||||||
initialState: T,
|
initialState: T,
|
||||||
nameOverride: String? = null,
|
nameOverride: String? = null,
|
||||||
descriptorBuilder: PropertyDescriptor.() -> Unit,
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
): PropertyDelegateProvider<DeviceConstructor, ReadOnlyProperty<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),
|
||||||
nameOverride,
|
nameOverride,
|
||||||
descriptorBuilder
|
descriptorBuilder
|
||||||
|
@ -3,6 +3,8 @@ package space.kscience.controls.constructor
|
|||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import space.kscience.controls.api.*
|
import space.kscience.controls.api.*
|
||||||
import space.kscience.controls.api.DeviceLifecycleState.*
|
import space.kscience.controls.api.DeviceLifecycleState.*
|
||||||
import space.kscience.controls.manager.DeviceManager
|
import space.kscience.controls.manager.DeviceManager
|
||||||
@ -40,25 +42,30 @@ public open class DeviceGroup(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
override val context: Context get() = deviceManager.context
|
override final val context: Context get() = deviceManager.context
|
||||||
|
|
||||||
override val coroutineContext: CoroutineContext by lazy {
|
|
||||||
context.newCoroutineContext(
|
private val sharedMessageFlow = MutableSharedFlow<DeviceMessage>()
|
||||||
SupervisorJob(context.coroutineContext[Job]) +
|
|
||||||
CoroutineName("Device $this") +
|
override val messageFlow: Flow<DeviceMessage>
|
||||||
CoroutineExceptionHandler { _, throwable ->
|
get() = sharedMessageFlow
|
||||||
launch {
|
|
||||||
sharedMessageFlow.emit(
|
override val coroutineContext: CoroutineContext = context.newCoroutineContext(
|
||||||
DeviceErrorMessage(
|
SupervisorJob(context.coroutineContext[Job]) +
|
||||||
errorMessage = throwable.message,
|
CoroutineName("Device $this") +
|
||||||
errorType = throwable::class.simpleName,
|
CoroutineExceptionHandler { _, throwable ->
|
||||||
errorStackTrace = throwable.stackTraceToString()
|
context.launch {
|
||||||
)
|
sharedMessageFlow.emit(
|
||||||
|
DeviceErrorMessage(
|
||||||
|
errorMessage = throwable.message,
|
||||||
|
errorType = throwable::class.simpleName,
|
||||||
|
errorStackTrace = throwable.stackTraceToString()
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
|
|
||||||
private val _devices = hashMapOf<NameToken, Device>()
|
private val _devices = hashMapOf<NameToken, Device>()
|
||||||
|
|
||||||
@ -68,14 +75,10 @@ public open class DeviceGroup(
|
|||||||
* Register and initialize (synchronize child's lifecycle state with group state) a new device in this group
|
* Register and initialize (synchronize child's lifecycle state with group state) a new device in this group
|
||||||
*/
|
*/
|
||||||
@OptIn(DFExperimental::class)
|
@OptIn(DFExperimental::class)
|
||||||
public fun <D : Device> registerDevice(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 or stop the child if needed
|
//start the child device if needed
|
||||||
when (lifecycleState) {
|
if(lifecycleState == STARTED || lifecycleState == STARTING) launch { device.start() }
|
||||||
STARTING, STARTED -> launch { device.start() }
|
|
||||||
STOPPED -> device.stop()
|
|
||||||
ERROR -> {}
|
|
||||||
}
|
|
||||||
_devices[token] = device
|
_devices[token] = device
|
||||||
return device
|
return device
|
||||||
}
|
}
|
||||||
@ -89,6 +92,14 @@ public open class DeviceGroup(
|
|||||||
val name = descriptor.name.parseAsName()
|
val name = descriptor.name.parseAsName()
|
||||||
require(properties[name] == null) { "Can't add property with name $name. It already exists." }
|
require(properties[name] == null) { "Can't add property with name $name. It already exists." }
|
||||||
properties[name] = Property(state, descriptor)
|
properties[name] = Property(state, descriptor)
|
||||||
|
state.metaFlow.onEach {
|
||||||
|
sharedMessageFlow.emit(
|
||||||
|
PropertyChangedMessage(
|
||||||
|
descriptor.name,
|
||||||
|
it
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}.launchIn(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val actions: MutableMap<Name, Action> = hashMapOf()
|
private val actions: MutableMap<Name, Action> = hashMapOf()
|
||||||
@ -115,10 +126,6 @@ public open class DeviceGroup(
|
|||||||
property.valueAsMeta = value
|
property.valueAsMeta = value
|
||||||
}
|
}
|
||||||
|
|
||||||
private val sharedMessageFlow = MutableSharedFlow<DeviceMessage>()
|
|
||||||
|
|
||||||
override val messageFlow: Flow<DeviceMessage>
|
|
||||||
get() = sharedMessageFlow
|
|
||||||
|
|
||||||
override suspend fun execute(actionName: String, argument: Meta?): Meta? {
|
override suspend fun execute(actionName: String, argument: Meta?): Meta? {
|
||||||
val action = actions[actionName] ?: error("Action with name $actionName not found")
|
val action = actions[actionName] ?: error("Action with name $actionName not found")
|
||||||
@ -185,7 +192,7 @@ private fun DeviceGroup.getOrCreateGroup(name: Name): DeviceGroup {
|
|||||||
1 -> {
|
1 -> {
|
||||||
val token = name.first()
|
val token = name.first()
|
||||||
when (val d = devices[token]) {
|
when (val d = devices[token]) {
|
||||||
null -> registerDevice(
|
null -> install(
|
||||||
token,
|
token,
|
||||||
DeviceGroup(deviceManager, meta[token] ?: Meta.EMPTY)
|
DeviceGroup(deviceManager, meta[token] ?: Meta.EMPTY)
|
||||||
)
|
)
|
||||||
@ -201,15 +208,18 @@ private fun DeviceGroup.getOrCreateGroup(name: Name): DeviceGroup {
|
|||||||
/**
|
/**
|
||||||
* Register a device at given [name] path
|
* Register a device at given [name] path
|
||||||
*/
|
*/
|
||||||
public fun <D : Device> DeviceGroup.registerDevice(name: Name, device: D): D {
|
public fun <D : Device> DeviceGroup.install(name: Name, device: D): D {
|
||||||
return when (name.length) {
|
return when (name.length) {
|
||||||
0 -> error("Can't use empty name for a child device")
|
0 -> error("Can't use empty name for a child device")
|
||||||
1 -> registerDevice(name.first(), device)
|
1 -> install(name.first(), device)
|
||||||
else -> getOrCreateGroup(name.cutLast()).registerDevice(name.tokens.last(), device)
|
else -> getOrCreateGroup(name.cutLast()).install(name.tokens.last(), device)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <D : Device> DeviceGroup.registerDevice(name: String, device: D): D = registerDevice(name.parseAsName(), device)
|
public fun <D : Device> DeviceGroup.install(name: String, device: D): D =
|
||||||
|
install(name.parseAsName(), device)
|
||||||
|
|
||||||
|
public fun <D : Device> Context.install(name: String, device: D): D = request(DeviceManager).install(name, device)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a device creating intermediate groups if necessary. If device with given [name] already exists, throws an error.
|
* Add a device creating intermediate groups if necessary. If device with given [name] already exists, throws an error.
|
||||||
@ -218,23 +228,23 @@ public fun <D : Device> DeviceGroup.registerDevice(name: String, device: D): D =
|
|||||||
* @param deviceMeta meta override for this specific device
|
* @param deviceMeta meta override for this specific device
|
||||||
* @param metaLocation location of the template meta in parent group meta
|
* @param metaLocation location of the template meta in parent group meta
|
||||||
*/
|
*/
|
||||||
public fun <D : Device> DeviceGroup.registerDevice(
|
public fun <D : Device> DeviceGroup.install(
|
||||||
name: Name,
|
name: Name,
|
||||||
factory: Factory<D>,
|
factory: Factory<D>,
|
||||||
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(deviceManager.context, Laminate(deviceMeta, meta[metaLocation]))
|
||||||
registerDevice(name, newDevice)
|
install(name, newDevice)
|
||||||
return newDevice
|
return newDevice
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <D : Device> DeviceGroup.registerDevice(
|
public fun <D : Device> DeviceGroup.install(
|
||||||
name: String,
|
name: String,
|
||||||
factory: Factory<D>,
|
factory: Factory<D>,
|
||||||
metaLocation: Name = name.parseAsName(),
|
metaLocation: Name = name.parseAsName(),
|
||||||
metaBuilder: (MutableMeta.() -> Unit)? = null,
|
metaBuilder: (MutableMeta.() -> Unit)? = null,
|
||||||
): D = registerDevice(name.parseAsName(), factory, metaBuilder?.let { Meta(it) }, metaLocation)
|
): D = install(name.parseAsName(), factory, metaBuilder?.let { Meta(it) }, metaLocation)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create or edit a group with a given [name].
|
* Create or edit a group with a given [name].
|
||||||
|
@ -75,7 +75,7 @@ private open class BoundDeviceState<T>(
|
|||||||
/**
|
/**
|
||||||
* Bind a read-only [DeviceState] to a [Device] property
|
* Bind a read-only [DeviceState] to a [Device] property
|
||||||
*/
|
*/
|
||||||
public suspend fun <T> Device.bindStateToProperty(
|
public suspend fun <T> Device.propertyAsState(
|
||||||
propertyName: String,
|
propertyName: String,
|
||||||
metaConverter: MetaConverter<T>,
|
metaConverter: MetaConverter<T>,
|
||||||
): DeviceState<T> {
|
): DeviceState<T> {
|
||||||
@ -83,9 +83,9 @@ public suspend fun <T> Device.bindStateToProperty(
|
|||||||
return BoundDeviceState(metaConverter, this, propertyName, initialValue)
|
return BoundDeviceState(metaConverter, this, propertyName, initialValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
public suspend fun <D : Device, T> D.bindStateToProperty(
|
public suspend fun <D : Device, T> D.propertyAsState(
|
||||||
propertySpec: DevicePropertySpec<D, T>,
|
propertySpec: DevicePropertySpec<D, T>,
|
||||||
): DeviceState<T> = bindStateToProperty(propertySpec.name, propertySpec.converter)
|
): DeviceState<T> = propertyAsState(propertySpec.name, propertySpec.converter)
|
||||||
|
|
||||||
public fun <T, R> DeviceState<T>.map(
|
public fun <T, R> DeviceState<T>.map(
|
||||||
converter: MetaConverter<R>, mapper: (T) -> R,
|
converter: MetaConverter<R>, mapper: (T) -> R,
|
||||||
@ -113,17 +113,28 @@ private class MutableBoundDeviceState<T>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public suspend fun <T> Device.bindMutableStateToProperty(
|
public fun <T> Device.mutablePropertyAsState(
|
||||||
|
propertyName: String,
|
||||||
|
metaConverter: MetaConverter<T>,
|
||||||
|
initialValue: T,
|
||||||
|
): MutableDeviceState<T> = MutableBoundDeviceState(metaConverter, this, propertyName, initialValue)
|
||||||
|
|
||||||
|
public suspend fun <T> Device.mutablePropertyAsState(
|
||||||
propertyName: String,
|
propertyName: String,
|
||||||
metaConverter: MetaConverter<T>,
|
metaConverter: MetaConverter<T>,
|
||||||
): MutableDeviceState<T> {
|
): MutableDeviceState<T> {
|
||||||
val initialValue = metaConverter.metaToObject(readProperty(propertyName)) ?: error("Conversion of property failed")
|
val initialValue = metaConverter.metaToObject(readProperty(propertyName)) ?: error("Conversion of property failed")
|
||||||
return MutableBoundDeviceState(metaConverter, this, propertyName, initialValue)
|
return mutablePropertyAsState(propertyName, metaConverter, initialValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
public suspend fun <D : Device, T> D.bindMutableStateToProperty(
|
public suspend fun <D : Device, T> D.mutablePropertyAsState(
|
||||||
propertySpec: MutableDevicePropertySpec<D, T>,
|
propertySpec: MutableDevicePropertySpec<D, T>,
|
||||||
): MutableDeviceState<T> = bindMutableStateToProperty(propertySpec.name, propertySpec.converter)
|
): MutableDeviceState<T> = mutablePropertyAsState(propertySpec.name, propertySpec.converter)
|
||||||
|
|
||||||
|
public fun <D : Device, T> D.mutablePropertyAsState(
|
||||||
|
propertySpec: MutableDevicePropertySpec<D, T>,
|
||||||
|
initialValue: T,
|
||||||
|
): MutableDeviceState<T> = mutablePropertyAsState(propertySpec.name, propertySpec.converter, initialValue)
|
||||||
|
|
||||||
|
|
||||||
private open class ExternalState<T>(
|
private open class ExternalState<T>(
|
||||||
|
@ -96,4 +96,4 @@ public class VirtualDrive(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public suspend fun Drive.stateOfForce(): MutableDeviceState<Double> = bindMutableStateToProperty(Drive.force)
|
public suspend fun Drive.stateOfForce(): MutableDeviceState<Double> = mutablePropertyAsState(Drive.force)
|
||||||
|
@ -79,4 +79,4 @@ public fun DeviceGroup.pid(
|
|||||||
name: String,
|
name: String,
|
||||||
drive: Drive,
|
drive: Drive,
|
||||||
pidParameters: PidParameters,
|
pidParameters: PidParameters,
|
||||||
): PidRegulator = registerDevice(name, PidRegulator(drive, pidParameters))
|
): PidRegulator = install(name, PidRegulator(drive, pidParameters))
|
@ -72,23 +72,21 @@ public abstract class DeviceBase<D : Device>(
|
|||||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||||
)
|
)
|
||||||
|
|
||||||
override val coroutineContext: CoroutineContext by lazy {
|
override val coroutineContext: CoroutineContext = context.newCoroutineContext(
|
||||||
context.newCoroutineContext(
|
SupervisorJob(context.coroutineContext[Job]) +
|
||||||
SupervisorJob(context.coroutineContext[Job]) +
|
CoroutineName("Device $this") +
|
||||||
CoroutineName("Device $this") +
|
CoroutineExceptionHandler { _, throwable ->
|
||||||
CoroutineExceptionHandler { _, throwable ->
|
launch {
|
||||||
launch {
|
sharedMessageFlow.emit(
|
||||||
sharedMessageFlow.emit(
|
DeviceErrorMessage(
|
||||||
DeviceErrorMessage(
|
errorMessage = throwable.message,
|
||||||
errorMessage = throwable.message,
|
errorType = throwable::class.simpleName,
|
||||||
errorType = throwable::class.simpleName,
|
errorStackTrace = throwable.stackTraceToString()
|
||||||
errorStackTrace = throwable.stackTraceToString()
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("space.kscience.gradle.mpp")
|
id("space.kscience.gradle.mpp")
|
||||||
application
|
application
|
||||||
@ -21,3 +23,5 @@ kscience {
|
|||||||
application {
|
application {
|
||||||
mainClass.set("space.kscience.controls.demo.constructor.MainKt")
|
mainClass.set("space.kscience.controls.demo.constructor.MainKt")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kotlin.explicitApi = ExplicitApiMode.Disabled
|
@ -1,18 +1,18 @@
|
|||||||
package space.kscience.controls.demo.constructor
|
package space.kscience.controls.demo.constructor
|
||||||
|
|
||||||
import space.kscience.controls.api.get
|
|
||||||
import space.kscience.controls.constructor.*
|
import space.kscience.controls.constructor.*
|
||||||
import space.kscience.controls.manager.ClockManager
|
import space.kscience.controls.manager.ClockManager
|
||||||
import space.kscience.controls.manager.DeviceManager
|
import space.kscience.controls.manager.DeviceManager
|
||||||
import space.kscience.controls.manager.clock
|
import space.kscience.controls.manager.clock
|
||||||
import space.kscience.controls.spec.doRecurring
|
import space.kscience.controls.spec.doRecurring
|
||||||
import space.kscience.controls.spec.name
|
import space.kscience.controls.spec.name
|
||||||
import space.kscience.controls.spec.write
|
|
||||||
import space.kscience.controls.vision.plot
|
import space.kscience.controls.vision.plot
|
||||||
import space.kscience.controls.vision.plotDeviceProperty
|
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.plotly.models.ScatterMode
|
import space.kscience.plotly.models.ScatterMode
|
||||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
@ -21,6 +21,27 @@ import kotlin.time.Duration.Companion.milliseconds
|
|||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
import kotlin.time.DurationUnit
|
import kotlin.time.DurationUnit
|
||||||
|
|
||||||
|
|
||||||
|
class LinearDrive(
|
||||||
|
context: Context,
|
||||||
|
state: DoubleRangeState,
|
||||||
|
mass: Double,
|
||||||
|
pidParameters: PidParameters,
|
||||||
|
meta: Meta = Meta.EMPTY,
|
||||||
|
) : DeviceConstructor(context.request(DeviceManager), meta) {
|
||||||
|
|
||||||
|
val drive by device(VirtualDrive.factory(mass, state))
|
||||||
|
val pid by device(PidRegulator(drive, pidParameters))
|
||||||
|
|
||||||
|
val start by device(LimitSwitch.factory(state.atStartState))
|
||||||
|
val end by device(LimitSwitch.factory(state.atEndState))
|
||||||
|
|
||||||
|
|
||||||
|
val position by property(state)
|
||||||
|
var target by mutableProperty(pid.mutablePropertyAsState(Regulator.target, 0.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public fun main() {
|
public fun main() {
|
||||||
val context = Context {
|
val context = Context {
|
||||||
plugin(DeviceManager)
|
plugin(DeviceManager)
|
||||||
@ -37,25 +58,20 @@ public fun main() {
|
|||||||
timeStep = 0.005.seconds
|
timeStep = 0.005.seconds
|
||||||
)
|
)
|
||||||
|
|
||||||
val device = context.registerDeviceGroup {
|
val device = context.install("device", LinearDrive(context, state, 0.005, pidParameters)).apply {
|
||||||
val drive = VirtualDrive(context, 0.005, state)
|
|
||||||
val pid = pid("pid", drive, pidParameters)
|
|
||||||
registerDevice("start", LimitSwitch.factory(state.atStartState))
|
|
||||||
registerDevice("end", LimitSwitch.factory(state.atEndState))
|
|
||||||
|
|
||||||
val clock = context.clock
|
val clock = context.clock
|
||||||
val clockStart = clock.now()
|
val clockStart = clock.now()
|
||||||
|
|
||||||
doRecurring(10.milliseconds) {
|
doRecurring(10.milliseconds) {
|
||||||
val timeFromStart = clock.now() - clockStart
|
val timeFromStart = clock.now() - clockStart
|
||||||
val t = timeFromStart.toDouble(DurationUnit.SECONDS)
|
val t = timeFromStart.toDouble(DurationUnit.SECONDS)
|
||||||
val freq = 0.1
|
val freq = 0.1
|
||||||
val target = 5 * sin(2.0 * PI * freq * t) +
|
|
||||||
|
target = 5 * sin(2.0 * PI * freq * t) +
|
||||||
sin(2 * PI * 21 * freq * t + 0.02 * (timeFromStart / pidParameters.timeStep))
|
sin(2 * PI * 21 * freq * t + 0.02 * (timeFromStart / pidParameters.timeStep))
|
||||||
pid.write(Regulator.target, target)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val maxAge = 10.seconds
|
val maxAge = 10.seconds
|
||||||
|
|
||||||
context.showDashboard {
|
context.showDashboard {
|
||||||
@ -63,21 +79,21 @@ public fun main() {
|
|||||||
plotNumberState(context, state, maxAge = maxAge) {
|
plotNumberState(context, state, maxAge = maxAge) {
|
||||||
name = "real position"
|
name = "real position"
|
||||||
}
|
}
|
||||||
plotDeviceProperty(device["pid"], Regulator.position.name, maxAge = maxAge) {
|
plotDeviceProperty(device.pid, Regulator.position.name, maxAge = maxAge) {
|
||||||
name = "read position"
|
name = "read position"
|
||||||
}
|
}
|
||||||
|
|
||||||
plotDeviceProperty(device["pid"], Regulator.target.name, maxAge = maxAge) {
|
plotDeviceProperty(device.pid, Regulator.target.name, maxAge = maxAge) {
|
||||||
name = "target"
|
name = "target"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plot {
|
plot {
|
||||||
plotDeviceProperty(device["start"], LimitSwitch.locked.name, maxAge = maxAge) {
|
plotDeviceProperty(device.start, LimitSwitch.locked.name, maxAge = maxAge) {
|
||||||
name = "start measured"
|
name = "start measured"
|
||||||
mode = ScatterMode.markers
|
mode = ScatterMode.markers
|
||||||
}
|
}
|
||||||
plotDeviceProperty(device["end"], LimitSwitch.locked.name, maxAge = maxAge) {
|
plotDeviceProperty(device.end, LimitSwitch.locked.name, maxAge = maxAge) {
|
||||||
name = "end measured"
|
name = "end measured"
|
||||||
mode = ScatterMode.markers
|
mode = ScatterMode.markers
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user