From 1ae7e7c3d4451d633e6d187c662cf3c0c744f1f6 Mon Sep 17 00:00:00 2001 From: Sabina Abdiganieva Date: Fri, 16 Feb 2024 01:26:14 +0300 Subject: [PATCH] bind device toggles with UI only flow apllication -> webpage is supported now --- .../control/readvac/VacCollectorDisplay.kt | 1 + .../inr/numass/control/readvac/VacDisplay.kt | 2 + .../main/java/inr/numass/webcontrol/Server.kt | 45 ++++++++++++++++++- .../src/main/resources/index.html | 1 + .../src/main/resources/script.js | 25 ++++++++++- .../src/main/resources/styles.css | 14 +++++- 6 files changed, 84 insertions(+), 4 deletions(-) diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDisplay.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDisplay.kt index a0c9eff..b20e931 100644 --- a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDisplay.kt +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacCollectorDisplay.kt @@ -84,6 +84,7 @@ class VacCollectorDisplay : DeviceDisplayFX() { } separator(Orientation.VERTICAL) togglebutton("Log") { + this.id = "Log" isSelected = false LogFragment().apply { addLogHandler(device.logger) diff --git a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacDisplay.kt b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacDisplay.kt index 6ce8c9e..ec025e2 100644 --- a/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacDisplay.kt +++ b/numass-control/vac/src/main/kotlin/inr/numass/control/readvac/VacDisplay.kt @@ -121,6 +121,7 @@ open class VacDisplay : DeviceDisplayFX() { value = "---" } } + id = device.name } } } @@ -161,6 +162,7 @@ open class VacDisplay : DeviceDisplayFX() { minHeight = 30.0 vgrow = Priority.ALWAYS switch("Power") { + this.id = "power" alignment = Pos.CENTER booleanStateProperty("power").bindBidirectional(selectedProperty()) } diff --git a/numass-web-control/src/main/java/inr/numass/webcontrol/Server.kt b/numass-web-control/src/main/java/inr/numass/webcontrol/Server.kt index d5998bc..3934947 100644 --- a/numass-web-control/src/main/java/inr/numass/webcontrol/Server.kt +++ b/numass-web-control/src/main/java/inr/numass/webcontrol/Server.kt @@ -1,7 +1,11 @@ package inr.numass.webcontrol +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.AppenderBase +import hep.dataforge.get import hep.dataforge.io.JSONMetaWriter import inr.numass.control.readvac.ReadVac +import inr.numass.control.readvac.VacCollectorDevice import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.engine.* @@ -15,6 +19,7 @@ import javafx.application.Application import javafx.scene.control.ToggleButton import javafx.stage.Stage import kotlinx.coroutines.runBlocking +import org.controlsfx.control.ToggleSwitch import java.io.ByteArrayOutputStream import java.nio.file.Paths import java.util.* @@ -29,15 +34,32 @@ class Connection(val session: DefaultWebSocketServerSession) { class ReadVacSvr : ReadVac() { var server : NettyApplicationEngine? = null + private var device : VacCollectorDevice? = null + override fun setupStage(stage: Stage, device: VacCollectorDevice) { + super.setupStage(stage, device) + this.device = device + } override fun start(stage: Stage) { super.start(stage) val measureButton = stage.scene.lookup("#Measure") as ToggleButton val storeButton = stage.scene.lookup("#Store") as ToggleButton + val sensors = this@ReadVacSvr.getDeviceMeta(this@ReadVacSvr.getDeviceConfig()).getMetaList("sensor").map { it["name"] } this.server = embeddedServer(Netty, port = 8000) { install(WebSockets) routing { staticFiles("/", Paths.get(this.javaClass.classLoader.getResource("index.html").toURI()).toFile().parentFile) val connections = Collections.synchronizedSet(LinkedHashSet()) + for (sensor in sensors) { + (stage.scene.lookup("#${sensor}") as ToggleSwitch).selectedProperty().addListener{ _, oldValue, newValue -> + if (oldValue != newValue) { + connections.forEach { + runBlocking { + it.session.send("{ \"sensor\": {\"${sensor}\": ${newValue}}}") + } + } + } + } + } measureButton.selectedProperty().addListener { _, oldValue, newValue -> if (oldValue != newValue) { connections.forEach { @@ -56,6 +78,22 @@ class ReadVacSvr : ReadVac() { } } } + (device!!.logger as ch.qos.logback.classic.Logger).addAppender( + object : AppenderBase() { + override fun append(eventObject: ILoggingEvent) { + synchronized(this) { + connections.forEach { + runBlocking { + it.session.send("{ \"log\": {\"value\": \"${eventObject}\"}}") // FIXME: '"' in eventObject + } + } + } + } + }.apply { + name = "serverLogger" + start() + } + ) webSocket("/echo") { val thisConnection = Connection(this) connections += thisConnection @@ -64,17 +102,20 @@ class ReadVacSvr : ReadVac() { val stream = ByteArrayOutputStream() JSONMetaWriter.write(stream, this@ReadVacSvr.getDeviceMeta(this@ReadVacSvr.getDeviceConfig())) send("{ \"devices\": $stream, \"storing\": {\"value\": ${storeButton.selectedProperty().get()}}, \"measurement\": {\"value\": ${measureButton.selectedProperty().get()}} }") + for (sensor in sensors) { send("{ \"sensor\": {\"${sensor}\": ${(stage.scene.lookup("#${sensor}") as ToggleSwitch).selectedProperty().get()}}}") } for (frame in incoming) { frame as? Frame.Text ?: continue - val receivedText = frame.readText() - when (receivedText) { + when (frame.readText()) { "measurePressed" -> { measureButton.fire() } "storePressed" -> { storeButton.fire() } + "logPressed" -> { + //logButton.fire() + } "bye" -> close(CloseReason(CloseReason.Codes.NORMAL, "Client said BYE")) } } diff --git a/numass-web-control/src/main/resources/index.html b/numass-web-control/src/main/resources/index.html index 7851ed8..4094ac7 100644 --- a/numass-web-control/src/main/resources/index.html +++ b/numass-web-control/src/main/resources/index.html @@ -7,6 +7,7 @@ +
diff --git a/numass-web-control/src/main/resources/script.js b/numass-web-control/src/main/resources/script.js index 259bb34..78b78d6 100644 --- a/numass-web-control/src/main/resources/script.js +++ b/numass-web-control/src/main/resources/script.js @@ -9,6 +9,7 @@ function drawCard(device_name = "Default", col = "red", measure = "---", power = let name = document.createElement("span") name.style.fontWeight = "bolder" let p_toggle = document.createElement("input") + p_toggle.id = device_name p_toggle.type= "checkbox" p_toggle.classList = "switch" name.textContent = device_name @@ -47,6 +48,7 @@ function drawCard(device_name = "Default", col = "red", measure = "---", power = } document.querySelector("#sidebar").appendChild(card) } + var devices = [] const nuWebSocket = new WebSocket("ws://" + window.location.host + "/echo") nuWebSocket.onmessage = (event) => { @@ -70,6 +72,20 @@ nuWebSocket.onmessage = (event) => { } else document.querySelector("#store").classList.remove("pressedButton") } + if (msg.log) { + if (msg.log.value) { + document.querySelector("#logBar").innerHTML += msg.log.value + "
" + document.querySelector("#logBar").scrollTo(0, document.querySelector("#logBar").scrollHeight) + } + } + if (msg.sensor) { + for (sensor in msg.sensor) { + let sensorButton = document.querySelector("#" + sensor) + if (sensorButton) { + sensorButton.checked = msg.sensor[sensor] + } + } + } } /* fetch("/api/devices") @@ -103,4 +119,11 @@ document.querySelector("#measure").addEventListener("click", (e) => { document.querySelector("#store").addEventListener("click", (e) => { nuWebSocket.send("storePressed") console.log("storePressed") -}) \ No newline at end of file +}) + +document.querySelector("#log").addEventListener("click", (e) => { + console.log("logPressed") + document.querySelector("#logBar").hidden = !document.querySelector("#logBar").hidden + document.querySelector("#logBar").scrollTo(0, document.querySelector("#logBar").scrollHeight) +}) + diff --git a/numass-web-control/src/main/resources/styles.css b/numass-web-control/src/main/resources/styles.css index 3a62c07..b588e55 100644 --- a/numass-web-control/src/main/resources/styles.css +++ b/numass-web-control/src/main/resources/styles.css @@ -41,9 +41,21 @@ body { font-family: Sans-Serif; } #lower { - flex: 1; + flex: 4; display: flex; } +#logBar { + position: absolute; + width: 100%; + background: grey; + border: 1px black solid; + bottom: 0; + z-index: 10; + box-sizing: border-box; + padding: 0.5em; + max-height: 20%; + overflow-y: scroll; +} .upperSection { background: lightblue; text-align: center;