forked from kscience/visionforge
Refactor server API
This commit is contained in:
parent
fd8f693151
commit
fef1df3ab4
@ -10,13 +10,14 @@ import space.kscience.visionforge.jupyter.VFIntegrationBase
|
|||||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||||
import space.kscience.visionforge.plotly.asVision
|
import space.kscience.visionforge.plotly.asVision
|
||||||
import space.kscience.visionforge.solid.Solids
|
import space.kscience.visionforge.solid.Solids
|
||||||
|
import space.kscience.visionforge.visionManager
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
internal class VisionForgePlayGroundForJupyter : VFIntegrationBase(
|
internal class VisionForgePlayGroundForJupyter : VFIntegrationBase(
|
||||||
Context("VisionForge") {
|
Context("VisionForge") {
|
||||||
plugin(Solids)
|
plugin(Solids)
|
||||||
plugin(PlotlyPlugin)
|
plugin(PlotlyPlugin)
|
||||||
}
|
}.visionManager
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun Builder.afterLoaded() {
|
override fun Builder.afterLoaded() {
|
||||||
|
@ -8,8 +8,9 @@ import kotlinx.html.*
|
|||||||
import space.kscience.dataforge.context.Global
|
import space.kscience.dataforge.context.Global
|
||||||
import space.kscience.dataforge.context.fetch
|
import space.kscience.dataforge.context.fetch
|
||||||
import space.kscience.visionforge.VisionManager
|
import space.kscience.visionforge.VisionManager
|
||||||
|
import space.kscience.visionforge.html.VisionOfHtmlForm
|
||||||
import space.kscience.visionforge.html.VisionPage
|
import space.kscience.visionforge.html.VisionPage
|
||||||
import space.kscience.visionforge.html.formFragment
|
import space.kscience.visionforge.html.bindForm
|
||||||
import space.kscience.visionforge.onPropertyChange
|
import space.kscience.visionforge.onPropertyChange
|
||||||
import space.kscience.visionforge.server.EngineConnectorConfig
|
import space.kscience.visionforge.server.EngineConnectorConfig
|
||||||
import space.kscience.visionforge.server.close
|
import space.kscience.visionforge.server.close
|
||||||
@ -28,8 +29,18 @@ fun main() {
|
|||||||
resources()
|
resources()
|
||||||
}
|
}
|
||||||
|
|
||||||
visionPage(connector, visionManager, VisionPage.scriptHeader("js/visionforge-playground.js")) {
|
val form = VisionOfHtmlForm("form").apply {
|
||||||
val form = formFragment("form") {
|
onPropertyChange(visionManager.context) {
|
||||||
|
println(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visionPage(
|
||||||
|
connector,
|
||||||
|
visionManager,
|
||||||
|
VisionPage.scriptHeader("js/visionforge-playground.js"),
|
||||||
|
) {
|
||||||
|
bindForm(form) {
|
||||||
label {
|
label {
|
||||||
htmlFor = "fname"
|
htmlFor = "fname"
|
||||||
+"First name:"
|
+"First name:"
|
||||||
@ -61,10 +72,7 @@ fun main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vision("form") { form }
|
vision(form)
|
||||||
form.onPropertyChange {
|
|
||||||
println(this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}.start(false)
|
}.start(false)
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
package space.kscience.visionforge.examples
|
package space.kscience.visionforge.examples
|
||||||
|
|
||||||
import space.kscience.dataforge.context.Global
|
import space.kscience.dataforge.context.Global
|
||||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
import space.kscience.visionforge.html.*
|
||||||
import space.kscience.visionforge.html.ResourceLocation
|
|
||||||
import space.kscience.visionforge.html.VisionPage
|
|
||||||
import space.kscience.visionforge.html.importScriptHeader
|
|
||||||
import space.kscience.visionforge.makeFile
|
|
||||||
import space.kscience.visionforge.visionManager
|
import space.kscience.visionforge.visionManager
|
||||||
import java.awt.Desktop
|
import java.awt.Desktop
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
@ -20,10 +20,10 @@ import space.kscience.dataforge.context.logger
|
|||||||
import space.kscience.dataforge.meta.get
|
import space.kscience.dataforge.meta.get
|
||||||
import space.kscience.dataforge.meta.int
|
import space.kscience.dataforge.meta.int
|
||||||
import space.kscience.dataforge.meta.string
|
import space.kscience.dataforge.meta.string
|
||||||
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.visionforge.Vision
|
||||||
import space.kscience.visionforge.VisionManager
|
import space.kscience.visionforge.VisionManager
|
||||||
import space.kscience.visionforge.html.HtmlFormFragment
|
|
||||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||||
import space.kscience.visionforge.html.VisionCollector
|
|
||||||
import space.kscience.visionforge.html.visionFragment
|
import space.kscience.visionforge.html.visionFragment
|
||||||
import space.kscience.visionforge.server.EngineConnectorConfig
|
import space.kscience.visionforge.server.EngineConnectorConfig
|
||||||
import space.kscience.visionforge.server.VisionRoute
|
import space.kscience.visionforge.server.VisionRoute
|
||||||
@ -107,7 +107,7 @@ public class VFForNotebook(override val context: Context) : ContextAware, Corout
|
|||||||
//server.serveVisionsFromFragment(consumer, "content-${counter++}", fragment)
|
//server.serveVisionsFromFragment(consumer, "content-${counter++}", fragment)
|
||||||
val cellRoute = "content-${counter++}"
|
val cellRoute = "content-${counter++}"
|
||||||
|
|
||||||
val collector: VisionCollector = mutableMapOf()
|
val collector: MutableMap<Name, Vision> = mutableMapOf()
|
||||||
|
|
||||||
val url = engine.environment.connectors.first().let {
|
val url = engine.environment.connectors.first().let {
|
||||||
url {
|
url {
|
||||||
@ -121,15 +121,15 @@ public class VFForNotebook(override val context: Context) : ContextAware, Corout
|
|||||||
engine.application.serveVisionData(VisionRoute(cellRoute, visionManager), collector)
|
engine.application.serveVisionData(VisionRoute(cellRoute, visionManager), collector)
|
||||||
|
|
||||||
visionFragment(
|
visionFragment(
|
||||||
context,
|
visionManager,
|
||||||
embedData = true,
|
embedData = true,
|
||||||
updatesUrl = url,
|
updatesUrl = url,
|
||||||
collector = collector,
|
onVisionRendered = { name, vision -> collector[name] = vision },
|
||||||
fragment = fragment
|
fragment = fragment
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
//if not, use static rendering
|
//if not, use static rendering
|
||||||
visionFragment(context, fragment = fragment)
|
visionFragment(visionManager, fragment = fragment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
renderScriptForId(id)
|
renderScriptForId(id)
|
||||||
|
@ -9,6 +9,7 @@ import space.kscience.dataforge.context.Context
|
|||||||
import space.kscience.dataforge.context.ContextAware
|
import space.kscience.dataforge.context.ContextAware
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.visionforge.Vision
|
import space.kscience.visionforge.Vision
|
||||||
|
import space.kscience.visionforge.VisionManager
|
||||||
import space.kscience.visionforge.html.*
|
import space.kscience.visionforge.html.*
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import kotlin.random.nextUInt
|
import kotlin.random.nextUInt
|
||||||
@ -17,9 +18,12 @@ import kotlin.random.nextUInt
|
|||||||
* A base class for different Jupyter VF integrations
|
* A base class for different Jupyter VF integrations
|
||||||
*/
|
*/
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public abstract class VFIntegrationBase(final override val context: Context) : JupyterIntegration(), ContextAware {
|
public abstract class VFIntegrationBase(
|
||||||
|
public val visionManager: VisionManager,
|
||||||
|
) : JupyterIntegration(), ContextAware {
|
||||||
|
|
||||||
protected val handler: VFForNotebook = VFForNotebook(context)
|
override val context: Context get() = visionManager.context
|
||||||
|
protected val handler: VFForNotebook = VFForNotebook(visionManager.context)
|
||||||
|
|
||||||
protected abstract fun Builder.afterLoaded()
|
protected abstract fun Builder.afterLoaded()
|
||||||
|
|
||||||
@ -67,7 +71,7 @@ public abstract class VFIntegrationBase(final override val context: Context) : J
|
|||||||
val id = "fragment[${page.hashCode()}/${Random.nextUInt()}]"
|
val id = "fragment[${page.hashCode()}/${Random.nextUInt()}]"
|
||||||
div {
|
div {
|
||||||
this.id = id
|
this.id = id
|
||||||
visionFragment(context, fragment = page.content)
|
visionFragment(visionManager, fragment = page.content)
|
||||||
}
|
}
|
||||||
renderScriptForId(id)
|
renderScriptForId(id)
|
||||||
}
|
}
|
||||||
|
27
jupyter/src/jvmMain/kotlin/forms.kt
Normal file
27
jupyter/src/jvmMain/kotlin/forms.kt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package space.kscience.visionforge.jupyter
|
||||||
|
|
||||||
|
import kotlinx.html.FORM
|
||||||
|
import kotlinx.html.form
|
||||||
|
import kotlinx.html.id
|
||||||
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.get
|
||||||
|
import space.kscience.visionforge.html.HtmlFragment
|
||||||
|
import space.kscience.visionforge.html.VisionOfHtmlForm
|
||||||
|
|
||||||
|
public class HtmlFormFragment internal constructor(
|
||||||
|
public val vision: VisionOfHtmlForm,
|
||||||
|
public val formBody: HtmlFragment,
|
||||||
|
){
|
||||||
|
public val values: Meta? get() = vision.values
|
||||||
|
public operator fun get(valueName: String): Meta? = values?.get(valueName)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun HtmlFormFragment(id: String? = null, builder: FORM.() -> Unit): HtmlFormFragment {
|
||||||
|
val realId = id ?: "form[${builder.hashCode().toUInt()}]"
|
||||||
|
return HtmlFormFragment(VisionOfHtmlForm(realId)) {
|
||||||
|
form {
|
||||||
|
this.id = realId
|
||||||
|
builder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,12 +7,13 @@ import space.kscience.gdml.Gdml
|
|||||||
import space.kscience.visionforge.gdml.toVision
|
import space.kscience.visionforge.gdml.toVision
|
||||||
import space.kscience.visionforge.jupyter.VFIntegrationBase
|
import space.kscience.visionforge.jupyter.VFIntegrationBase
|
||||||
import space.kscience.visionforge.solid.Solids
|
import space.kscience.visionforge.solid.Solids
|
||||||
|
import space.kscience.visionforge.visionManager
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
internal class GdmlForJupyter : VFIntegrationBase(
|
internal class GdmlForJupyter : VFIntegrationBase(
|
||||||
Context("GDML") {
|
Context("GDML") {
|
||||||
plugin(Solids)
|
plugin(Solids)
|
||||||
}
|
}.visionManager
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun Builder.afterLoaded() {
|
override fun Builder.afterLoaded() {
|
||||||
|
@ -25,7 +25,7 @@ public class ThreeWithControlsPlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
override fun rateVision(vision: Vision): Int =
|
override fun rateVision(vision: Vision): Int =
|
||||||
if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING * 2 else ElementVisionRenderer.ZERO_RATING
|
if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING * 2 else ElementVisionRenderer.ZERO_RATING
|
||||||
|
|
||||||
override fun render(element: Element, vision: Vision, meta: Meta) {
|
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
||||||
createRoot(element).render {
|
createRoot(element).render {
|
||||||
child(ThreeCanvasWithControls) {
|
child(ThreeCanvasWithControls) {
|
||||||
attrs {
|
attrs {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package space.kscience.visionforge.html
|
package space.kscience.visionforge.html
|
||||||
|
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
import space.kscience.dataforge.context.Context
|
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
@ -15,8 +14,6 @@ public typealias HtmlVisionFragment = VisionTagConsumer<*>.() -> Unit
|
|||||||
@DFExperimental
|
@DFExperimental
|
||||||
public fun HtmlVisionFragment(content: VisionTagConsumer<*>.() -> Unit): HtmlVisionFragment = content
|
public fun HtmlVisionFragment(content: VisionTagConsumer<*>.() -> Unit): HtmlVisionFragment = content
|
||||||
|
|
||||||
public typealias VisionCollector = MutableMap<Name, Pair<VisionOutput, Vision>>
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a fragment in the given consumer and return a map of extracted visions
|
* Render a fragment in the given consumer and return a map of extracted visions
|
||||||
@ -27,15 +24,18 @@ public typealias VisionCollector = MutableMap<Name, Pair<VisionOutput, Vision>>
|
|||||||
* @param idPrefix a prefix to be used before vision ids
|
* @param idPrefix a prefix to be used before vision ids
|
||||||
*/
|
*/
|
||||||
public fun TagConsumer<*>.visionFragment(
|
public fun TagConsumer<*>.visionFragment(
|
||||||
context: Context,
|
visionManager: VisionManager,
|
||||||
embedData: Boolean = true,
|
embedData: Boolean = true,
|
||||||
fetchDataUrl: String? = null,
|
fetchDataUrl: String? = null,
|
||||||
updatesUrl: String? = null,
|
updatesUrl: String? = null,
|
||||||
idPrefix: String? = null,
|
idPrefix: String? = null,
|
||||||
collector: VisionCollector = mutableMapOf(),
|
onVisionRendered: (Name, Vision) -> Unit = { _, _ -> },
|
||||||
fragment: HtmlVisionFragment,
|
fragment: HtmlVisionFragment,
|
||||||
) {
|
) {
|
||||||
val consumer = object : VisionTagConsumer<Any?>(this@visionFragment, context, idPrefix) {
|
|
||||||
|
val collector: MutableMap<Name, Pair<VisionOutput, Vision>> = mutableMapOf()
|
||||||
|
|
||||||
|
val consumer = object : VisionTagConsumer<Any?>(this@visionFragment, visionManager, idPrefix) {
|
||||||
|
|
||||||
override fun <T> TagConsumer<T>.vision(name: Name?, buildOutput: VisionOutput.() -> Vision): T {
|
override fun <T> TagConsumer<T>.vision(name: Name?, buildOutput: VisionOutput.() -> Vision): T {
|
||||||
//Avoid re-creating cached visions
|
//Avoid re-creating cached visions
|
||||||
@ -47,6 +47,7 @@ public fun TagConsumer<*>.visionFragment(
|
|||||||
val (output, vision) = collector.getOrPut(actualName) {
|
val (output, vision) = collector.getOrPut(actualName) {
|
||||||
val output = VisionOutput(context, actualName)
|
val output = VisionOutput(context, actualName)
|
||||||
val vision = output.buildOutput()
|
val vision = output.buildOutput()
|
||||||
|
onVisionRendered(actualName, vision)
|
||||||
output to vision
|
output to vision
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,8 +55,15 @@ public fun TagConsumer<*>.visionFragment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun DIV.renderVision(manager: VisionManager, name: Name, vision: Vision, outputMeta: Meta) {
|
override fun DIV.renderVision(manager: VisionManager, name: Name, vision: Vision, outputMeta: Meta) {
|
||||||
// Toggle update mode
|
|
||||||
|
|
||||||
|
val (_, actualVision) = collector.getOrPut(name) {
|
||||||
|
val output = VisionOutput(context, name)
|
||||||
|
onVisionRendered(name, vision)
|
||||||
|
output to vision
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Toggle update mode
|
||||||
updatesUrl?.let {
|
updatesUrl?.let {
|
||||||
attributes[OUTPUT_CONNECT_ATTRIBUTE] = it
|
attributes[OUTPUT_CONNECT_ATTRIBUTE] = it
|
||||||
}
|
}
|
||||||
@ -69,7 +77,7 @@ public fun TagConsumer<*>.visionFragment(
|
|||||||
type = "text/json"
|
type = "text/json"
|
||||||
attributes["class"] = OUTPUT_DATA_CLASS
|
attributes["class"] = OUTPUT_DATA_CLASS
|
||||||
unsafe {
|
unsafe {
|
||||||
+"\n${manager.encodeToString(vision)}\n"
|
+"\n${manager.encodeToString(actualVision)}\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,19 +88,20 @@ public fun TagConsumer<*>.visionFragment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
public fun FlowContent.visionFragment(
|
public fun FlowContent.visionFragment(
|
||||||
context: Context,
|
visionManager: VisionManager,
|
||||||
embedData: Boolean = true,
|
embedData: Boolean = true,
|
||||||
fetchDataUrl: String? = null,
|
fetchDataUrl: String? = null,
|
||||||
updatesUrl: String? = null,
|
updatesUrl: String? = null,
|
||||||
|
onVisionRendered: (Name, Vision) -> Unit = { _, _ -> },
|
||||||
idPrefix: String? = null,
|
idPrefix: String? = null,
|
||||||
visionCache: VisionCollector = mutableMapOf(),
|
|
||||||
fragment: HtmlVisionFragment,
|
fragment: HtmlVisionFragment,
|
||||||
): Unit = consumer.visionFragment(
|
): Unit = consumer.visionFragment(
|
||||||
context,
|
visionManager = visionManager,
|
||||||
embedData,
|
embedData = embedData,
|
||||||
fetchDataUrl,
|
fetchDataUrl = fetchDataUrl,
|
||||||
updatesUrl,
|
updatesUrl = updatesUrl,
|
||||||
idPrefix,
|
idPrefix = idPrefix,
|
||||||
visionCache,
|
onVisionRendered = onVisionRendered,
|
||||||
fragment = fragment
|
fragment = fragment
|
||||||
)
|
)
|
@ -7,7 +7,6 @@ import kotlinx.html.id
|
|||||||
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.get
|
|
||||||
import space.kscience.dataforge.meta.node
|
import space.kscience.dataforge.meta.node
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -18,29 +17,10 @@ public class VisionOfHtmlForm(
|
|||||||
public var values: Meta? by mutableProperties.node()
|
public var values: Meta? by mutableProperties.node()
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HtmlFormFragment internal constructor(
|
public fun <R> TagConsumer<R>.bindForm(
|
||||||
public val vision: VisionOfHtmlForm,
|
visionOfForm: VisionOfHtmlForm,
|
||||||
public val formBody: HtmlFragment,
|
builder: FORM.() -> Unit,
|
||||||
){
|
): R = form {
|
||||||
public val values: Meta? get() = vision.values
|
this.id = visionOfForm.formId
|
||||||
public operator fun get(valueName: String): Meta? = values?.get(valueName)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun HtmlFormFragment(id: String? = null, builder: FORM.() -> Unit): HtmlFormFragment {
|
|
||||||
val realId = id ?: "form[${builder.hashCode().toUInt()}]"
|
|
||||||
return HtmlFormFragment(VisionOfHtmlForm(realId)) {
|
|
||||||
form {
|
|
||||||
this.id = realId
|
|
||||||
builder()
|
builder()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun <R> TagConsumer<R>.formFragment(
|
|
||||||
id: String? = null,
|
|
||||||
builder: FORM.() -> Unit,
|
|
||||||
): VisionOfHtmlForm {
|
|
||||||
val formFragment = HtmlFormFragment(id, builder)
|
|
||||||
fragment(formFragment.formBody)
|
|
||||||
return formFragment.vision
|
|
||||||
}
|
|
@ -2,6 +2,7 @@ package space.kscience.visionforge.html
|
|||||||
|
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
|
import space.kscience.dataforge.context.ContextAware
|
||||||
import space.kscience.dataforge.context.PluginFactory
|
import space.kscience.dataforge.context.PluginFactory
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.MetaSerializer
|
import space.kscience.dataforge.meta.MetaSerializer
|
||||||
@ -56,9 +57,11 @@ public class VisionOutput @PublishedApi internal constructor(public val context:
|
|||||||
@VisionDSL
|
@VisionDSL
|
||||||
public abstract class VisionTagConsumer<R>(
|
public abstract class VisionTagConsumer<R>(
|
||||||
private val root: TagConsumer<R>,
|
private val root: TagConsumer<R>,
|
||||||
public val context: Context,
|
public val visionManager: VisionManager,
|
||||||
private val idPrefix: String? = null,
|
private val idPrefix: String? = null,
|
||||||
) : TagConsumer<R> by root {
|
) : TagConsumer<R> by root, ContextAware {
|
||||||
|
|
||||||
|
override val context: Context get() = visionManager.context
|
||||||
|
|
||||||
public open fun resolveId(name: Name): String = (idPrefix ?: "output") + "[$name]"
|
public open fun resolveId(name: Name): String = (idPrefix ?: "output") + "[$name]"
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ fun FlowContent.renderVisionFragment(
|
|||||||
fragment: HtmlVisionFragment,
|
fragment: HtmlVisionFragment,
|
||||||
): Map<Name, Vision> {
|
): Map<Name, Vision> {
|
||||||
val visionMap = HashMap<Name, Vision>()
|
val visionMap = HashMap<Name, Vision>()
|
||||||
val consumer = object : VisionTagConsumer<Any?>(consumer, Global, idPrefix) {
|
val consumer = object : VisionTagConsumer<Any?>(consumer, Global.visionManager, idPrefix) {
|
||||||
override fun DIV.renderVision(manager: VisionManager, name: Name, vision: Vision, outputMeta: Meta) {
|
override fun DIV.renderVision(manager: VisionManager, name: Name, vision: Vision, outputMeta: Meta) {
|
||||||
visionMap[name] = vision
|
visionMap[name] = vision
|
||||||
renderer(name, vision, outputMeta)
|
renderer(name, vision, outputMeta)
|
||||||
|
@ -4,6 +4,7 @@ import kotlinx.browser.document
|
|||||||
import kotlinx.browser.window
|
import kotlinx.browser.window
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.w3c.dom.*
|
import org.w3c.dom.*
|
||||||
import org.w3c.dom.url.URL
|
import org.w3c.dom.url.URL
|
||||||
@ -68,18 +69,6 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
changeCollector.propertyChanged(visionName, propertyName, item)
|
changeCollector.propertyChanged(visionName, propertyName, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun visionPropertyChanged(visionName: Name, propertyName: Name, item: Boolean) {
|
|
||||||
visionPropertyChanged(visionName, propertyName, Meta(item))
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun visionPropertyChanged(visionName: Name, propertyName: Name, item: String) {
|
|
||||||
visionPropertyChanged(visionName, propertyName, Meta(item))
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun visionPropertyChanged(visionName: Name, propertyName: Name, item: Number) {
|
|
||||||
visionPropertyChanged(visionName, propertyName, Meta(item))
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun visionChanged(name: Name?, child: Vision?) {
|
public fun visionChanged(name: Name?, child: Vision?) {
|
||||||
changeCollector.setChild(name, child)
|
changeCollector.setChild(name, child)
|
||||||
}
|
}
|
||||||
@ -139,12 +128,14 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
|
|
||||||
onopen = {
|
onopen = {
|
||||||
feedbackJob = visionManager.context.launch {
|
feedbackJob = visionManager.context.launch {
|
||||||
|
while (isActive) {
|
||||||
delay(feedbackAggregationTime.milliseconds)
|
delay(feedbackAggregationTime.milliseconds)
|
||||||
if (!changeCollector.isEmpty()) {
|
if (!changeCollector.isEmpty()) {
|
||||||
send(visionManager.encodeToString(changeCollector.deepCopy(visionManager)))
|
send(visionManager.encodeToString(changeCollector.deepCopy(visionManager)))
|
||||||
changeCollector.reset()
|
changeCollector.reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
logger.info { "WebSocket update channel established for output '$name'" }
|
logger.info { "WebSocket update channel established for output '$name'" }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,6 +238,21 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun VisionClient.visionPropertyChanged(visionName: Name, propertyName: String, item: Meta?) {
|
||||||
|
visionPropertyChanged(visionName, propertyName.parseAsName(true), item)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun VisionClient.visionPropertyChanged(visionName: Name, propertyName: String, item: Number) {
|
||||||
|
visionPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun VisionClient.visionPropertyChanged(visionName: Name, propertyName: String, item: String) {
|
||||||
|
visionPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun VisionClient.visionPropertyChanged(visionName: Name, propertyName: String, item: Boolean) {
|
||||||
|
visionPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
|
||||||
|
}
|
||||||
|
|
||||||
private fun whenDocumentLoaded(block: Document.() -> Unit): Unit {
|
private fun whenDocumentLoaded(block: Document.() -> Unit): Unit {
|
||||||
if (document.body != null) {
|
if (document.body != null) {
|
||||||
|
@ -9,8 +9,11 @@ import org.w3c.dom.HTMLFormElement
|
|||||||
import org.w3c.dom.HTMLInputElement
|
import org.w3c.dom.HTMLInputElement
|
||||||
import org.w3c.dom.get
|
import org.w3c.dom.get
|
||||||
import org.w3c.xhr.FormData
|
import org.w3c.xhr.FormData
|
||||||
|
import space.kscience.dataforge.context.debug
|
||||||
|
import space.kscience.dataforge.context.logger
|
||||||
import space.kscience.dataforge.meta.DynamicMeta
|
import space.kscience.dataforge.meta.DynamicMeta
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.toMap
|
||||||
import space.kscience.dataforge.meta.valueSequence
|
import space.kscience.dataforge.meta.valueSequence
|
||||||
import space.kscience.visionforge.html.VisionOfHtmlForm
|
import space.kscience.visionforge.html.VisionOfHtmlForm
|
||||||
import space.kscience.visionforge.html.VisionOfNumberField
|
import space.kscience.visionforge.html.VisionOfNumberField
|
||||||
@ -33,7 +36,7 @@ internal fun textVisionRenderer(
|
|||||||
value = it ?: ""
|
value = it ?: ""
|
||||||
}
|
}
|
||||||
onChangeFunction = {
|
onChangeFunction = {
|
||||||
// client.visionPropertyChanged(name, VisionOfTextField::text.name.pa, value)
|
client.visionPropertyChanged(name, VisionOfTextField::text.name, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,7 +58,7 @@ internal fun numberVisionRenderer(
|
|||||||
value = it?.toDouble() ?: 0.0
|
value = it?.toDouble() ?: 0.0
|
||||||
}
|
}
|
||||||
onChangeFunction = {
|
onChangeFunction = {
|
||||||
// vision.value = value.toDoubleOrNull()
|
client.visionPropertyChanged(name, VisionOfNumberField::value.name, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,9 +93,10 @@ internal fun formVisionRenderer(
|
|||||||
val form = document.getElementById(vision.formId) as? HTMLFormElement
|
val form = document.getElementById(vision.formId) as? HTMLFormElement
|
||||||
?: error("An element with id = '${vision.formId} is not a form")
|
?: error("An element with id = '${vision.formId} is not a form")
|
||||||
|
|
||||||
console.info("Adding hooks to form '$form'")
|
client.logger.debug{"Adding hooks to form with id = '$vision.formId'"}
|
||||||
|
|
||||||
vision.useProperty(VisionOfHtmlForm::values) { values ->
|
vision.useProperty(VisionOfHtmlForm::values) { values ->
|
||||||
|
client.logger.debug{"Updating form '${vision.formId}' with values $values"}
|
||||||
val inputs = form.getElementsByTagName("input")
|
val inputs = form.getElementsByTagName("input")
|
||||||
values?.valueSequence()?.forEach { (token, value) ->
|
values?.valueSequence()?.forEach { (token, value) ->
|
||||||
(inputs[token.toString()] as? HTMLInputElement)?.value = value.toString()
|
(inputs[token.toString()] as? HTMLInputElement)?.value = value.toString()
|
||||||
@ -102,8 +106,8 @@ internal fun formVisionRenderer(
|
|||||||
form.onsubmit = { event ->
|
form.onsubmit = { event ->
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
val formData = FormData(form).toMeta()
|
val formData = FormData(form).toMeta()
|
||||||
//console.log(formData.toString())
|
client.visionPropertyChanged(name, VisionOfHtmlForm::values.name, formData)
|
||||||
//vision.values = formData
|
console.info("Sent: ${formData.toMap()}")
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.visionforge
|
package space.kscience.visionforge.html
|
||||||
|
|
||||||
import kotlinx.html.body
|
import kotlinx.html.body
|
||||||
import kotlinx.html.head
|
import kotlinx.html.head
|
||||||
@ -6,10 +6,7 @@ import kotlinx.html.meta
|
|||||||
import kotlinx.html.stream.createHTML
|
import kotlinx.html.stream.createHTML
|
||||||
import space.kscience.dataforge.context.Global
|
import space.kscience.dataforge.context.Global
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.visionforge.html.HtmlFragment
|
import space.kscience.visionforge.visionManager
|
||||||
import space.kscience.visionforge.html.VisionPage
|
|
||||||
import space.kscience.visionforge.html.fragment
|
|
||||||
import space.kscience.visionforge.html.visionFragment
|
|
||||||
import java.awt.Desktop
|
import java.awt.Desktop
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -87,7 +84,7 @@ public fun VisionPage.makeFile(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
visionFragment(Global, fragment = content)
|
visionFragment(Global.visionManager, fragment = content)
|
||||||
}
|
}
|
||||||
}.finalize()
|
}.finalize()
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer {
|
|||||||
else -> ElementVisionRenderer.ZERO_RATING
|
else -> ElementVisionRenderer.ZERO_RATING
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(element: Element, vision: Vision, meta: Meta) {
|
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
||||||
val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found")
|
val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found")
|
||||||
val config = PlotlyConfig.read(meta)
|
val config = PlotlyConfig.read(meta)
|
||||||
element.plot(plot, config)
|
element.plot(plot, config)
|
||||||
|
@ -15,7 +15,6 @@ import io.ktor.server.util.*
|
|||||||
import io.ktor.server.websocket.*
|
import io.ktor.server.websocket.*
|
||||||
import io.ktor.util.pipeline.*
|
import io.ktor.util.pipeline.*
|
||||||
import io.ktor.websocket.*
|
import io.ktor.websocket.*
|
||||||
import kotlinx.coroutines.channels.consumeEach
|
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -97,8 +96,8 @@ public fun Application.serveVisionData(
|
|||||||
val vision: Vision = resolveVision(Name.parse(name)) ?: error("Plot with id='$name' not registered")
|
val vision: Vision = resolveVision(Name.parse(name)) ?: error("Plot with id='$name' not registered")
|
||||||
|
|
||||||
launch {
|
launch {
|
||||||
incoming.consumeEach {
|
for(frame in incoming) {
|
||||||
val data = it.data.decodeToString()
|
val data = frame.data.decodeToString()
|
||||||
application.log.debug("Received update: \n$data")
|
application.log.debug("Received update: \n$data")
|
||||||
val change = configuration.visionManager.jsonFormat.decodeFromString(
|
val change = configuration.visionManager.jsonFormat.decodeFromString(
|
||||||
VisionChange.serializer(), data
|
VisionChange.serializer(), data
|
||||||
@ -143,8 +142,8 @@ public fun Application.serveVisionData(
|
|||||||
|
|
||||||
public fun Application.serveVisionData(
|
public fun Application.serveVisionData(
|
||||||
configuration: VisionRoute,
|
configuration: VisionRoute,
|
||||||
cache: VisionCollector,
|
data: Map<Name, Vision>,
|
||||||
): Unit = serveVisionData(configuration) { cache[it]?.second }
|
): Unit = serveVisionData(configuration) { data[it] }
|
||||||
|
|
||||||
//
|
//
|
||||||
///**
|
///**
|
||||||
@ -179,7 +178,7 @@ public fun Application.visionPage(
|
|||||||
) {
|
) {
|
||||||
require(WebSockets)
|
require(WebSockets)
|
||||||
|
|
||||||
val collector: VisionCollector = mutableMapOf()
|
val collector: MutableMap<Name, Vision> = mutableMapOf()
|
||||||
|
|
||||||
val html = createHTML().apply {
|
val html = createHTML().apply {
|
||||||
head {
|
head {
|
||||||
@ -193,7 +192,7 @@ public fun Application.visionPage(
|
|||||||
body {
|
body {
|
||||||
//Load the fragment and remember all loaded visions
|
//Load the fragment and remember all loaded visions
|
||||||
visionFragment(
|
visionFragment(
|
||||||
context = configuration.context,
|
visionManager = configuration.visionManager,
|
||||||
embedData = configuration.dataMode == VisionRoute.Mode.EMBED,
|
embedData = configuration.dataMode == VisionRoute.Mode.EMBED,
|
||||||
fetchDataUrl = if (configuration.dataMode != VisionRoute.Mode.EMBED) {
|
fetchDataUrl = if (configuration.dataMode != VisionRoute.Mode.EMBED) {
|
||||||
url {
|
url {
|
||||||
@ -210,7 +209,7 @@ public fun Application.visionPage(
|
|||||||
path(route, "ws")
|
path(route, "ws")
|
||||||
}
|
}
|
||||||
} else null,
|
} else null,
|
||||||
visionCache = collector,
|
onVisionRendered = { name, vision -> collector[name] = vision },
|
||||||
fragment = visionFragment
|
fragment = visionFragment
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ public class TableVisionJsPlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
else -> ElementVisionRenderer.ZERO_RATING
|
else -> ElementVisionRenderer.ZERO_RATING
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(element: Element, vision: Vision, meta: Meta) {
|
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
||||||
val table: VisionOfTable = (vision as? VisionOfTable)
|
val table: VisionOfTable = (vision as? VisionOfTable)
|
||||||
?: error("VisionOfTable expected but ${vision::class} found")
|
?: error("VisionOfTable expected but ${vision::class} found")
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
render(vision)
|
render(vision)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(element: Element, vision: Vision, meta: Meta) {
|
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
||||||
renderSolid(
|
renderSolid(
|
||||||
element,
|
element,
|
||||||
vision as? Solid ?: error("Solid expected but ${vision::class} found"),
|
vision as? Solid ?: error("Solid expected but ${vision::class} found"),
|
||||||
|
@ -3,7 +3,6 @@ package space.kscience.visionforge.three
|
|||||||
import space.kscience.dataforge.context.Global
|
import space.kscience.dataforge.context.Global
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.visionforge.html.*
|
import space.kscience.visionforge.html.*
|
||||||
import space.kscience.visionforge.makeFile
|
|
||||||
import space.kscience.visionforge.visionManager
|
import space.kscience.visionforge.visionManager
|
||||||
import java.awt.Desktop
|
import java.awt.Desktop
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
Loading…
Reference in New Issue
Block a user