bind device toggles with UI
only flow apllication -> webpage is supported now
This commit is contained in:
parent
b0154ac187
commit
1ae7e7c3d4
@ -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)
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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)
|
||||||
|
})
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user