From 9c2db6d32110bd26732fc8d13c385209c181f16a Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 11 Dec 2023 22:57:23 +0300 Subject: [PATCH] Allow custom events --- visionforge-core/build.gradle.kts | 4 ++-- .../space/kscience/visionforge/ControlVision.kt | 8 ++++---- .../space/kscience/visionforge/VisionEvent.kt | 11 ++++++++--- .../space/kscience/visionforge/VisionManager.kt | 14 +++++++++++--- .../space/kscience/visionforge/JsVisionClient.kt | 12 ++++-------- .../kscience/visionforge/server/VisionServer.kt | 12 ++++-------- 6 files changed, 33 insertions(+), 28 deletions(-) diff --git a/visionforge-core/build.gradle.kts b/visionforge-core/build.gradle.kts index f2cac4c6..bc7103b9 100644 --- a/visionforge-core/build.gradle.kts +++ b/visionforge-core/build.gradle.kts @@ -8,11 +8,11 @@ kscience { jvm() js() native() +// wasm() useCoroutines() - dependencies { + commonMain { api("space.kscience:dataforge-context:$dataforgeVersion") api(spclibs.kotlinx.html) -// api("org.jetbrains.kotlin-wrappers:kotlin-css") } jsMain { api("org.jetbrains.kotlin-wrappers:kotlin-extensions") diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/ControlVision.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/ControlVision.kt index fec77548..dc27662f 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/ControlVision.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/ControlVision.kt @@ -12,12 +12,12 @@ import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.parseAsName -@Serializable -@SerialName("control") -public sealed class VisionControlEvent : VisionEvent, MetaRepr { +public abstract class VisionControlEvent : VisionEvent, MetaRepr { public abstract val meta: Meta override fun toMeta(): Meta = meta + + override fun toString(): String = toMeta().toString() } public interface ControlVision : Vision { @@ -42,7 +42,7 @@ public class VisionClickEvent(override val meta: Meta) : VisionControlEvent() { public val payload: Meta? by meta.node() public val name: Name? get() = meta["name"].string?.parseAsName() - override fun toString(): String = meta.toString() + override fun toString(): String = meta.toString() } public fun VisionClickEvent(payload: Meta = Meta.EMPTY, name: Name? = null): VisionClickEvent = VisionClickEvent( diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionEvent.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionEvent.kt index de0b2643..e0f5beca 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionEvent.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionEvent.kt @@ -3,13 +3,13 @@ package space.kscience.visionforge import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.MetaRepr import space.kscience.dataforge.names.Name /** * An event propagated from client to a server */ -@Serializable -public sealed interface VisionEvent { +public interface VisionEvent { public companion object { public val CLICK_EVENT_KEY: Name get() = Name.of("events", "click", "payload") } @@ -20,4 +20,9 @@ public sealed interface VisionEvent { */ @Serializable @SerialName("meta") -public class VisionMetaEvent(public val meta: Meta) : VisionEvent \ No newline at end of file +public class VisionMetaEvent(public val meta: Meta) : VisionEvent, MetaRepr { + override fun toMeta(): Meta = meta + + override fun toString(): String = toMeta().toString() + +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt index 2d765a35..9b5a21ac 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt @@ -30,10 +30,11 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta), MutableVisionCont } } - public val jsonFormat: Json - get() = Json(defaultJson) { + public val jsonFormat: Json by lazy { + Json(defaultJson) { serializersModule = this@VisionManager.serializersModule } + } public fun decodeFromString(string: String): Vision = jsonFormat.decodeFromString(visionSerializer, string) @@ -78,6 +79,13 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta), MutableVisionCont subclass(VisionOfHtmlForm.serializer()) subclass(VisionOfHtmlButton.serializer()) } + + polymorphic(VisionEvent::class) { + subclass(VisionChange.serializer()) + subclass(VisionMetaEvent.serializer()) + subclass(VisionClickEvent.serializer()) + subclass(VisionValueChangeEvent.serializer()) + } } @OptIn(ExperimentalSerializationApi::class) @@ -107,7 +115,7 @@ public abstract class VisionPlugin(meta: Meta = Meta.EMPTY) : AbstractPlugin(met /** * Fetch a [VisionManager] from this plugin or create a child plugin with a [VisionManager] */ -public val Context.visionManager: VisionManager get() = request(VisionManager ) +public val Context.visionManager: VisionManager get() = request(VisionManager) public fun Vision.encodeToString(): String = manager?.encodeToString(this) ?: error("Orphan vision could not be encoded") diff --git a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/JsVisionClient.kt b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/JsVisionClient.kt index e0dc975a..45af671c 100644 --- a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/JsVisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/JsVisionClient.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock +import kotlinx.serialization.encodeToString import org.w3c.dom.* import org.w3c.dom.url.URL import space.kscience.dataforge.context.* @@ -81,9 +82,7 @@ public class JsVisionClient : AbstractPlugin(), VisionClient { } } - private val eventCollector by lazy { - MutableSharedFlow>(meta["feedback.eventCache"].int ?: 100) - } + private val eventCollector = MutableSharedFlow>(meta["feedback.eventCache"].int ?: 100) /** * Send a custom feedback event @@ -122,10 +121,7 @@ public class JsVisionClient : AbstractPlugin(), VisionClient { onmessage = { messageEvent -> val stringData: String? = messageEvent.data as? String if (stringData != null) { - val event: VisionEvent = visionManager.jsonFormat.decodeFromString( - VisionEvent.serializer(), - stringData - ) + val event: VisionEvent = visionManager.jsonFormat.decodeFromString(stringData) // If change contains root vision replacement, do it if (event is VisionChange) { @@ -154,7 +150,7 @@ public class JsVisionClient : AbstractPlugin(), VisionClient { feedbackJob = visionManager.context.launch { //launch a separate coroutine to send events to the backend eventCollector.filter { it.first == visionName }.onEach { - send(visionManager.jsonFormat.encodeToString(VisionEvent.serializer(), it.second)) + send(visionManager.jsonFormat.encodeToString(it.second)) }.launchIn(this) //aggregate atomic changes diff --git a/visionforge-server/src/main/kotlin/space/kscience/visionforge/server/VisionServer.kt b/visionforge-server/src/main/kotlin/space/kscience/visionforge/server/VisionServer.kt index 3c5e397a..d89bf1c0 100644 --- a/visionforge-server/src/main/kotlin/space/kscience/visionforge/server/VisionServer.kt +++ b/visionforge-server/src/main/kotlin/space/kscience/visionforge/server/VisionServer.kt @@ -20,6 +20,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.html.* +import kotlinx.serialization.encodeToString import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.ContextAware import space.kscience.dataforge.meta.* @@ -97,9 +98,7 @@ public fun Application.serveVisionData( for (frame in incoming) { val data = frame.data.decodeToString() application.log.debug("Received event for $name: \n$data") - val event = configuration.visionManager.jsonFormat.decodeFromString( - VisionEvent.serializer(), data - ) + val event: VisionEvent = configuration.visionManager.jsonFormat.decodeFromString(data) vision.receiveEvent(event) } @@ -108,10 +107,7 @@ public fun Application.serveVisionData( try { withContext(configuration.context.coroutineContext) { vision.flowChanges(configuration.updateInterval.milliseconds).onEach { event -> - val json = configuration.visionManager.jsonFormat.encodeToString( - VisionEvent.serializer(), - event - ) + val json = configuration.visionManager.jsonFormat.encodeToString(event) application.log.debug("Sending update for $name: \n$json") outgoing.send(Frame.Text(json)) }.collect() @@ -155,7 +151,7 @@ public fun Application.visionPage( headers: Collection, connector: EngineConnectorConfig? = null, visionFragment: HtmlVisionFragment, -){ +) { require(WebSockets) val collector: MutableMap = mutableMapOf()