bind device toggles with UI

only flow apllication -> webpage is supported now
This commit is contained in:
Sabina Abdiganieva 2024-02-16 01:26:14 +03:00
parent b0154ac187
commit 1ae7e7c3d4
6 changed files with 84 additions and 4 deletions

View File

@ -84,6 +84,7 @@ class VacCollectorDisplay : DeviceDisplayFX<VacCollectorDevice>() {
} }
separator(Orientation.VERTICAL) separator(Orientation.VERTICAL)
togglebutton("Log") { togglebutton("Log") {
this.id = "Log"
isSelected = false isSelected = false
LogFragment().apply { LogFragment().apply {
addLogHandler(device.logger) addLogHandler(device.logger)

View File

@ -121,6 +121,7 @@ open class VacDisplay : DeviceDisplayFX<Sensor>() {
value = "---" value = "---"
} }
} }
id = device.name
} }
} }
} }
@ -161,6 +162,7 @@ open class VacDisplay : DeviceDisplayFX<Sensor>() {
minHeight = 30.0 minHeight = 30.0
vgrow = Priority.ALWAYS vgrow = Priority.ALWAYS
switch("Power") { switch("Power") {
this.id = "power"
alignment = Pos.CENTER alignment = Pos.CENTER
booleanStateProperty("power").bindBidirectional(selectedProperty()) booleanStateProperty("power").bindBidirectional(selectedProperty())
} }

View File

@ -1,7 +1,11 @@
package inr.numass.webcontrol 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 hep.dataforge.io.JSONMetaWriter
import inr.numass.control.readvac.ReadVac import inr.numass.control.readvac.ReadVac
import inr.numass.control.readvac.VacCollectorDevice
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.engine.* import io.ktor.server.engine.*
@ -15,6 +19,7 @@ import javafx.application.Application
import javafx.scene.control.ToggleButton import javafx.scene.control.ToggleButton
import javafx.stage.Stage import javafx.stage.Stage
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.controlsfx.control.ToggleSwitch
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.nio.file.Paths import java.nio.file.Paths
import java.util.* import java.util.*
@ -29,15 +34,32 @@ class Connection(val session: DefaultWebSocketServerSession) {
class ReadVacSvr : ReadVac() { class ReadVacSvr : ReadVac() {
var server : NettyApplicationEngine? = null 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) { override fun start(stage: Stage) {
super.start(stage) super.start(stage)
val measureButton = stage.scene.lookup("#Measure") as ToggleButton val measureButton = stage.scene.lookup("#Measure") as ToggleButton
val storeButton = stage.scene.lookup("#Store") 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) { this.server = embeddedServer(Netty, port = 8000) {
install(WebSockets) install(WebSockets)
routing { routing {
staticFiles("/", Paths.get(this.javaClass.classLoader.getResource("index.html").toURI()).toFile().parentFile) staticFiles("/", Paths.get(this.javaClass.classLoader.getResource("index.html").toURI()).toFile().parentFile)
val connections = Collections.synchronizedSet<Connection?>(LinkedHashSet()) val connections = Collections.synchronizedSet<Connection?>(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 -> measureButton.selectedProperty().addListener { _, oldValue, newValue ->
if (oldValue != newValue) { if (oldValue != newValue) {
connections.forEach { connections.forEach {
@ -56,6 +78,22 @@ class ReadVacSvr : ReadVac() {
} }
} }
} }
(device!!.logger as ch.qos.logback.classic.Logger).addAppender(
object : AppenderBase<ILoggingEvent>() {
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") { webSocket("/echo") {
val thisConnection = Connection(this) val thisConnection = Connection(this)
connections += thisConnection connections += thisConnection
@ -64,17 +102,20 @@ class ReadVacSvr : ReadVac() {
val stream = ByteArrayOutputStream() val stream = ByteArrayOutputStream()
JSONMetaWriter.write(stream, this@ReadVacSvr.getDeviceMeta(this@ReadVacSvr.getDeviceConfig())) JSONMetaWriter.write(stream, this@ReadVacSvr.getDeviceMeta(this@ReadVacSvr.getDeviceConfig()))
send("{ \"devices\": $stream, \"storing\": {\"value\": ${storeButton.selectedProperty().get()}}, \"measurement\": {\"value\": ${measureButton.selectedProperty().get()}} }") 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) { for (frame in incoming) {
frame as? Frame.Text ?: continue frame as? Frame.Text ?: continue
val receivedText = frame.readText() when (frame.readText()) {
when (receivedText) {
"measurePressed" -> { "measurePressed" -> {
measureButton.fire() measureButton.fire()
} }
"storePressed" -> { "storePressed" -> {
storeButton.fire() storeButton.fire()
} }
"logPressed" -> {
//logButton.fire()
}
"bye" -> close(CloseReason(CloseReason.Codes.NORMAL, "Client said BYE")) "bye" -> close(CloseReason(CloseReason.Codes.NORMAL, "Client said BYE"))
} }
} }

View File

@ -7,6 +7,7 @@
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js" charset="utf-8"></script> <script src="https://cdn.plot.ly/plotly-2.27.0.min.js" charset="utf-8"></script>
</head> </head>
<body> <body>
<div id="logBar"></div>
<div class="toolbar"> <div class="toolbar">
<button id="measure" type="button" class="myButton toolbarButton">Measure</button> <button id="measure" type="button" class="myButton toolbarButton">Measure</button>
<button id="store" type="button" class="myButton toolbarButton">Store</button> <button id="store" type="button" class="myButton toolbarButton">Store</button>

View File

@ -9,6 +9,7 @@ function drawCard(device_name = "Default", col = "red", measure = "---", power =
let name = document.createElement("span") let name = document.createElement("span")
name.style.fontWeight = "bolder" name.style.fontWeight = "bolder"
let p_toggle = document.createElement("input") let p_toggle = document.createElement("input")
p_toggle.id = device_name
p_toggle.type= "checkbox" p_toggle.type= "checkbox"
p_toggle.classList = "switch" p_toggle.classList = "switch"
name.textContent = device_name name.textContent = device_name
@ -47,6 +48,7 @@ function drawCard(device_name = "Default", col = "red", measure = "---", power =
} }
document.querySelector("#sidebar").appendChild(card) document.querySelector("#sidebar").appendChild(card)
} }
var devices = [] var devices = []
const nuWebSocket = new WebSocket("ws://" + window.location.host + "/echo") const nuWebSocket = new WebSocket("ws://" + window.location.host + "/echo")
nuWebSocket.onmessage = (event) => { nuWebSocket.onmessage = (event) => {
@ -70,6 +72,20 @@ nuWebSocket.onmessage = (event) => {
} }
else document.querySelector("#store").classList.remove("pressedButton") else document.querySelector("#store").classList.remove("pressedButton")
} }
if (msg.log) {
if (msg.log.value) {
document.querySelector("#logBar").innerHTML += msg.log.value + "<br>"
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") fetch("/api/devices")
@ -103,4 +119,11 @@ document.querySelector("#measure").addEventListener("click", (e) => {
document.querySelector("#store").addEventListener("click", (e) => { document.querySelector("#store").addEventListener("click", (e) => {
nuWebSocket.send("storePressed") nuWebSocket.send("storePressed")
console.log("storePressed") console.log("storePressed")
}) })
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)
})

View File

@ -41,9 +41,21 @@ body {
font-family: Sans-Serif; font-family: Sans-Serif;
} }
#lower { #lower {
flex: 1; flex: 4;
display: flex; 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 { .upperSection {
background: lightblue; background: lightblue;
text-align: center; text-align: center;