forked from kscience/visionforge
Allow custom events
This commit is contained in:
parent
f40bed7bb9
commit
9c2db6d321
@ -8,11 +8,11 @@ kscience {
|
|||||||
jvm()
|
jvm()
|
||||||
js()
|
js()
|
||||||
native()
|
native()
|
||||||
|
// wasm()
|
||||||
useCoroutines()
|
useCoroutines()
|
||||||
dependencies {
|
commonMain {
|
||||||
api("space.kscience:dataforge-context:$dataforgeVersion")
|
api("space.kscience:dataforge-context:$dataforgeVersion")
|
||||||
api(spclibs.kotlinx.html)
|
api(spclibs.kotlinx.html)
|
||||||
// api("org.jetbrains.kotlin-wrappers:kotlin-css")
|
|
||||||
}
|
}
|
||||||
jsMain {
|
jsMain {
|
||||||
api("org.jetbrains.kotlin-wrappers:kotlin-extensions")
|
api("org.jetbrains.kotlin-wrappers:kotlin-extensions")
|
||||||
|
@ -12,12 +12,12 @@ import space.kscience.dataforge.meta.*
|
|||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.parseAsName
|
import space.kscience.dataforge.names.parseAsName
|
||||||
|
|
||||||
@Serializable
|
public abstract class VisionControlEvent : VisionEvent, MetaRepr {
|
||||||
@SerialName("control")
|
|
||||||
public sealed class VisionControlEvent : VisionEvent, MetaRepr {
|
|
||||||
public abstract val meta: Meta
|
public abstract val meta: Meta
|
||||||
|
|
||||||
override fun toMeta(): Meta = meta
|
override fun toMeta(): Meta = meta
|
||||||
|
|
||||||
|
override fun toString(): String = toMeta().toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ControlVision : Vision {
|
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 payload: Meta? by meta.node()
|
||||||
public val name: Name? get() = meta["name"].string?.parseAsName()
|
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(
|
public fun VisionClickEvent(payload: Meta = Meta.EMPTY, name: Name? = null): VisionClickEvent = VisionClickEvent(
|
||||||
|
@ -3,13 +3,13 @@ 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.MetaRepr
|
||||||
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
|
public interface VisionEvent {
|
||||||
public sealed interface VisionEvent {
|
|
||||||
public companion object {
|
public companion object {
|
||||||
public val CLICK_EVENT_KEY: Name get() = Name.of("events", "click", "payload")
|
public val CLICK_EVENT_KEY: Name get() = Name.of("events", "click", "payload")
|
||||||
}
|
}
|
||||||
@ -20,4 +20,9 @@ public sealed interface VisionEvent {
|
|||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("meta")
|
@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()
|
||||||
|
|
||||||
|
}
|
@ -30,10 +30,11 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta), MutableVisionCont
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public val jsonFormat: Json
|
public val jsonFormat: Json by lazy {
|
||||||
get() = Json(defaultJson) {
|
Json(defaultJson) {
|
||||||
serializersModule = this@VisionManager.serializersModule
|
serializersModule = this@VisionManager.serializersModule
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public fun decodeFromString(string: String): Vision = jsonFormat.decodeFromString(visionSerializer, string)
|
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(VisionOfHtmlForm.serializer())
|
||||||
subclass(VisionOfHtmlButton.serializer())
|
subclass(VisionOfHtmlButton.serializer())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
polymorphic(VisionEvent::class) {
|
||||||
|
subclass(VisionChange.serializer())
|
||||||
|
subclass(VisionMetaEvent.serializer())
|
||||||
|
subclass(VisionClickEvent.serializer())
|
||||||
|
subclass(VisionValueChangeEvent.serializer())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
@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]
|
* 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 =
|
public fun Vision.encodeToString(): String =
|
||||||
manager?.encodeToString(this) ?: error("Orphan vision could not be encoded")
|
manager?.encodeToString(this) ?: error("Orphan vision could not be encoded")
|
||||||
|
@ -12,6 +12,7 @@ import kotlinx.coroutines.isActive
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
import org.w3c.dom.*
|
import org.w3c.dom.*
|
||||||
import org.w3c.dom.url.URL
|
import org.w3c.dom.url.URL
|
||||||
import space.kscience.dataforge.context.*
|
import space.kscience.dataforge.context.*
|
||||||
@ -81,9 +82,7 @@ public class JsVisionClient : AbstractPlugin(), VisionClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val eventCollector by lazy {
|
private val eventCollector = MutableSharedFlow<Pair<Name, VisionEvent>>(meta["feedback.eventCache"].int ?: 100)
|
||||||
MutableSharedFlow<Pair<Name, VisionEvent>>(meta["feedback.eventCache"].int ?: 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a custom feedback event
|
* Send a custom feedback event
|
||||||
@ -122,10 +121,7 @@ public class JsVisionClient : AbstractPlugin(), VisionClient {
|
|||||||
onmessage = { messageEvent ->
|
onmessage = { messageEvent ->
|
||||||
val stringData: String? = messageEvent.data as? String
|
val stringData: String? = messageEvent.data as? String
|
||||||
if (stringData != null) {
|
if (stringData != null) {
|
||||||
val event: VisionEvent = visionManager.jsonFormat.decodeFromString(
|
val event: VisionEvent = visionManager.jsonFormat.decodeFromString(stringData)
|
||||||
VisionEvent.serializer(),
|
|
||||||
stringData
|
|
||||||
)
|
|
||||||
|
|
||||||
// If change contains root vision replacement, do it
|
// If change contains root vision replacement, do it
|
||||||
if (event is VisionChange) {
|
if (event is VisionChange) {
|
||||||
@ -154,7 +150,7 @@ public class JsVisionClient : AbstractPlugin(), VisionClient {
|
|||||||
feedbackJob = visionManager.context.launch {
|
feedbackJob = visionManager.context.launch {
|
||||||
//launch a separate coroutine to send events to the backend
|
//launch a separate coroutine to send events to the backend
|
||||||
eventCollector.filter { it.first == visionName }.onEach {
|
eventCollector.filter { it.first == visionName }.onEach {
|
||||||
send(visionManager.jsonFormat.encodeToString(VisionEvent.serializer(), it.second))
|
send(visionManager.jsonFormat.encodeToString(it.second))
|
||||||
}.launchIn(this)
|
}.launchIn(this)
|
||||||
|
|
||||||
//aggregate atomic changes
|
//aggregate atomic changes
|
||||||
|
@ -20,6 +20,7 @@ import kotlinx.coroutines.flow.onEach
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.context.ContextAware
|
import space.kscience.dataforge.context.ContextAware
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
@ -97,9 +98,7 @@ public fun Application.serveVisionData(
|
|||||||
for (frame in incoming) {
|
for (frame in incoming) {
|
||||||
val data = frame.data.decodeToString()
|
val data = frame.data.decodeToString()
|
||||||
application.log.debug("Received event for $name: \n$data")
|
application.log.debug("Received event for $name: \n$data")
|
||||||
val event = configuration.visionManager.jsonFormat.decodeFromString(
|
val event: VisionEvent = configuration.visionManager.jsonFormat.decodeFromString(data)
|
||||||
VisionEvent.serializer(), data
|
|
||||||
)
|
|
||||||
|
|
||||||
vision.receiveEvent(event)
|
vision.receiveEvent(event)
|
||||||
}
|
}
|
||||||
@ -108,10 +107,7 @@ public fun Application.serveVisionData(
|
|||||||
try {
|
try {
|
||||||
withContext(configuration.context.coroutineContext) {
|
withContext(configuration.context.coroutineContext) {
|
||||||
vision.flowChanges(configuration.updateInterval.milliseconds).onEach { event ->
|
vision.flowChanges(configuration.updateInterval.milliseconds).onEach { event ->
|
||||||
val json = configuration.visionManager.jsonFormat.encodeToString(
|
val json = configuration.visionManager.jsonFormat.encodeToString<VisionEvent>(event)
|
||||||
VisionEvent.serializer(),
|
|
||||||
event
|
|
||||||
)
|
|
||||||
application.log.debug("Sending update for $name: \n$json")
|
application.log.debug("Sending update for $name: \n$json")
|
||||||
outgoing.send(Frame.Text(json))
|
outgoing.send(Frame.Text(json))
|
||||||
}.collect()
|
}.collect()
|
||||||
@ -155,7 +151,7 @@ public fun Application.visionPage(
|
|||||||
headers: Collection<HtmlFragment>,
|
headers: Collection<HtmlFragment>,
|
||||||
connector: EngineConnectorConfig? = null,
|
connector: EngineConnectorConfig? = null,
|
||||||
visionFragment: HtmlVisionFragment,
|
visionFragment: HtmlVisionFragment,
|
||||||
){
|
) {
|
||||||
require(WebSockets)
|
require(WebSockets)
|
||||||
|
|
||||||
val collector: MutableMap<Name, Vision> = mutableMapOf()
|
val collector: MutableMap<Name, Vision> = mutableMapOf()
|
||||||
|
Loading…
Reference in New Issue
Block a user