diff --git a/demo/playground/src/jvmMain/kotlin/formServer.kt b/demo/playground/src/jvmMain/kotlin/formServer.kt index cef13f0b..461262a5 100644 --- a/demo/playground/src/jvmMain/kotlin/formServer.kt +++ b/demo/playground/src/jvmMain/kotlin/formServer.kt @@ -31,7 +31,7 @@ fun main() { val form = VisionOfHtmlForm("form").apply { onPropertyChange(visionManager.context) { - println(this) + println(values) } } @@ -71,7 +71,7 @@ fun main() { value = "Submit" } } - + println(form.values) vision(form) } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt index 1fbc33bc..358e72aa 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt @@ -55,6 +55,8 @@ public class VisionChangeBuilder : MutableVisionContainer { private var propertyChange = MutableMeta() private val children: HashMap = HashMap() + public operator fun get(name: Name): VisionChangeBuilder? = children[name] + public fun isEmpty(): Boolean = propertyChange.isEmpty() && propertyChange.isEmpty() && children.isEmpty() @Synchronized @@ -153,7 +155,7 @@ private fun CoroutineScope.collectChange( */ public fun Vision.flowChanges( collectionDuration: Duration, - sendInitial: Boolean = false + sendInitial: Boolean = false, ): Flow = flow { val manager = manager ?: error("Orphan vision could not collect changes") coroutineScope { @@ -161,7 +163,7 @@ public fun Vision.flowChanges( val mutex = Mutex() collectChange(Name.EMPTY, this@flowChanges, mutex, collector) - if(sendInitial) { + if (sendInitial) { //Send initial vision state val initialChange = VisionChange(vision = deepCopy(manager)) emit(initialChange) diff --git a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt index 9a6547ce..6f322268 100644 --- a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt @@ -69,9 +69,9 @@ public class VisionClient : AbstractPlugin() { changeCollector.propertyChanged(visionName, propertyName, item) } - public fun visionChanged(name: Name?, child: Vision?) { - changeCollector.setChild(name, child) - } +// public fun visionChanged(name: Name?, child: Vision?) { +// changeCollector.setChild(name, child) +// } private fun renderVision(element: Element, name: Name, vision: Vision, outputMeta: Meta) { vision.setAsRoot(visionManager) @@ -79,7 +79,7 @@ public class VisionClient : AbstractPlugin() { renderer.render(element, name, vision, outputMeta) } - private fun updateVision(element: Element, name: Name, vision: Vision?, outputMeta: Meta) { + private fun startVisionUpdate(element: Element, name: Name, vision: Vision?, outputMeta: Meta) { element.attributes[OUTPUT_CONNECT_ATTRIBUTE]?.let { attr -> val wsUrl = if (attr.value.isBlank() || attr.value == VisionTagConsumer.AUTO_DATA_ATTRIBUTE) { val endpoint = resolveEndpoint(element) @@ -130,9 +130,10 @@ public class VisionClient : AbstractPlugin() { feedbackJob = visionManager.context.launch { while (isActive) { delay(feedbackAggregationTime.milliseconds) - if (!changeCollector.isEmpty()) { - send(visionManager.encodeToString(changeCollector.deepCopy(visionManager))) - changeCollector.reset() + val change = changeCollector[name] ?: continue + if (!change.isEmpty()) { + send(visionManager.encodeToString(change.deepCopy(visionManager))) + change.reset() } } } @@ -192,7 +193,7 @@ public class VisionClient : AbstractPlugin() { response.text().then { text -> val vision = visionManager.decodeFromString(text) renderVision(element, name, vision, outputMeta) - updateVision(element, name, vision, outputMeta) + startVisionUpdate(element, name, vision, outputMeta) } } else { logger.error { "Failed to fetch initial vision state from $fetchUrl" } @@ -208,12 +209,12 @@ public class VisionClient : AbstractPlugin() { } logger.info { "Found embedded vision for output with name $name" } renderVision(element, name, embeddedVision, outputMeta) - updateVision(element, name, embeddedVision, outputMeta) + startVisionUpdate(element, name, embeddedVision, outputMeta) } //Try to load vision via websocket element.attributes[OUTPUT_CONNECT_ATTRIBUTE] != null -> { - updateVision(element, name, null, outputMeta) + startVisionUpdate(element, name, null, outputMeta) } else -> error("No embedded vision data / fetch url for $name") 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 42f02e75..c05a1834 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 @@ -93,12 +93,12 @@ public fun Application.serveVisionData( webSocket("ws") { val name: String = call.request.queryParameters.getOrFail("name") application.log.debug("Opened server socket for $name") - val vision: Vision = resolveVision(Name.parse(name)) ?: error("Plot with id='$name' not registered") + val vision: Vision = resolveVision(Name.parse(name)) ?: error("Vision with id='$name' not registered") launch { for(frame in incoming) { val data = frame.data.decodeToString() - application.log.debug("Received update: \n$data") + application.log.debug("Received update for $name: \n$data") val change = configuration.visionManager.jsonFormat.decodeFromString( VisionChange.serializer(), data ) @@ -113,7 +113,7 @@ public fun Application.serveVisionData( VisionChange.serializer(), update ) - application.log.debug("Sending update: \n$json") + application.log.debug("Sending update for $name: \n$json") outgoing.send(Frame.Text(json)) }.collect() } @@ -145,27 +145,6 @@ public fun Application.serveVisionData( data: Map, ): Unit = serveVisionData(configuration) { data[it] } -// -///** -// * Compile a fragment to string and serve visions from it -// */ -//public fun Route.serveVisionsFromFragment( -// consumer: TagConsumer<*>, -// sererPageUrl: Url, -// visionManager: VisionManager, -// fragment: HtmlVisionFragment, -//): Unit { -// val visions = consumer.visionFragment( -// visionManager.context, -// embedData = true, -// fetchUpdatesUrl = "$serverUrl$route/ws", -// fragment = fragment -// ) -// -// serveVisionData(visionManager, visions) -//} - - /** * Serve a page, potentially containing any number of visions at a given [route] with given [header]. */