Move all-things to Compose

This commit is contained in:
Alexander Nozik 2024-05-11 17:56:28 +03:00
parent 381da970bf
commit 24b6856f15
7 changed files with 146 additions and 161 deletions

View File

@ -3,7 +3,6 @@ import space.kscience.gradle.useSPCTeam
plugins { plugins {
id("space.kscience.gradle.project") id("space.kscience.gradle.project")
alias(libs.plugins.versions)
} }
allprojects { allprojects {

View File

@ -35,7 +35,7 @@ public class VirtualLimitSwitch(
public val lockedState: DeviceState<Boolean>, public val lockedState: DeviceState<Boolean>,
) : DeviceBySpec<LimitSwitch>(LimitSwitch, context), LimitSwitch { ) : DeviceBySpec<LimitSwitch>(LimitSwitch, context), LimitSwitch {
init { override suspend fun onStart() {
lockedState.valueFlow.onEach { lockedState.valueFlow.onEach {
propertyChanged(LimitSwitch.locked, it) propertyChanged(LimitSwitch.locked, it)
}.launchIn(this) }.launchIn(this)

View File

@ -1,7 +1,6 @@
plugins { plugins {
kotlin("jvm") kotlin("jvm")
id("org.openjfx.javafxplugin") version "0.0.13" alias(spclibs.plugins.compose)
application
} }
@ -20,28 +19,46 @@ dependencies {
implementation(projects.controlsOpcua) implementation(projects.controlsOpcua)
implementation(spclibs.ktor.client.cio) implementation(spclibs.ktor.client.cio)
implementation(libs.tornadofx)
implementation(libs.plotlykt.server) implementation(libs.plotlykt.server)
// implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6") // implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6")
implementation(compose.runtime)
implementation(compose.desktop.currentOs)
implementation(compose.material3)
// implementation("org.pushing-pixels:aurora-window:1.3.0")
// implementation("org.pushing-pixels:aurora-component:1.3.0")
// implementation("org.pushing-pixels:aurora-theming:1.3.0")
implementation(spclibs.logback.classic) implementation(spclibs.logback.classic)
} }
kotlin{ kotlin{
jvmToolchain(11) jvmToolchain(17)
} compilerOptions {
freeCompilerArgs.addAll("-Xjvm-default=all", "-Xopt-in=kotlin.RequiresOptIn")
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions {
freeCompilerArgs = freeCompilerArgs + listOf("-Xjvm-default=all", "-Xopt-in=kotlin.RequiresOptIn")
} }
} }
javafx { compose{
version = "17" desktop{
modules("javafx.controls") application{
} mainClass = "space.kscience.controls.demo.DemoControllerViewKt"
}
application { }
mainClass.set("space.kscience.controls.demo.DemoControllerViewKt")
} }
//
//
//tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
// kotlinOptions {
// freeCompilerArgs = freeCompilerArgs + listOf("-Xjvm-default=all", "-Xopt-in=kotlin.RequiresOptIn")
// }
//}
//
//javafx {
// version = "17"
// modules("javafx.controls")
//}
//
//application {
// mainClass.set("space.kscience.controls.demo.DemoControllerViewKt")
//}

View File

@ -1,14 +1,19 @@
package space.kscience.controls.demo package space.kscience.controls.demo
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import io.ktor.server.engine.ApplicationEngine import io.ktor.server.engine.ApplicationEngine
import javafx.scene.Parent import kotlinx.coroutines.Job
import javafx.scene.control.Slider
import javafx.scene.layout.Priority
import javafx.stage.Stage
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.eclipse.milo.opcua.sdk.server.OpcUaServer import org.eclipse.milo.opcua.sdk.server.OpcUaServer
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText
@ -17,9 +22,6 @@ import space.kscience.controls.api.GetDescriptionMessage
import space.kscience.controls.api.PropertyChangedMessage import space.kscience.controls.api.PropertyChangedMessage
import space.kscience.controls.client.launchMagixService import space.kscience.controls.client.launchMagixService
import space.kscience.controls.client.magixFormat import space.kscience.controls.client.magixFormat
import space.kscience.controls.demo.DemoDevice.Companion.cosScale
import space.kscience.controls.demo.DemoDevice.Companion.sinScale
import space.kscience.controls.demo.DemoDevice.Companion.timeScale
import space.kscience.controls.manager.DeviceManager import space.kscience.controls.manager.DeviceManager
import space.kscience.controls.manager.install import space.kscience.controls.manager.install
import space.kscience.controls.opcua.server.OpcUaServer import space.kscience.controls.opcua.server.OpcUaServer
@ -35,16 +37,15 @@ import space.kscience.magix.rsocket.rSocketWithWebSockets
import space.kscience.magix.server.RSocketMagixFlowPlugin import space.kscience.magix.server.RSocketMagixFlowPlugin
import space.kscience.magix.server.startMagixServer import space.kscience.magix.server.startMagixServer
import space.kscince.magix.zmq.ZmqMagixFlowPlugin import space.kscince.magix.zmq.ZmqMagixFlowPlugin
import tornadofx.*
import java.awt.Desktop import java.awt.Desktop
import java.net.URI import java.net.URI
class DemoController : Controller(), ContextAware { class DemoController : ContextAware {
var device: DemoDevice? = null var device: DemoDevice? = null
var magixServer: ApplicationEngine? = null var magixServer: ApplicationEngine? = null
var visualizer: ApplicationEngine? = null var visualizer: ApplicationEngine? = null
var opcUaServer: OpcUaServer = OpcUaServer { val opcUaServer: OpcUaServer = OpcUaServer {
setApplicationName(LocalizedText.english("space.kscience.controls.opcua")) setApplicationName(LocalizedText.english("space.kscience.controls.opcua"))
endpoint { endpoint {
@ -60,39 +61,37 @@ class DemoController : Controller(), ContextAware {
private val deviceManager = context.request(DeviceManager) private val deviceManager = context.request(DeviceManager)
fun init() { fun start(): Job = context.launch {
context.launch { device = deviceManager.install("demo", DemoDevice)
device = deviceManager.install("demo", DemoDevice) //starting magix event loop
//starting magix event loop magixServer = startMagixServer(
magixServer = startMagixServer( RSocketMagixFlowPlugin(), //TCP rsocket support
RSocketMagixFlowPlugin(), //TCP rsocket support ZmqMagixFlowPlugin() //ZMQ support
ZmqMagixFlowPlugin() //ZMQ support )
) //Launch a device client and connect it to the server
//Launch a device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost")
val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost") deviceManager.launchMagixService(deviceEndpoint)
deviceManager.launchMagixService(deviceEndpoint) //connect visualization to a magix endpoint
//connect visualization to a magix endpoint val visualEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost")
val visualEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost") visualizer = startDemoDeviceServer(visualEndpoint)
visualizer = startDemoDeviceServer(visualEndpoint)
//serve devices as OPC-UA namespace //serve devices as OPC-UA namespace
opcUaServer.startup() opcUaServer.startup()
opcUaServer.serveDevices(deviceManager) opcUaServer.serveDevices(deviceManager)
val listenerEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost") val listenerEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost")
listenerEndpoint.subscribe(DeviceManager.magixFormat).onEach { (_, deviceMessage)-> listenerEndpoint.subscribe(DeviceManager.magixFormat).onEach { (_, deviceMessage) ->
// print all messages that are not property change message // print all messages that are not property change message
if(deviceMessage !is PropertyChangedMessage){ if (deviceMessage !is PropertyChangedMessage) {
println(">> ${Json.encodeToString(DeviceMessage.serializer(), deviceMessage)}") println(">> ${Json.encodeToString(DeviceMessage.serializer(), deviceMessage)}")
} }
}.launchIn(this) }.launchIn(this)
listenerEndpoint.send(DeviceManager.magixFormat, GetDescriptionMessage(), "listener", "controls-kt") listenerEndpoint.send(DeviceManager.magixFormat, GetDescriptionMessage(), "listener", "controls-kt")
}
} }
suspend fun shutdown() { fun shutdown(): Job = context.launch {
logger.info { "Shutting down..." } logger.info { "Shutting down..." }
opcUaServer.shutdown() opcUaServer.shutdown()
logger.info { "OpcUa server stopped" } logger.info { "OpcUa server stopped" }
@ -102,92 +101,82 @@ class DemoController : Controller(), ContextAware {
logger.info { "Magix server stopped" } logger.info { "Magix server stopped" }
device?.stop() device?.stop()
logger.info { "Device server stopped" } logger.info { "Device server stopped" }
context.close() }
}
@Composable
fun DemoControls(controller: DemoController) {
var timeScale by remember { mutableStateOf(5000f) }
var xScale by remember { mutableStateOf(1f) }
var yScale by remember { mutableStateOf(1f) }
Surface(Modifier.padding(5.dp)) {
Column {
Row(Modifier.fillMaxWidth()) {
Text("Time Scale", modifier = Modifier.align(Alignment.CenterVertically).width(100.dp))
TextField(String.format("%.2f", timeScale),{}, enabled = false, modifier = Modifier.align(Alignment.CenterVertically).width(100.dp))
Slider(timeScale, onValueChange = { timeScale = it }, steps = 20, valueRange = 1000f..5000f)
}
Row(Modifier.fillMaxWidth()) {
Text("X scale", modifier = Modifier.align(Alignment.CenterVertically).width(100.dp))
TextField(String.format("%.2f", xScale),{}, enabled = false, modifier = Modifier.align(Alignment.CenterVertically).width(100.dp))
Slider(xScale, onValueChange = { xScale = it }, steps = 20, valueRange = 0.1f..2.0f)
}
Row(Modifier.fillMaxWidth()) {
Text("Y scale", modifier = Modifier.align(Alignment.CenterVertically).width(100.dp))
TextField(String.format("%.2f", yScale),{}, enabled = false, modifier = Modifier.align(Alignment.CenterVertically).width(100.dp))
Slider(yScale, onValueChange = { yScale = it }, steps = 20, valueRange = 0.1f..2.0f)
}
Row(Modifier.fillMaxWidth()) {
Button(
onClick = {
controller.device?.run {
launch {
write(DemoDevice.timeScale, timeScale.toDouble())
write(DemoDevice.sinScale, xScale.toDouble())
write(DemoDevice.cosScale, yScale.toDouble())
}
}
},
Modifier.fillMaxWidth()
) {
Text("Submit")
}
}
Row(Modifier.fillMaxWidth()) {
Button(
onClick = {
controller.visualizer?.run {
val host = "localhost"//environment.connectors.first().host
val port = environment.connectors.first().port
val uri = URI("http", null, host, port, "/", null, null)
Desktop.getDesktop().browse(uri)
}
},
Modifier.fillMaxWidth()
) {
Text("Show plots")
}
}
}
} }
} }
class DemoControllerView : View(title = " Demo controller remote") { fun main() = application {
private val controller: DemoController by inject() val controller = remember { DemoController().apply { start() } }
private var timeScaleSlider: Slider by singleAssign()
private var xScaleSlider: Slider by singleAssign()
private var yScaleSlider: Slider by singleAssign()
override val root: Parent = vbox { Window(
hbox { title = "All things control",
label("Time scale") onCloseRequest = {
pane {
hgrow = Priority.ALWAYS
}
timeScaleSlider = slider(1000..10000, 5000) {
isShowTickLabels = true
isShowTickMarks = true
}
}
hbox {
label("X scale")
pane {
hgrow = Priority.ALWAYS
}
xScaleSlider = slider(0.1..2.0, 1.0) {
isShowTickLabels = true
isShowTickMarks = true
}
}
hbox {
label("Y scale")
pane {
hgrow = Priority.ALWAYS
}
yScaleSlider = slider(0.1..2.0, 1.0) {
isShowTickLabels = true
isShowTickMarks = true
}
}
button("Submit") {
useMaxWidth = true
action {
controller.device?.run {
launch {
write(timeScale, timeScaleSlider.value)
write(sinScale, xScaleSlider.value)
write(cosScale, yScaleSlider.value)
}
}
}
}
button("Show plots") {
useMaxWidth = true
action {
controller.visualizer?.run {
val host = "localhost"//environment.connectors.first().host
val port = environment.connectors.first().port
val uri = URI("http", null, host, port, "/", null, null)
Desktop.getDesktop().browse(uri)
}
}
}
}
}
class DemoControllerApp : App(DemoControllerView::class) {
private val controller: DemoController by inject()
override fun start(stage: Stage) {
super.start(stage)
controller.init()
}
override fun stop() {
runBlocking {
controller.shutdown() controller.shutdown()
exitApplication()
},
state = rememberWindowState(width = 400.dp, height = 300.dp)
) {
MaterialTheme {
DemoControls(controller)
} }
super.stop()
} }
} }
fun main() {
launch<DemoControllerApp>()
}

View File

@ -1,10 +0,0 @@
package space.kscience.controls.demo
//import com.github.ricky12awesome.jss.encodeToSchema
//import com.github.ricky12awesome.jss.globalJson
//import space.kscience.controls.api.DeviceMessage
//fun main() {
// val schema = globalJson.encodeToSchema(DeviceMessage.serializer(), generateDefinitions = false)
// println(schema)
//}

View File

@ -1,19 +1,11 @@
plugins { plugins {
id("space.kscience.gradle.jvm") id("space.kscience.gradle.jvm")
application alias(spclibs.plugins.compose)
id("org.openjfx.javafxplugin")
} }
//TODO to be moved to a separate project //application{
// mainClass.set("ru.mipt.npm.devices.pimotionmaster.PiMotionMasterAppKt")
javafx { //}
version = "17"
modules = listOf("javafx.controls")
}
application{
mainClass.set("ru.mipt.npm.devices.pimotionmaster.PiMotionMasterAppKt")
}
kotlin{ kotlin{
explicitApi = null explicitApi = null
@ -25,5 +17,4 @@ val dataforgeVersion: String by extra
dependencies { dependencies {
implementation(project(":controls-ports-ktor")) implementation(project(":controls-ports-ktor"))
implementation(projects.controlsMagix) implementation(projects.controlsMagix)
implementation(libs.tornadofx)
} }

View File

@ -18,7 +18,6 @@ pluginManagement {
id("space.kscience.gradle.mpp") version toolsVersion id("space.kscience.gradle.mpp") version toolsVersion
id("space.kscience.gradle.jvm") version toolsVersion id("space.kscience.gradle.jvm") version toolsVersion
id("space.kscience.gradle.js") version toolsVersion id("space.kscience.gradle.js") version toolsVersion
id("org.openjfx.javafxplugin") version "0.0.13"
} }
} }