Allow custom events

This commit is contained in:
Alexander Nozik 2023-12-11 22:57:23 +03:00
parent f40bed7bb9
commit 9c2db6d321
6 changed files with 33 additions and 28 deletions

View File

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

View File

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

View File

@ -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
public class VisionMetaEvent(public val meta: Meta) : VisionEvent, MetaRepr {
override fun toMeta(): Meta = meta
override fun toString(): String = toMeta().toString()
}

View File

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

View File

@ -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<Pair<Name, VisionEvent>>(meta["feedback.eventCache"].int ?: 100)
}
private val eventCollector = MutableSharedFlow<Pair<Name, VisionEvent>>(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

View File

@ -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<VisionEvent>(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<HtmlFragment>,
connector: EngineConnectorConfig? = null,
visionFragment: HtmlVisionFragment,
){
) {
require(WebSockets)
val collector: MutableMap<Name, Vision> = mutableMapOf()