More or less working motors app

This commit is contained in:
Alexander Nozik 2020-10-12 15:37:46 +03:00
parent 62dc6ef127
commit 599d08b62a
4 changed files with 98 additions and 44 deletions

View File

@ -93,6 +93,21 @@ public fun DeviceBase.readingNumber(
}
)
public fun DeviceBase.readingDouble(
default: Number? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend () -> Double,
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Double>> = 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 = {},

View File

@ -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<String, PiMotionMasterDevice.Axis>, 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<Boolean> = device.connected.fxProperty(device)
private val debugServerJobProperty = SimpleObjectProperty<Job>()
private val debugServerStarted = debugServerJobProperty.booleanBinding { it != null }
private val axisList = FXCollections.observableArrayList<Map.Entry<String, PiMotionMasterDevice.Axis>>()
//private val axisList = FXCollections.observableArrayList<Map.Entry<String, PiMotionMasterDevice.Axis>>()
override val root: Parent = borderpane {
top {
@ -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
}
}
}
}
}
}
}
}
}
}
}
}

View File

@ -165,7 +165,7 @@ class PiMotionMasterDevice(
private suspend fun requestAndParse(command: String, vararg arguments: String): Map<String, String> = 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<Double> by axisNumberProperty("POS") {
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)

View File

@ -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<ByteArray> {
private val toReceive = Channel<ByteArray>(100)
private val toRespond = Channel<ByteArray>(100)
private val mutex = Mutex()
private val receiveJob: Job = toReceive.consumeAsFlow().transformRequests().onEach {
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,6 +252,7 @@ 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()
}