diff --git a/.gitignore b/.gitignore index 3bf252e..e688053 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Created by .ignore support plugin (hsz.mobi) .idea/ .gradle +.kotlin *.iws *.iml diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Inertia.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Inertia.kt index 1c6a507..1259151 100644 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Inertia.kt +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Inertia.kt @@ -24,7 +24,7 @@ public class Inertia( private var currentForce = force.value - private val movement = onTimer { prev, next -> + private val movement = onTimer(DefaultTimer.REALTIME) { prev, next -> val dtSeconds = (next - prev).toDouble(DurationUnit.SECONDS) // compute new value based on velocity and acceleration from the previous step diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/ScrewDrive.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Leadscrew.kt similarity index 72% rename from controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/ScrewDrive.kt rename to controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Leadscrew.kt index 573a648..ebe0d30 100644 --- a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/ScrewDrive.kt +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/models/Leadscrew.kt @@ -7,22 +7,25 @@ import space.kscience.controls.constructor.units.* import space.kscience.dataforge.context.Context import kotlin.math.PI -public class ScrewDrive( +/** + * https://en.wikipedia.org/wiki/Leadscrew + */ +public class Leadscrew( context: Context, public val leverage: NumericalValue, ) : ModelConstructor(context) { public fun torqueToForce( - stateOfMomentum: DeviceState>, - ): DeviceState> = DeviceState.map(stateOfMomentum) { momentum -> - NumericalValue(momentum.value / leverage.value ) + stateOfTorque: DeviceState>, + ): DeviceState> = DeviceState.map(stateOfTorque) { torque -> + NumericalValue(torque.value / leverage.value ) } public fun degreesToMeters( stateOfAngle: DeviceState>, offset: NumericalValue = NumericalValue(0), ): DeviceState> = DeviceState.map(stateOfAngle) { degrees -> - offset + NumericalValue(degrees.value * 2 * PI / 360 *leverage.value ) + offset + NumericalValue(degrees.value * 2 * PI / 360 * leverage.value ) } } \ No newline at end of file diff --git a/controls-vision/src/commonMain/kotlin/ControlVisionPlugin.kt b/controls-vision/src/commonMain/kotlin/ControlVisionPlugin.kt index d9ec746..b51d15c 100644 --- a/controls-vision/src/commonMain/kotlin/ControlVisionPlugin.kt +++ b/controls-vision/src/commonMain/kotlin/ControlVisionPlugin.kt @@ -3,12 +3,20 @@ package space.kscience.controls.vision import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.polymorphic import kotlinx.serialization.modules.subclass +import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.PluginFactory +import space.kscience.dataforge.context.PluginTag +import space.kscience.dataforge.meta.Meta import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionPlugin public expect class ControlVisionPlugin: VisionPlugin{ - public companion object: PluginFactory + override val tag: PluginTag + override val visionSerializersModule: SerializersModule + public companion object: PluginFactory{ + override val tag: PluginTag + override fun build(context: Context, meta: Meta): ControlVisionPlugin + } } internal val controlsVisionSerializersModule = SerializersModule { diff --git a/controls-vision/src/commonMain/kotlin/koalaPlotExtensions.kt b/controls-vision/src/commonMain/kotlin/koalaPlotExtensions.kt index 8eeb9a4..8be10df 100644 --- a/controls-vision/src/commonMain/kotlin/koalaPlotExtensions.kt +++ b/controls-vision/src/commonMain/kotlin/koalaPlotExtensions.kt @@ -158,7 +158,7 @@ public fun Plot.plotDeviceState( public fun Plot.plotNumberState( context: Context, - state: DeviceState, + state: DeviceState, maxAge: Duration = defaultMaxAge, maxPoints: Int = defaultMaxPoints, minPoints: Int = defaultMinPoints, diff --git a/controls-vision/src/jsMain/kotlin/ControlsVisionPlugin.js.kt b/controls-vision/src/jsMain/kotlin/ControlsVisionPlugin.js.kt index b966791..f75630b 100644 --- a/controls-vision/src/jsMain/kotlin/ControlsVisionPlugin.js.kt +++ b/controls-vision/src/jsMain/kotlin/ControlsVisionPlugin.js.kt @@ -43,9 +43,9 @@ private val sliderRenderer = ElementVisionRenderer { name, vision: public actual class ControlVisionPlugin : VisionPlugin() { - override val tag: PluginTag get() = Companion.tag + actual override val tag: PluginTag get() = Companion.tag - override val visionSerializersModule: SerializersModule get() = controlsVisionSerializersModule + actual override val visionSerializersModule: SerializersModule get() = controlsVisionSerializersModule override fun content(target: String): Map = when (target) { ElementVisionRenderer.TYPE -> mapOf( @@ -57,9 +57,9 @@ public actual class ControlVisionPlugin : VisionPlugin() { } public actual companion object : PluginFactory { - override val tag: PluginTag = PluginTag("controls.vision") + actual override val tag: PluginTag = PluginTag("controls.vision") - override fun build(context: Context, meta: Meta): ControlVisionPlugin = ControlVisionPlugin() + actual override fun build(context: Context, meta: Meta): ControlVisionPlugin = ControlVisionPlugin() } } \ No newline at end of file diff --git a/controls-vision/src/jvmMain/kotlin/ControlsVisionPlugin.jvm.kt b/controls-vision/src/jvmMain/kotlin/ControlsVisionPlugin.jvm.kt index 55074ae..53ada2e 100644 --- a/controls-vision/src/jvmMain/kotlin/ControlsVisionPlugin.jvm.kt +++ b/controls-vision/src/jvmMain/kotlin/ControlsVisionPlugin.jvm.kt @@ -8,14 +8,14 @@ import space.kscience.dataforge.meta.Meta import space.kscience.visionforge.VisionPlugin public actual class ControlVisionPlugin : VisionPlugin() { - override val tag: PluginTag get() = Companion.tag + actual override val tag: PluginTag get() = Companion.tag - override val visionSerializersModule: SerializersModule get() = controlsVisionSerializersModule + actual override val visionSerializersModule: SerializersModule get() = controlsVisionSerializersModule public actual companion object : PluginFactory { - override val tag: PluginTag = PluginTag("controls.vision") + actual override val tag: PluginTag = PluginTag("controls.vision") - override fun build(context: Context, meta: Meta): ControlVisionPlugin = ControlVisionPlugin() + actual override fun build(context: Context, meta: Meta): ControlVisionPlugin = ControlVisionPlugin() } } \ No newline at end of file diff --git a/controls-visualisation-compose/build.gradle.kts b/controls-visualisation-compose/build.gradle.kts index 48ae9cd..d9d8215 100644 --- a/controls-visualisation-compose/build.gradle.kts +++ b/controls-visualisation-compose/build.gradle.kts @@ -2,7 +2,8 @@ import org.jetbrains.compose.ExperimentalComposeLibrary plugins { id("space.kscience.gradle.mpp") - alias(spclibs.plugins.compose) + alias(spclibs.plugins.compose.compiler) + alias(spclibs.plugins.compose.jb) `maven-publish` } diff --git a/controls-visualisation-compose/src/commonMain/kotlin/NumberTextField.kt b/controls-visualisation-compose/src/commonMain/kotlin/NumberTextField.kt index 68e61ca..3ab10c6 100644 --- a/controls-visualisation-compose/src/commonMain/kotlin/NumberTextField.kt +++ b/controls-visualisation-compose/src/commonMain/kotlin/NumberTextField.kt @@ -2,8 +2,8 @@ package space.kscience.controls.compose import androidx.compose.foundation.layout.Row import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.KeyboardArrowLeft -import androidx.compose.material.icons.filled.KeyboardArrowRight +import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft +import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -30,7 +30,7 @@ public fun NumberTextField( Row (verticalAlignment = Alignment.CenterVertically, modifier = modifier) { step.takeIf { it > 0.0 }?.let { IconButton({ onValueChange(value.toDouble() - step) }, enabled = enabled) { - Icon(Icons.Default.KeyboardArrowLeft, "decrease value") + Icon(Icons.AutoMirrored.Filled.KeyboardArrowLeft, "decrease value") } } TextField( @@ -52,7 +52,7 @@ public fun NumberTextField( ) step.takeIf { it > 0.0 }?.let { IconButton({ onValueChange(value.toDouble() + step) }, enabled = enabled) { - Icon(Icons.Default.KeyboardArrowRight, "increase value") + Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, "increase value") } } } diff --git a/demo/all-things/build.gradle.kts b/demo/all-things/build.gradle.kts index 2abde80..077e55d 100644 --- a/demo/all-things/build.gradle.kts +++ b/demo/all-things/build.gradle.kts @@ -1,6 +1,7 @@ plugins { kotlin("jvm") - alias(spclibs.plugins.compose) + alias(spclibs.plugins.compose.compiler) + alias(spclibs.plugins.compose.jb) } diff --git a/demo/constructor/build.gradle.kts b/demo/constructor/build.gradle.kts index 6b6f461..6d6d7f0 100644 --- a/demo/constructor/build.gradle.kts +++ b/demo/constructor/build.gradle.kts @@ -3,7 +3,8 @@ import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode plugins { id("space.kscience.gradle.mpp") - alias(spclibs.plugins.compose) + alias(spclibs.plugins.compose.compiler) + alias(spclibs.plugins.compose.jb) } kscience { diff --git a/demo/constructor/src/jvmMain/kotlin/BodyOnSprings.kt b/demo/constructor/src/jvmMain/kotlin/BodyOnSprings.kt index 57bc426..af6da52 100644 --- a/demo/constructor/src/jvmMain/kotlin/BodyOnSprings.kt +++ b/demo/constructor/src/jvmMain/kotlin/BodyOnSprings.kt @@ -85,7 +85,7 @@ private class BodyOnSprings( } fun main() = application { - val initialState = XYZ(0, 0.4, 0) + val initialState = XYZ(0.05, 0.4, 0) Window(title = "Ball on springs", onCloseRequest = ::exitApplication) { window.minimumSize = Dimension(400, 400) diff --git a/demo/constructor/src/jvmMain/kotlin/PidDemo.kt b/demo/constructor/src/jvmMain/kotlin/PidDemo.kt index b19e179..81a5923 100644 --- a/demo/constructor/src/jvmMain/kotlin/PidDemo.kt +++ b/demo/constructor/src/jvmMain/kotlin/PidDemo.kt @@ -32,10 +32,10 @@ import space.kscience.controls.constructor.devices.Drive import space.kscience.controls.constructor.devices.LimitSwitch import space.kscience.controls.constructor.devices.LinearDrive import space.kscience.controls.constructor.models.Inertia +import space.kscience.controls.constructor.models.Leadscrew import space.kscience.controls.constructor.models.MutableRangeState import space.kscience.controls.constructor.models.PidParameters -import space.kscience.controls.constructor.models.ScrewDrive -import space.kscience.controls.constructor.timer +import space.kscience.controls.constructor.onTimer import space.kscience.controls.constructor.units.Kilograms import space.kscience.controls.constructor.units.Meters import space.kscience.controls.constructor.units.NumericalValue @@ -56,13 +56,13 @@ import kotlin.time.DurationUnit class Modulator( context: Context, target: MutableDeviceState>, - var freq: Double = 0.1, var timeStep: Duration = 5.milliseconds, + var freq: Double = 0.1, ) : DeviceConstructor(context) { private val clockStart = clock.now() - private val modulation = timer(10.milliseconds).onNext { - val timeFromStart = clock.now() - clockStart + private val modulation = onTimer(timeStep) { _, next -> + val timeFromStart = next - clockStart val t = timeFromStart.toDouble(DurationUnit.SECONDS) target.value = NumericalValue( 5 * sin(2.0 * PI * freq * t) + @@ -72,9 +72,9 @@ class Modulator( } -private val mass = NumericalValue(0.1) +private val mass = NumericalValue(1) -private val leverage = NumericalValue(0.05) +private val leverage = NumericalValue(1.0) private val maxAge = 10.seconds @@ -95,7 +95,7 @@ internal fun createLinearDriveModel( val drive = Drive(context) //a screw drive to convert a rotational moment into a force - val screwDrive = ScrewDrive(context, leverage) + val leadscrew = Leadscrew(context, leverage) /** @@ -107,7 +107,7 @@ internal fun createLinearDriveModel( */ val inertiaModel = Inertia.linear( context = context, - force = screwDrive.torqueToForce(drive.force), + force = leadscrew.torqueToForce(drive.force), mass = mass, position = position ) @@ -130,6 +130,8 @@ private fun createModulator(linearDrive: LinearDrive): Modulator = linearDrive.c Modulator(linearDrive.context, linearDrive.pid.target) ) +private val startPid = PidParameters(kp = 250.0, ki = 0.0, kd = -20.0, timeStep = 20.milliseconds) + @OptIn(ExperimentalSplitPaneApi::class, ExperimentalKoalaPlotApi::class) fun main() = application { val context = remember { @@ -140,7 +142,7 @@ fun main() = application { } var pidParameters by remember { - mutableStateOf(PidParameters(kp = 900.0, ki = 20.0, kd = -50.0, timeStep = 0.005.seconds)) + mutableStateOf(startPid) } val linearDrive: LinearDrive = remember { @@ -183,14 +185,14 @@ fun main() = application { NumberTextField( value = pidParameters.kp, onValueChange = { pidParameters = pidParameters.copy(kp = it.toDouble()) }, - formatter = { String.format("%.2f", it.toDouble()) }, - step = 1.0, + formatter = { String.format("%.3f", it.toDouble()) }, + step = 0.01, modifier = Modifier.width(200.dp), ) Slider( pidParameters.kp.toFloat(), { pidParameters = pidParameters.copy(kp = it.toDouble()) }, - valueRange = 0f..1000f, + valueRange = 0f..100f, steps = 100 ) } @@ -199,15 +201,15 @@ fun main() = application { NumberTextField( value = pidParameters.ki, onValueChange = { pidParameters = pidParameters.copy(ki = it.toDouble()) }, - formatter = { String.format("%.2f", it.toDouble()) }, - step = 0.1, + formatter = { String.format("%.3f", it.toDouble()) }, + step = 0.01, modifier = Modifier.width(200.dp), ) Slider( pidParameters.ki.toFloat(), { pidParameters = pidParameters.copy(ki = it.toDouble()) }, - valueRange = -100f..100f, + valueRange = -10f..10f, steps = 100 ) } @@ -216,15 +218,15 @@ fun main() = application { NumberTextField( value = pidParameters.kd, onValueChange = { pidParameters = pidParameters.copy(kd = it.toDouble()) }, - formatter = { String.format("%.2f", it.toDouble()) }, - step = 0.1, + formatter = { String.format("%.3f", it.toDouble()) }, + step = 0.01, modifier = Modifier.width(200.dp), ) Slider( pidParameters.kd.toFloat(), { pidParameters = pidParameters.copy(kd = it.toDouble()) }, - valueRange = -100f..100f, + valueRange = -10f..10f, steps = 100 ) } @@ -247,12 +249,7 @@ fun main() = application { } Row { Button({ - pidParameters = PidParameters( - kp = 2.5, - ki = 0.0, - kd = -0.1, - timeStep = 0.005.seconds - ) + pidParameters = startPid }) { Text("Reset") } diff --git a/demo/constructor/src/jvmMain/kotlin/Plotter.kt b/demo/constructor/src/jvmMain/kotlin/Plotter.kt index af2c4dd..dfded59 100644 --- a/demo/constructor/src/jvmMain/kotlin/Plotter.kt +++ b/demo/constructor/src/jvmMain/kotlin/Plotter.kt @@ -17,7 +17,7 @@ import kotlinx.coroutines.isActive import space.kscience.controls.constructor.* import space.kscience.controls.constructor.devices.StepDrive import space.kscience.controls.constructor.devices.angle -import space.kscience.controls.constructor.models.ScrewDrive +import space.kscience.controls.constructor.models.Leadscrew import space.kscience.controls.constructor.models.coerceIn import space.kscience.controls.constructor.units.* import space.kscience.controls.manager.ClockManager @@ -99,11 +99,11 @@ private class PlotterModel( ) : ModelConstructor(context) { private val xDrive = StepDrive(context, ticksPerSecond) - private val xTransmission = ScrewDrive(context, NumericalValue(0.01)) + private val xTransmission = Leadscrew(context, NumericalValue(0.01)) val x = xTransmission.degreesToMeters(xDrive.angle(step)).coerceIn(xRange) private val yDrive = StepDrive(context, ticksPerSecond) - private val yTransmission = ScrewDrive(context, NumericalValue(0.01)) + private val yTransmission = Leadscrew(context, NumericalValue(0.01)) val y = yTransmission.degreesToMeters(yDrive.angle(step)).coerceIn(yRange) val xy: DeviceState> = combineState(x, y) { x, y -> XY(x, y) } @@ -143,7 +143,7 @@ suspend fun main() = application { val range = -1000..1000 -// plotter.modernArt(range, range) +// plotterModel.plotter.modernArt(range, range) plotterModel.plotter.square(range, range) } diff --git a/demo/devices-on-map/build.gradle.kts b/demo/devices-on-map/build.gradle.kts new file mode 100644 index 0000000..b81dc12 --- /dev/null +++ b/demo/devices-on-map/build.gradle.kts @@ -0,0 +1,41 @@ +import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode + +plugins { + id("space.kscience.gradle.mpp") + alias(spclibs.plugins.compose.compiler) + alias(spclibs.plugins.compose.jb) +} + +kscience { + jvm() + useSerialization() + useContextReceivers() + commonMain { + implementation(projects.controlsVisualisationCompose) + implementation(projects.controlsConstructor) + } + jvmMain { +// implementation("io.ktor:ktor-server-cio") + implementation(spclibs.logback.classic) + implementation(libs.sciprog.maps.compose) + } +} + +kotlin { + sourceSets { + jvmMain { + dependencies { + implementation(compose.desktop.currentOs) + } + } + } +} + +kotlin.explicitApi = ExplicitApiMode.Disabled + + +compose.desktop { + application { + mainClass = "space.kscience.controls.demo.map.MainKt" + } +} \ No newline at end of file diff --git a/demo/devices-on-map/src/jvmMain/kotlin/main.kt b/demo/devices-on-map/src/jvmMain/kotlin/main.kt new file mode 100644 index 0000000..648610e --- /dev/null +++ b/demo/devices-on-map/src/jvmMain/kotlin/main.kt @@ -0,0 +1,8 @@ +package space.kscience.controls.demo.map + +import androidx.compose.ui.window.application + + +fun main() = application { + +} \ No newline at end of file diff --git a/demo/motors/build.gradle.kts b/demo/motors/build.gradle.kts index 9241764..774df46 100644 --- a/demo/motors/build.gradle.kts +++ b/demo/motors/build.gradle.kts @@ -1,6 +1,7 @@ plugins { id("space.kscience.gradle.jvm") - alias(spclibs.plugins.compose) + alias(spclibs.plugins.compose.compiler) + alias(spclibs.plugins.compose.jb) } kotlin{ diff --git a/gradle.properties b/gradle.properties index 4f937f0..99c39d0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,4 +7,4 @@ org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.jvmargs=-Xmx4096m -toolsVersion=0.15.2-kotlin-1.9.22 \ No newline at end of file +toolsVersion=0.15.4-kotlin-2.0.0 \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 617933a..102c441 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -81,6 +81,8 @@ visionforge-markdown = { module = "space.kscience:visionforge-markdown", version visionforge-server = { module = "space.kscience:visionforge-server", version.ref = "visionforge" } visionforge-compose-html = { module = "space.kscience:visionforge-compose-html", version.ref = "visionforge" } +sciprog-maps-compose = "space.kscience:maps-kt-compose:0.3.0" + # Buildscript [plugins] diff --git a/settings.gradle.kts b/settings.gradle.kts index 366c39e..7aebf36 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -86,5 +86,6 @@ include( ":demo:motors", ":demo:echo", ":demo:mks-pdr900", - ":demo:constructor" + ":demo:constructor", + ":demo:devices-on-map" )