From 599d08b62a82ce5b682ebdc83eb987716c5e5c30 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 12 Oct 2020 15:37:46 +0300 Subject: [PATCH] More or less working motors app --- .../control/base/devicePropertyDelegates.kt | 15 ++++ .../pimotionmaster/PiMotionMasterApp.kt | 87 ++++++++++++------- .../pimotionmaster/PiMotionMasterDevice.kt | 22 +++-- .../PiMotionMasterVirtualDevice.kt | 18 +++- 4 files changed, 98 insertions(+), 44 deletions(-) diff --git a/dataforge-device-core/src/commonMain/kotlin/hep/dataforge/control/base/devicePropertyDelegates.kt b/dataforge-device-core/src/commonMain/kotlin/hep/dataforge/control/base/devicePropertyDelegates.kt index 246a1a5..420c947 100644 --- a/dataforge-device-core/src/commonMain/kotlin/hep/dataforge/control/base/devicePropertyDelegates.kt +++ b/dataforge-device-core/src/commonMain/kotlin/hep/dataforge/control/base/devicePropertyDelegates.kt @@ -93,6 +93,21 @@ public fun DeviceBase.readingNumber( } ) +public fun DeviceBase.readingDouble( + default: Number? = null, + descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + getter: suspend () -> Double, +): PropertyDelegateProvider> = TypedReadOnlyDevicePropertyProvider( + this, + default?.let { MetaItem.ValueItem(it.asValue()) }, + MetaConverter.double, + descriptorBuilder, + getter = { + val number = getter() + MetaItem.ValueItem(number.asValue()) + } +) + public fun DeviceBase.readingString( default: String? = null, descriptorBuilder: PropertyDescriptor.() -> Unit = {}, diff --git a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt b/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt index 68de8f2..9eda700 100644 --- a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt +++ b/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt @@ -7,9 +7,11 @@ import javafx.beans.property.ReadOnlyProperty import javafx.beans.property.SimpleIntegerProperty import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleStringProperty -import javafx.collections.FXCollections +import javafx.geometry.Pos import javafx.scene.Parent import javafx.scene.layout.Priority +import javafx.scene.layout.VBox +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.launch import tornadofx.* @@ -27,6 +29,51 @@ class PiMotionMasterController : Controller() { val motionMaster: PiMotionMasterDevice by deviceManager.installing(PiMotionMasterDevice) } +fun VBox.piMotionMasterAxis( + axisName: String, + axis: PiMotionMasterDevice.Axis, + coroutineScope: CoroutineScope, +) = hbox { + alignment = Pos.CENTER + label(axisName) + coroutineScope.launch { + val min = axis.minPosition.readTyped(true) + val max = axis.maxPosition.readTyped(true) + val positionProperty = axis.position.fxProperty(axis) + val startPosition = axis.position.readTyped(true) + runLater { + vbox { + hgrow = Priority.ALWAYS + slider(min..max, startPosition) { + minWidth = 300.0 + isShowTickLabels = true + isShowTickMarks = true + minorTickCount = 10 + majorTickUnit = 1.0 + valueProperty().onChange { + coroutineScope.launch { + axis.move(value) + } + } + } + slider(min..max) { + isDisable = true + valueProperty().bind(positionProperty) + } + } + } + } +} + +fun Parent.axisPane(axes: Map, coroutineScope: CoroutineScope) { + vbox { + axes.forEach { (name, axis) -> + this.piMotionMasterAxis(name, axis, coroutineScope) + } + } +} + + class PiMotionMasterView : View() { private val controller: PiMotionMasterController by inject() @@ -35,7 +82,7 @@ class PiMotionMasterView : View() { private val connectedProperty: ReadOnlyProperty = device.connected.fxProperty(device) private val debugServerJobProperty = SimpleObjectProperty() private val debugServerStarted = debugServerJobProperty.booleanBinding { it != null } - private val axisList = FXCollections.observableArrayList>() + //private val axisList = FXCollections.observableArrayList>() override val root: Parent = borderpane { top { @@ -49,7 +96,7 @@ class PiMotionMasterView : View() { } } field("Port:") { - textfield(port){ + textfield(port) { stripNonNumeric() } button { @@ -86,9 +133,11 @@ class PiMotionMasterView : View() { action { if (!connectedProperty.value) { device.connect(host.get(), port.get()) - axisList.addAll(device.axes.entries) + center { + axisPane(device.axes,controller.context) + } } else { - axisList.removeAll() + this@borderpane.center = null device.disconnect() } } @@ -98,34 +147,6 @@ class PiMotionMasterView : View() { } } - center { - listview(axisList) { - cellFormat { (name, axis) -> - hbox { - minHeight = 40.0 - label(name) - controller.context.launch { - val min = axis.minPosition.readTyped(true) - val max = axis.maxPosition.readTyped(true) - runLater { - slider(min.toDouble()..max.toDouble()){ - hgrow = Priority.ALWAYS - valueProperty().onChange { - isDisable = true - launch { - axis.move(value) - runLater { - isDisable = false - } - } - } - } - } - } - } - } - } - } } } diff --git a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt b/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt index 8ccd67a..733398d 100644 --- a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt +++ b/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt @@ -81,7 +81,7 @@ class PiMotionMasterDevice( } fun disconnect() { - runBlocking{ + runBlocking { disconnect.invoke() } } @@ -165,7 +165,7 @@ class PiMotionMasterDevice( private suspend fun requestAndParse(command: String, vararg arguments: String): Map = buildMap { request(command, *arguments).forEach { line -> val (key, value) = line.split("=") - put(key, value) + put(key, value.trim()) } } @@ -277,7 +277,7 @@ class PiMotionMasterDevice( send("FRF", axisId) } - val minPosition by readingNumber( + val minPosition by readingDouble( descriptorBuilder = { info = "Minimal position value for the axis" }, @@ -287,7 +287,7 @@ class PiMotionMasterDevice( } ) - val maxPosition by readingNumber( + val maxPosition by readingDouble( descriptorBuilder = { info = "Maximal position value for the axis" }, @@ -297,9 +297,15 @@ class PiMotionMasterDevice( } ) - val position: TypedDeviceProperty by axisNumberProperty("POS") { - info = "The current axis position." - } + val position by readingDouble( + descriptorBuilder = { + info = "The current axis position." + }, + getter = { + requestAndParse("POS?", axisId)[axisId]?.toDoubleOrNull() + ?: error("Malformed `POS?` response. Should include float value for $axisId") + } + ) val openLoopTarget: DeviceProperty by axisNumberProperty("OMA") { info = "Position for open-loop operation." @@ -320,7 +326,7 @@ class PiMotionMasterDevice( it.node["velocity"].double?.let { v -> velocity.write(v) } - position.write(target) + targetPosition.write(target) //read `onTarget` and `position` properties in a cycle until movement is complete while (!onTarget.readTyped(true)) { position.read(true) diff --git a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice.kt b/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice.kt index 4c17a6d..3cd4f00 100644 --- a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice.kt +++ b/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice.kt @@ -8,6 +8,8 @@ import hep.dataforge.control.ports.withDelimiter import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.* +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlin.math.abs import kotlin.time.Duration @@ -20,8 +22,12 @@ abstract class VirtualDevice(val scope: CoroutineScope) : Socket { private val toReceive = Channel(100) private val toRespond = Channel(100) + private val mutex = Mutex() + private val receiveJob: Job = toReceive.consumeAsFlow().transformRequests().onEach { - evaluateRequest(it) + mutex.withLock { + evaluateRequest(it) + } }.catch { it.printStackTrace() }.launchIn(scope) @@ -143,7 +149,12 @@ class PiMotionMasterVirtualDevice( } val response = selectedAxis.joinToString(separator = " \n") { val state = axisState.getValue(it) - "$it=${state.extract(it)}" + val value = when (val extracted = state.extract(it)) { + true -> 1 + false -> 0 + else -> extracted + } + "$it=$value" } respond(response) } @@ -241,13 +252,14 @@ class PiMotionMasterVirtualDevice( "TMX?" -> respondForAllAxis(axisIds) { maxPosition } "VEL?" -> respondForAllAxis(axisIds) { velocity } "SRG?" -> respond(WAT) + "ONT?" -> respondForAllAxis(axisIds) { onTarget() } "SVO" -> doForEachAxis(parts) { key, value -> axisState[key]?.servoMode = value.toInt() } "MOV" -> doForEachAxis(parts) { key, value -> axisState[key]?.targetPosition = value.toDouble() } - "VEL"-> doForEachAxis(parts){key, value -> + "VEL" -> doForEachAxis(parts) { key, value -> axisState[key]?.velocity = value.toDouble() } "INI" -> {