Add vision client-side events

This commit is contained in:
Alexander Nozik 2023-10-18 13:52:42 +03:00
parent 6144410d22
commit cf6d73305b
4 changed files with 53 additions and 41 deletions

View File

@ -9,12 +9,34 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.isEmpty
import space.kscience.dataforge.names.plus
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.
*/
@ -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]
*/

View File

@ -3,50 +3,23 @@ package space.kscience.visionforge
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.names.Name
/**
* An event propagated from client to a server
*/
@Serializable
public sealed interface VisionEvent
public sealed interface VisionEvent{
public val targetName: Name
}
/**
* An event that consists of custom meta
*/
@Serializable
@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
@SerialName("change")
public data class VisionChange(
public val vision: Vision? = null,
public val properties: Meta? = null,
public val children: Map<Name, VisionChange>? = null,
) : VisionEvent
public class VisionChangeEvent(override val targetName: Name, public val change: VisionChange) : VisionEvent

View File

@ -5,6 +5,9 @@ import kotlinx.browser.window
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
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.launch
import kotlinx.coroutines.sync.Mutex
@ -141,21 +144,19 @@ public class VisionClient : AbstractPlugin() {
onopen = {
feedbackJob = visionManager.context.launch {
eventCollector.filter { it.targetName == name }.onEach {
send(visionManager.jsonFormat.encodeToString(VisionEvent.serializer(), it))
}.launchIn(this)
while (isActive) {
delay(feedbackAggregationTime.milliseconds)
val change = changeCollector[name] ?: continue
if (!change.isEmpty()) {
mutex.withLock {
send(change.toJsonString(visionManager))
eventCollector.emit(VisionChangeEvent(name, change.deepCopy(visionManager)))
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'" }

View File

@ -75,8 +75,8 @@ public class VisionRoute(
public fun Application.serveVisionData(
configuration: VisionRoute,
onEvent: suspend Vision.(VisionEvent) -> Unit = { event ->
if (event is VisionChange) {
update(event)
if (event is VisionChangeEvent) {
update(event.change)
}
},
resolveVision: (Name) -> Vision?,