forked from kscience/visionforge
Add vision client-side events
This commit is contained in:
parent
6144410d22
commit
cf6d73305b
@ -9,12 +9,34 @@ import kotlinx.coroutines.flow.launchIn
|
|||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.isEmpty
|
import space.kscience.dataforge.names.isEmpty
|
||||||
import space.kscience.dataforge.names.plus
|
import space.kscience.dataforge.names.plus
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A vision used only in change propagation and showing that the target should be removed
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
@SerialName("null")
|
||||||
|
public object NullVision : Vision {
|
||||||
|
override var parent: Vision?
|
||||||
|
get() = null
|
||||||
|
set(_) {
|
||||||
|
error("Can't set parent for null vision")
|
||||||
|
}
|
||||||
|
|
||||||
|
override val properties: MutableVisionProperties get() = error("Can't get properties of `NullVision`")
|
||||||
|
|
||||||
|
override val descriptor: MetaDescriptor? = null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a deep copy of given Vision without external connections.
|
* Create a deep copy of given Vision without external connections.
|
||||||
*/
|
*/
|
||||||
@ -28,6 +50,22 @@ private fun Vision.deepCopy(manager: VisionManager): Vision {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event that contains changes made to a vision.
|
||||||
|
*
|
||||||
|
* @param vision a new value for vision content. If the Vision is to be removed should be [NullVision]
|
||||||
|
* @param properties updated properties
|
||||||
|
* @param children a map of children changed in ths [VisionChange].
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
@SerialName("change")
|
||||||
|
public data class VisionChange(
|
||||||
|
public val vision: Vision? = null,
|
||||||
|
public val properties: Meta? = null,
|
||||||
|
public val children: Map<Name, VisionChange>? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An update for a [Vision]
|
* An update for a [Vision]
|
||||||
*/
|
*/
|
@ -3,50 +3,23 @@ package space.kscience.visionforge
|
|||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event propagated from client to a server
|
* An event propagated from client to a server
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public sealed interface VisionEvent
|
public sealed interface VisionEvent{
|
||||||
|
public val targetName: Name
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event that consists of custom meta
|
* An event that consists of custom meta
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("meta")
|
@SerialName("meta")
|
||||||
public class VisionMetaEvent(public val targetName: Name, public val meta: Meta) : VisionEvent
|
public class VisionMetaEvent(override val targetName: Name, public val meta: Meta) : VisionEvent
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A vision used only in change propagation and showing that the target should be removed
|
|
||||||
*/
|
|
||||||
@Serializable
|
|
||||||
@SerialName("null")
|
|
||||||
public object NullVision : Vision {
|
|
||||||
override var parent: Vision?
|
|
||||||
get() = null
|
|
||||||
set(_) {
|
|
||||||
error("Can't set parent for null vision")
|
|
||||||
}
|
|
||||||
|
|
||||||
override val properties: MutableVisionProperties get() = error("Can't get properties of `NullVision`")
|
|
||||||
|
|
||||||
override val descriptor: MetaDescriptor? = null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param vision a new value for vision content. If the Vision is to be removed should be [NullVision]
|
|
||||||
* @param properties updated properties
|
|
||||||
* @param children a map of children changed in ths [VisionChange].
|
|
||||||
*/
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("change")
|
@SerialName("change")
|
||||||
public data class VisionChange(
|
public class VisionChangeEvent(override val targetName: Name, public val change: VisionChange) : VisionEvent
|
||||||
public val vision: Vision? = null,
|
|
||||||
public val properties: Meta? = null,
|
|
||||||
public val children: Map<Name, VisionChange>? = null,
|
|
||||||
) : VisionEvent
|
|
@ -5,6 +5,9 @@ import kotlinx.browser.window
|
|||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@ -141,21 +144,19 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
|
|
||||||
onopen = {
|
onopen = {
|
||||||
feedbackJob = visionManager.context.launch {
|
feedbackJob = visionManager.context.launch {
|
||||||
|
eventCollector.filter { it.targetName == name }.onEach {
|
||||||
|
send(visionManager.jsonFormat.encodeToString(VisionEvent.serializer(), it))
|
||||||
|
}.launchIn(this)
|
||||||
|
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
delay(feedbackAggregationTime.milliseconds)
|
delay(feedbackAggregationTime.milliseconds)
|
||||||
val change = changeCollector[name] ?: continue
|
val change = changeCollector[name] ?: continue
|
||||||
if (!change.isEmpty()) {
|
if (!change.isEmpty()) {
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
send(change.toJsonString(visionManager))
|
eventCollector.emit(VisionChangeEvent(name, change.deepCopy(visionManager)))
|
||||||
change.reset()
|
change.reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// // take channel for given vision name
|
|
||||||
// eventCollector[name]?.let { channel ->
|
|
||||||
// for (e in channel) {
|
|
||||||
// send(visionManager.jsonFormat.encodeToString(VisionEvent.serializer(), e))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.info { "WebSocket feedback channel established for output '$name'" }
|
logger.info { "WebSocket feedback channel established for output '$name'" }
|
||||||
|
@ -75,8 +75,8 @@ public class VisionRoute(
|
|||||||
public fun Application.serveVisionData(
|
public fun Application.serveVisionData(
|
||||||
configuration: VisionRoute,
|
configuration: VisionRoute,
|
||||||
onEvent: suspend Vision.(VisionEvent) -> Unit = { event ->
|
onEvent: suspend Vision.(VisionEvent) -> Unit = { event ->
|
||||||
if (event is VisionChange) {
|
if (event is VisionChangeEvent) {
|
||||||
update(event)
|
update(event.change)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resolveVision: (Name) -> Vision?,
|
resolveVision: (Name) -> Vision?,
|
||||||
|
Loading…
Reference in New Issue
Block a user