Add direct event processing to Vision

This commit is contained in:
Alexander Nozik 2023-11-15 10:42:56 +03:00
parent 1ea5ef86e6
commit c7d4bdfa5f
8 changed files with 22 additions and 51 deletions

View File

@ -45,7 +45,7 @@ include(
":ui:ring",
// ":ui:material",
":ui:bootstrap",
":ui:compose",
// ":ui:compose",
":visionforge-core",
":visionforge-solid",
// ":visionforge-fx",

View File

@ -11,7 +11,6 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.isEmpty
import space.kscience.visionforge.AbstractVisionGroup.Companion.updateProperties
import space.kscience.visionforge.Vision.Companion.TYPE
@ -50,13 +49,9 @@ public interface Vision : Described {
* Receive and process a generic [VisionEvent].
*/
public fun receiveEvent(event: VisionEvent) {
if(event.targetName.isEmpty()) {
when (event) {
is VisionChangeEvent -> receiveChange(event.change)
else -> TODO()
}
} else {
error("Vision is not a group and can't process an event with non-empty target")
when (event) {
is VisionChange -> receiveChange(event)
else -> TODO()
}
}

View File

@ -63,8 +63,7 @@ public data class VisionChange(
public val vision: Vision? = null,
public val properties: Meta? = null,
public val children: Map<Name, VisionChange>? = null,
)
) : VisionEvent
/**
* An update for a [Vision]

View File

@ -13,7 +13,7 @@ import space.kscience.dataforge.names.parseAsName
public interface VisionClient: Plugin {
public val visionManager: VisionManager
public suspend fun sendEvent(event: VisionEvent)
public suspend fun sendEvent(targetName: Name, event: VisionEvent)
public fun notifyPropertyChanged(visionName: Name, propertyName: Name, item: Meta?)
}
@ -35,8 +35,8 @@ public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: St
notifyPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
}
public fun VisionClient.sendEvent(visionName: Name, event: MetaRepr): Unit {
public fun VisionClient.sendEvent(targetName: Name, payload: MetaRepr): Unit {
context.launch {
sendEvent(VisionMetaEvent(visionName, event.toMeta()))
sendEvent(targetName, VisionMetaEvent(payload.toMeta()))
}
}

View File

@ -12,13 +12,6 @@ import space.kscience.dataforge.names.Name
*/
@Serializable
public sealed interface VisionEvent {
public val targetName: Name
/**
* Create a copy of this event with the same type and content, but different [targetName]
*/
public fun changeTarget(newTarget: Name): VisionEvent
public companion object {
public val CLICK_EVENT_KEY: Name get() = Name.of("events", "click", "payload")
}
@ -29,15 +22,8 @@ public sealed interface VisionEvent {
*/
@Serializable
@SerialName("meta")
public data class VisionMetaEvent(override val targetName: Name, public val meta: Meta) : VisionEvent {
override fun changeTarget(newTarget: Name): VisionMetaEvent = VisionMetaEvent(newTarget, meta)
}
public class VisionMetaEvent(public val meta: Meta) : VisionEvent
@Serializable
@SerialName("change")
public data class VisionChangeEvent(override val targetName: Name, public val change: VisionChange) : VisionEvent {
override fun changeTarget(newTarget: Name): VisionChangeEvent = VisionChangeEvent(newTarget, change)
}
public val Vision.Companion.CLICK_EVENT_KEY: Name get() = Name.of("events", "click", "payload")

View File

@ -6,7 +6,10 @@ import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.ValueType
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.value
import space.kscience.dataforge.names.*
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.parseAsName
import space.kscience.dataforge.names.plus
import space.kscience.visionforge.AbstractVisionGroup.Companion.updateProperties
import space.kscience.visionforge.Vision.Companion.STYLE_KEY
@ -26,15 +29,6 @@ public interface VisionGroup : Vision {
updateProperties(it, Name.EMPTY)
}
}
override fun receiveEvent(event: VisionEvent) {
if (event.targetName.isEmpty()) {
super.receiveEvent(event)
} else {
val target = children[event.targetName] ?: error("Child vision with name ${event.targetName} not found")
target.receiveEvent(event.changeTarget(Name.EMPTY))
}
}
}
public interface MutableVisionGroup : VisionGroup {

View File

@ -20,7 +20,6 @@ import space.kscience.dataforge.meta.MetaSerializer
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.int
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.isEmpty
import space.kscience.dataforge.names.parseAsName
import space.kscience.visionforge.html.VisionTagConsumer
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_CONNECT_ATTRIBUTE
@ -82,15 +81,14 @@ public class JsVisionClient : AbstractPlugin(), VisionClient {
}
private val eventCollector by lazy {
MutableSharedFlow<VisionEvent>(meta["feedback.eventCache"].int ?: 100)
MutableSharedFlow<Pair<Name, VisionEvent>>(meta["feedback.eventCache"].int ?: 100)
}
/**
* Send a custom feedback event
*/
override suspend fun sendEvent(event: VisionEvent) {
eventCollector.emit(event)
override suspend fun sendEvent(targetName: Name, event: VisionEvent) {
eventCollector.emit(targetName to event)
}
private fun renderVision(element: Element, name: Name, vision: Vision, outputMeta: Meta) {
@ -127,8 +125,8 @@ public class JsVisionClient : AbstractPlugin(), VisionClient {
)
// If change contains root vision replacement, do it
if(event is VisionChangeEvent && event.targetName.isEmpty()) {
event.change.vision?.let { vision ->
if(event is VisionChange) {
event.vision?.let { vision ->
renderVision(element, name, vision, outputMeta)
}
}
@ -150,8 +148,8 @@ public class JsVisionClient : AbstractPlugin(), VisionClient {
onopen = {
feedbackJob = visionManager.context.launch {
eventCollector.filter { it.targetName == name }.onEach {
send(visionManager.jsonFormat.encodeToString(VisionEvent.serializer(), it))
eventCollector.filter { it.first == name }.onEach {
send(visionManager.jsonFormat.encodeToString(VisionEvent.serializer(), it.second))
}.launchIn(this)
while (isActive) {
@ -159,7 +157,7 @@ public class JsVisionClient : AbstractPlugin(), VisionClient {
val change = changeCollector[name] ?: continue
if (!change.isEmpty()) {
mutex.withLock {
eventCollector.emit(VisionChangeEvent(name, change.deepCopy(visionManager)))
eventCollector.emit(name to change.deepCopy(visionManager))
change.reset()
}
}

View File

@ -107,8 +107,7 @@ public fun Application.serveVisionData(
try {
withContext(configuration.context.coroutineContext) {
vision.flowChanges(configuration.updateInterval.milliseconds).onEach { update ->
val event = VisionChangeEvent(Name.EMPTY, update)
vision.flowChanges(configuration.updateInterval.milliseconds).onEach { event ->
val json = configuration.visionManager.jsonFormat.encodeToString(
VisionEvent.serializer(),
event