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.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]
*/ */

View File

@ -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

View File

@ -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'" }

View File

@ -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?,