Update constructor
This commit is contained in:
parent
4f028ccee8
commit
1fcdbdc9f4
@ -3,6 +3,8 @@
|
|||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- Device lifecycle message
|
||||||
|
- Low-code constructor
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
description = """
|
description = """
|
||||||
A low-code constructor foe composite devices simulation
|
A low-code constructor for composite devices simulation
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
kscience{
|
kscience{
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
package space.kscience.controls.constructor
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An observable state of a device
|
||||||
|
*/
|
||||||
|
public interface DeviceState<T> {
|
||||||
|
public val converter: MetaConverter<T>
|
||||||
|
public val value: T
|
||||||
|
|
||||||
|
public val valueFlow: Flow<T>
|
||||||
|
|
||||||
|
public val metaFlow: Flow<Meta>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mutable state of a device
|
||||||
|
*/
|
||||||
|
public interface MutableDeviceState<T> : DeviceState<T>{
|
||||||
|
override var value: T
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.controls.spec
|
package space.kscience.controls.constructor
|
||||||
|
|
||||||
import space.kscience.controls.api.Device
|
import space.kscience.controls.api.Device
|
||||||
import space.kscience.controls.api.DeviceHub
|
import space.kscience.controls.api.DeviceHub
|
||||||
@ -7,11 +7,8 @@ import space.kscience.dataforge.context.Factory
|
|||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.get
|
import space.kscience.dataforge.meta.get
|
||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
import kotlin.collections.Map
|
|
||||||
import kotlin.collections.component1
|
import kotlin.collections.component1
|
||||||
import kotlin.collections.component2
|
import kotlin.collections.component2
|
||||||
import kotlin.collections.mapValues
|
|
||||||
import kotlin.collections.mutableMapOf
|
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
|
||||||
|
|
@ -0,0 +1,91 @@
|
|||||||
|
package space.kscience.controls.constructor
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.datetime.Clock
|
||||||
|
import space.kscience.controls.api.Device
|
||||||
|
import space.kscience.controls.spec.DeviceBySpec
|
||||||
|
import space.kscience.controls.spec.DevicePropertySpec
|
||||||
|
import space.kscience.controls.spec.DeviceSpec
|
||||||
|
import space.kscience.controls.spec.doubleProperty
|
||||||
|
import space.kscience.dataforge.context.Context
|
||||||
|
import space.kscience.dataforge.meta.double
|
||||||
|
import space.kscience.dataforge.meta.get
|
||||||
|
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||||
|
import kotlin.math.pow
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
import kotlin.time.DurationUnit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A classic drive regulated by force with encoder
|
||||||
|
*/
|
||||||
|
public interface Drive : Device {
|
||||||
|
/**
|
||||||
|
* Get or set drive force or momentum
|
||||||
|
*/
|
||||||
|
public var force: Double
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current position value
|
||||||
|
*/
|
||||||
|
public val position: Double
|
||||||
|
|
||||||
|
public companion object : DeviceSpec<Drive>() {
|
||||||
|
public val force: DevicePropertySpec<Drive, Double> by Drive.property(
|
||||||
|
MetaConverter.double,
|
||||||
|
Drive::force
|
||||||
|
)
|
||||||
|
|
||||||
|
public val position: DevicePropertySpec<Drive, Double> by doubleProperty { position }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A virtual drive
|
||||||
|
*/
|
||||||
|
public class VirtualDrive(
|
||||||
|
context: Context,
|
||||||
|
private val mass: Double,
|
||||||
|
position: Double,
|
||||||
|
) : Drive, DeviceBySpec<Drive>(Drive, context) {
|
||||||
|
|
||||||
|
private val dt = meta["time.step"].double?.milliseconds ?: 5.milliseconds
|
||||||
|
private val clock = Clock.System
|
||||||
|
|
||||||
|
override var force: Double = 0.0
|
||||||
|
|
||||||
|
override var position: Double = position
|
||||||
|
private set
|
||||||
|
|
||||||
|
public var velocity: Double = 0.0
|
||||||
|
private set
|
||||||
|
|
||||||
|
private var updateJob: Job? = null
|
||||||
|
|
||||||
|
override suspend fun onStart() {
|
||||||
|
updateJob = launch {
|
||||||
|
var lastTime = clock.now()
|
||||||
|
while (isActive) {
|
||||||
|
delay(dt)
|
||||||
|
val realTime = clock.now()
|
||||||
|
val dtSeconds = (realTime - lastTime).toDouble(DurationUnit.SECONDS)
|
||||||
|
|
||||||
|
//set last time and value to new values
|
||||||
|
lastTime = realTime
|
||||||
|
|
||||||
|
// compute new value based on velocity and acceleration from the previous step
|
||||||
|
position += velocity * dtSeconds + force/mass * dtSeconds.pow(2) / 2
|
||||||
|
|
||||||
|
// compute new velocity based on acceleration on the previous step
|
||||||
|
velocity += force/mass * dtSeconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
updateJob?.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package center.sciprog.controls.devices.misc
|
package space.kscience.controls.constructor
|
||||||
|
|
||||||
import space.kscience.controls.api.Device
|
import space.kscience.controls.api.Device
|
||||||
import space.kscience.controls.spec.DeviceBySpec
|
import space.kscience.controls.spec.DeviceBySpec
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package center.sciprog.controls.devices.misc
|
package space.kscience.controls.constructor
|
||||||
|
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@ -8,28 +8,27 @@ import kotlinx.coroutines.sync.Mutex
|
|||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
import kotlinx.datetime.Instant
|
import kotlinx.datetime.Instant
|
||||||
import space.kscience.controls.api.DeviceLifecycleState
|
|
||||||
import space.kscience.controls.spec.DeviceBySpec
|
import space.kscience.controls.spec.DeviceBySpec
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlin.time.DurationUnit
|
import kotlin.time.DurationUnit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PID controller on top of a [Regulator]
|
* A drive with PID regulator
|
||||||
*/
|
*/
|
||||||
public class PidRegulator(
|
public class PidRegulator(
|
||||||
public val regulator: Regulator,
|
public val drive: Drive,
|
||||||
public val kp: Double,
|
public val kp: Double,
|
||||||
public val ki: Double,
|
public val ki: Double,
|
||||||
public val kd: Double,
|
public val kd: Double,
|
||||||
private val dt: Duration = 0.5.milliseconds,
|
private val dt: Duration = 1.milliseconds,
|
||||||
private val clock: Clock = Clock.System,
|
private val clock: Clock = Clock.System,
|
||||||
) : DeviceBySpec<Regulator>(Regulator, regulator.context), Regulator {
|
) : DeviceBySpec<Regulator>(Regulator, drive.context), Regulator {
|
||||||
|
|
||||||
override var target: Double = regulator.target
|
override var target: Double = drive.position
|
||||||
|
|
||||||
private var lastTime: Instant = clock.now()
|
private var lastTime: Instant = clock.now()
|
||||||
private var lastRegulatorTarget: Double = target
|
private var lastPosition: Double = target
|
||||||
|
|
||||||
private var integral: Double = 0.0
|
private var integral: Double = 0.0
|
||||||
|
|
||||||
@ -39,10 +38,7 @@ public class PidRegulator(
|
|||||||
|
|
||||||
|
|
||||||
override suspend fun onStart() {
|
override suspend fun onStart() {
|
||||||
if(regulator.lifecycleState == DeviceLifecycleState.STOPPED){
|
drive.start()
|
||||||
regulator.start()
|
|
||||||
}
|
|
||||||
regulator.start()
|
|
||||||
updateJob = launch {
|
updateJob = launch {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
delay(dt)
|
delay(dt)
|
||||||
@ -51,13 +47,13 @@ public class PidRegulator(
|
|||||||
val delta = target - position
|
val delta = target - position
|
||||||
val dtSeconds = (realTime - lastTime).toDouble(DurationUnit.SECONDS)
|
val dtSeconds = (realTime - lastTime).toDouble(DurationUnit.SECONDS)
|
||||||
integral += delta * dtSeconds
|
integral += delta * dtSeconds
|
||||||
val derivative = (regulator.target - lastRegulatorTarget) / dtSeconds
|
val derivative = (drive.position - lastPosition) / dtSeconds
|
||||||
|
|
||||||
//set last time and value to new values
|
//set last time and value to new values
|
||||||
lastTime = realTime
|
lastTime = realTime
|
||||||
lastRegulatorTarget = regulator.target
|
lastPosition = drive.position
|
||||||
|
|
||||||
regulator.target = regulator.position + kp * delta + ki * integral + kd * derivative
|
drive.force = kp * delta + ki * integral + kd * derivative
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,129 +63,6 @@ public class PidRegulator(
|
|||||||
updateJob?.cancel()
|
updateJob?.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val position: Double get() = regulator.position
|
override val position: Double get() = drive.position
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
//interface PidRegulator : Device {
|
|
||||||
// /**
|
|
||||||
// * Proportional coefficient
|
|
||||||
// */
|
|
||||||
// val kp: Double
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Integral coefficient
|
|
||||||
// */
|
|
||||||
// val ki: Double
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Differential coefficient
|
|
||||||
// */
|
|
||||||
// val kd: Double
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * The target value for PID
|
|
||||||
// */
|
|
||||||
// var target: Double
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Read current value
|
|
||||||
// */
|
|
||||||
// suspend fun read(): Double
|
|
||||||
//
|
|
||||||
// companion object : DeviceSpec<PidRegulator>() {
|
|
||||||
// val target by property(MetaConverter.double, PidRegulator::target)
|
|
||||||
// val value by doubleProperty { read() }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// *
|
|
||||||
// */
|
|
||||||
//class VirtualPid(
|
|
||||||
// context: Context,
|
|
||||||
// override val kp: Double,
|
|
||||||
// override val ki: Double,
|
|
||||||
// override val kd: Double,
|
|
||||||
// val mass: Double,
|
|
||||||
// override var target: Double = 0.0,
|
|
||||||
// private val dt: Duration = 0.5.milliseconds,
|
|
||||||
// private val clock: Clock = Clock.System,
|
|
||||||
//) : DeviceBySpec<PidRegulator>(PidRegulator, context), PidRegulator {
|
|
||||||
//
|
|
||||||
// private val mutex = Mutex()
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// private var lastTime: Instant = clock.now()
|
|
||||||
// private var lastValue: Double = target
|
|
||||||
//
|
|
||||||
// private var value: Double = target
|
|
||||||
// private var velocity: Double = 0.0
|
|
||||||
// private var acceleration: Double = 0.0
|
|
||||||
// private var integral: Double = 0.0
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// private var updateJob: Job? = null
|
|
||||||
//
|
|
||||||
// override suspend fun onStart() {
|
|
||||||
// updateJob = launch {
|
|
||||||
// while (isActive) {
|
|
||||||
// delay(dt)
|
|
||||||
// mutex.withLock {
|
|
||||||
// val realTime = clock.now()
|
|
||||||
// val delta = target - value
|
|
||||||
// val dtSeconds = (realTime - lastTime).toDouble(DurationUnit.SECONDS)
|
|
||||||
// integral += delta * dtSeconds
|
|
||||||
// val derivative = (value - lastValue) / dtSeconds
|
|
||||||
//
|
|
||||||
// //set last time and value to new values
|
|
||||||
// lastTime = realTime
|
|
||||||
// lastValue = value
|
|
||||||
//
|
|
||||||
// // compute new value based on velocity and acceleration from the previous step
|
|
||||||
// value += velocity * dtSeconds + acceleration * dtSeconds.pow(2) / 2
|
|
||||||
//
|
|
||||||
// // compute new velocity based on acceleration on the previous step
|
|
||||||
// velocity += acceleration * dtSeconds
|
|
||||||
//
|
|
||||||
// //compute force for the next step based on current values
|
|
||||||
// acceleration = (kp * delta + ki * integral + kd * derivative) / mass
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// check(value.isFinite() && velocity.isFinite()) {
|
|
||||||
// "Value $value is not finite"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun onStop() {
|
|
||||||
// updateJob?.cancel()
|
|
||||||
// super<PidRegulator>.stop()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override suspend fun read(): Double = value
|
|
||||||
//
|
|
||||||
// suspend fun readVelocity(): Double = velocity
|
|
||||||
//
|
|
||||||
// suspend fun readAcceleration(): Double = acceleration
|
|
||||||
//
|
|
||||||
// suspend fun write(newTarget: Double) = mutex.withLock {
|
|
||||||
// require(newTarget.isFinite()) { "Value $newTarget is not valid" }
|
|
||||||
// target = newTarget
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// companion object : Factory<Device> {
|
|
||||||
// override fun build(context: Context, meta: Meta) = VirtualPid(
|
|
||||||
// context,
|
|
||||||
// meta["kp"].double ?: error("Kp is not defined"),
|
|
||||||
// meta["ki"].double ?: error("Ki is not defined"),
|
|
||||||
// meta["kd"].double ?: error("Kd is not defined"),
|
|
||||||
// meta["m"].double ?: error("Mass is not defined"),
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//}
|
|
@ -1,17 +1,14 @@
|
|||||||
package center.sciprog.controls.devices.misc
|
package space.kscience.controls.constructor
|
||||||
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import space.kscience.controls.api.Device
|
import space.kscience.controls.api.Device
|
||||||
import space.kscience.controls.spec.DeviceBySpec
|
|
||||||
import space.kscience.controls.spec.DevicePropertySpec
|
import space.kscience.controls.spec.DevicePropertySpec
|
||||||
import space.kscience.controls.spec.DeviceSpec
|
import space.kscience.controls.spec.DeviceSpec
|
||||||
import space.kscience.controls.spec.doubleProperty
|
import space.kscience.controls.spec.doubleProperty
|
||||||
import space.kscience.dataforge.context.Context
|
|
||||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single axis drive
|
* A regulator with target value and current position
|
||||||
*/
|
*/
|
||||||
public interface Regulator : Device {
|
public interface Regulator : Device {
|
||||||
/**
|
/**
|
||||||
@ -30,22 +27,3 @@ public interface Regulator : Device {
|
|||||||
public val position: DevicePropertySpec<Regulator, Double> by doubleProperty { position }
|
public val position: DevicePropertySpec<Regulator, Double> by doubleProperty { position }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Virtual [Regulator] with speed limit
|
|
||||||
*/
|
|
||||||
public class VirtualRegulator(
|
|
||||||
context: Context,
|
|
||||||
value: Double,
|
|
||||||
private val speed: Double,
|
|
||||||
) : DeviceBySpec<Regulator>(Regulator, context), Regulator {
|
|
||||||
|
|
||||||
private var moveJob: Job? = null
|
|
||||||
|
|
||||||
override var position: Double = value
|
|
||||||
private set
|
|
||||||
|
|
||||||
override var target: Double = value
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -7,6 +7,7 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
import kotlinx.coroutines.flow.filterIsInstance
|
import kotlinx.coroutines.flow.filterIsInstance
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.controls.api.Device.Companion.DEVICE_TARGET
|
import space.kscience.controls.api.Device.Companion.DEVICE_TARGET
|
||||||
import space.kscience.dataforge.context.ContextAware
|
import space.kscience.dataforge.context.ContextAware
|
||||||
import space.kscience.dataforge.context.info
|
import space.kscience.dataforge.context.info
|
||||||
@ -19,6 +20,7 @@ import space.kscience.dataforge.names.Name
|
|||||||
/**
|
/**
|
||||||
* A lifecycle state of a device
|
* A lifecycle state of a device
|
||||||
*/
|
*/
|
||||||
|
@Serializable
|
||||||
public enum class DeviceLifecycleState {
|
public enum class DeviceLifecycleState {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,7 +36,12 @@ public enum class DeviceLifecycleState {
|
|||||||
/**
|
/**
|
||||||
* The Device is closed
|
* The Device is closed
|
||||||
*/
|
*/
|
||||||
STOPPED
|
STOPPED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The device encountered irrecoverable error
|
||||||
|
*/
|
||||||
|
ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -98,7 +105,8 @@ public interface Device : ContextAware, CoroutineScope {
|
|||||||
public suspend fun execute(actionName: String, argument: Meta? = null): Meta?
|
public suspend fun execute(actionName: String, argument: Meta? = null): Meta?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the device. This function suspends until the device is finished initialization
|
* Initialize the device. This function suspends until the device is finished initialization.
|
||||||
|
* Does nothing if the device is started or is starting
|
||||||
*/
|
*/
|
||||||
public suspend fun start(): Unit = Unit
|
public suspend fun start(): Unit = Unit
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@file:OptIn(ExperimentalSerializationApi::class)
|
@file:OptIn(ExperimentalSerializationApi::class, ExperimentalSerializationApi::class)
|
||||||
|
|
||||||
package space.kscience.controls.api
|
package space.kscience.controls.api
|
||||||
|
|
||||||
@ -228,6 +228,21 @@ public data class DeviceErrorMessage(
|
|||||||
override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = block(sourceDevice))
|
override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = block(sourceDevice))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device [Device.lifecycleState] is changed
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
@SerialName("lifecycle")
|
||||||
|
public data class DeviceLifeCycleMessage(
|
||||||
|
val state: DeviceLifecycleState,
|
||||||
|
override val sourceDevice: Name = Name.EMPTY,
|
||||||
|
override val targetDevice: Name? = null,
|
||||||
|
override val comment: String? = null,
|
||||||
|
@EncodeDefault override val time: Instant? = Clock.System.now(),
|
||||||
|
) : DeviceMessage() {
|
||||||
|
override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = block(sourceDevice))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public fun DeviceMessage.toMeta(): Meta = Json.encodeToJsonElement(this).toMeta()
|
public fun DeviceMessage.toMeta(): Meta = Json.encodeToJsonElement(this).toMeta()
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ public suspend fun Device.respondMessage(deviceTarget: Name, request: DeviceMess
|
|||||||
is DeviceErrorMessage,
|
is DeviceErrorMessage,
|
||||||
is EmptyDeviceMessage,
|
is EmptyDeviceMessage,
|
||||||
is DeviceLogMessage,
|
is DeviceLogMessage,
|
||||||
|
is DeviceLifeCycleMessage,
|
||||||
-> null
|
-> null
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
|
@ -190,7 +190,16 @@ public abstract class DeviceBase<D : Device>(
|
|||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
override var lifecycleState: DeviceLifecycleState = DeviceLifecycleState.STOPPED
|
override var lifecycleState: DeviceLifecycleState = DeviceLifecycleState.STOPPED
|
||||||
protected set
|
protected set(value) {
|
||||||
|
if (field != value) {
|
||||||
|
launch {
|
||||||
|
sharedMessageFlow.emit(
|
||||||
|
DeviceLifeCycleMessage(value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
protected open suspend fun onStart() {
|
protected open suspend fun onStart() {
|
||||||
|
|
||||||
@ -198,7 +207,7 @@ public abstract class DeviceBase<D : Device>(
|
|||||||
|
|
||||||
@OptIn(DFExperimental::class)
|
@OptIn(DFExperimental::class)
|
||||||
final override suspend fun start() {
|
final override suspend fun start() {
|
||||||
if(lifecycleState == DeviceLifecycleState.STOPPED) {
|
if (lifecycleState == DeviceLifecycleState.STOPPED) {
|
||||||
super.start()
|
super.start()
|
||||||
lifecycleState = DeviceLifecycleState.STARTING
|
lifecycleState = DeviceLifecycleState.STARTING
|
||||||
onStart()
|
onStart()
|
||||||
|
20
controls-vision/build.gradle.kts
Normal file
20
controls-vision/build.gradle.kts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
plugins {
|
||||||
|
id("space.kscience.gradle.mpp")
|
||||||
|
`maven-publish`
|
||||||
|
}
|
||||||
|
|
||||||
|
description = """
|
||||||
|
Dashboard and visualization extensions for devices
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
kscience{
|
||||||
|
jvm()
|
||||||
|
js()
|
||||||
|
dependencies {
|
||||||
|
api(projects.controlsCore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readme{
|
||||||
|
maturity = space.kscience.gradle.Maturity.PROTOTYPE
|
||||||
|
}
|
@ -51,6 +51,7 @@ include(
|
|||||||
":controls-storage",
|
":controls-storage",
|
||||||
":controls-storage:controls-xodus",
|
":controls-storage:controls-xodus",
|
||||||
":controls-constructor",
|
":controls-constructor",
|
||||||
|
":controls-vision",
|
||||||
":magix",
|
":magix",
|
||||||
":magix:magix-api",
|
":magix:magix-api",
|
||||||
":magix:magix-server",
|
":magix:magix-server",
|
||||||
|
Loading…
Reference in New Issue
Block a user