Fix motors parser

This commit is contained in:
Alexander Nozik 2020-10-10 22:38:43 +03:00
parent eb3121aed4
commit 23c66d9703
6 changed files with 59 additions and 29 deletions

View File

@ -32,11 +32,11 @@ public class TypedDeviceProperty<T : Any>(
converter: MetaConverter<T>,
) : TypedReadOnlyDeviceProperty<T>(property, converter), DeviceProperty {
// override var value: MetaItem<*>?
// get() = property.value
// set(arg) {
// property.value = arg
// }
override var value: MetaItem<*>?
get() = property.value
set(arg) {
property.value = arg
}
public override var typedValue: T?
get() = value?.let { converter.itemToObject(it) }

View File

@ -40,7 +40,7 @@ public abstract class AbstractPort(
*/
protected fun receive(data: ByteArray) {
scope.launch {
logger.debug { "[${this@AbstractPort}] RECEIVED: ${data.decodeToString()}" }
logger.debug { "${this@AbstractPort} RECEIVED: ${data.decodeToString()}" }
incoming.send(data)
}
}
@ -49,7 +49,7 @@ public abstract class AbstractPort(
for (data in outgoing) {
try {
write(data)
logger.debug { "[${this@AbstractPort}] SENT: ${data.decodeToString()}" }
logger.debug { "${this@AbstractPort} SENT: ${data.decodeToString()}" }
} catch (ex: Exception) {
if (ex is CancellationException) throw ex
logger.error(ex) { "Error while writing data to the port" }

View File

@ -9,7 +9,7 @@ plugins {
kotlin{
explicitApi = null
useFx(ru.mipt.npm.gradle.FXModule.CONTROLS)
useFx(ru.mipt.npm.gradle.FXModule.CONTROLS, configuration = ru.mipt.npm.gradle.DependencyConfiguration.IMPLEMENTATION)
}
val ktorVersion: String by rootProject.extra

View File

@ -3,6 +3,7 @@ package ru.mipt.npm.devices.pimotionmaster
import hep.dataforge.context.Global
import hep.dataforge.control.controllers.DeviceManager
import hep.dataforge.control.controllers.installing
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleStringProperty
import javafx.scene.Parent
@ -27,20 +28,29 @@ class PiMotionMasterView : View() {
override val root: Parent = borderpane {
top {
hbox {
form {
val host = SimpleStringProperty("127.0.0.1")
val port = SimpleIntegerProperty(10024)
val virtual = SimpleBooleanProperty(false)
fieldset("Address:") {
field("Host:") {
textfield(host)
textfield(host){
enableWhen(virtual.not())
}
}
field("Port:") {
textfield(port)
}
field("Virtual device:") {
checkbox(property = virtual)
}
}
button("Connect") {
action {
if(virtual.get()){
controller.context.launchPiDebugServer(port.get(), listOf("1", "2"))
}
controller.motionMaster.connect(host.get(), port.get())
}
}

View File

@ -10,12 +10,9 @@ import hep.dataforge.control.controllers.*
import hep.dataforge.control.ports.*
import hep.dataforge.meta.*
import hep.dataforge.names.NameToken
import hep.dataforge.values.Null
import hep.dataforge.values.asValue
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import tornadofx.*
@ -32,10 +29,13 @@ class PiMotionMasterDevice(
context.coroutineContext + SupervisorJob(context.coroutineContext[Job])
)
val address: DeviceProperty by writingVirtual(Null) {
info = "The port for TCP connector"
}
private var address: Meta? = null
val connected by readingBoolean(false, descriptorBuilder = {
info = "True if the connection address is defined and the device is initialized"
}) {
address != null
}
val timeout: DeviceProperty by writingVirtual(200.asValue()) {
info = "Timeout"
@ -43,8 +43,7 @@ class PiMotionMasterDevice(
var timeoutValue: Duration by timeout.duration()
private val connector = PortProxy { portFactory(address.value.node ?: Meta.EMPTY, context) }
private val connector = PortProxy { portFactory(address ?: error("The device is not connected"), context) }
/**
* Name-friendly accessor for axis
@ -63,11 +62,13 @@ class PiMotionMasterDevice(
info = "Connect to specific port and initialize axis"
}) { portSpec ->
//Clear current actions if present
if (address.value != null) {
if (address != null) {
stop()
}
//Update port
address.value = portSpec
address = portSpec.node
connected.invalidate()
connector.open()
//Initialize axes
if (portSpec != null) {
val idn = identity.read()
@ -84,6 +85,17 @@ class PiMotionMasterDevice(
}
}
val disconnect: DeviceAction by acting({
info = "Disconnect the program from the device if it is connected"
}) {
connector.close()
if (address != null) {
stop()
}
address = null
connected.invalidate()
}
fun connect(host: String, port: Int) {
scope.launch {
connect(Meta {
@ -126,7 +138,16 @@ class PiMotionMasterDevice(
withTimeout(timeoutValue) {
sendCommandInternal(command, *arguments)
val phrases = connector.receiving().withDelimiter("\n")
phrases.takeWhile { it.endsWith(" \n") }.toList() + phrases.first()
var lastLineFlag = false
phrases.transformWhile { line ->
if (lastLineFlag) {
false
} else {
emit(line)
lastLineFlag = !line.endsWith(" \n")
true
}
}.toList()
}
} catch (ex: Throwable) {
logger.warn { "Error during PIMotionMaster request. Requesting error code." }

View File

@ -1,7 +1,7 @@
package ru.mipt.npm.devices.pimotionmaster
import hep.dataforge.context.Context
import hep.dataforge.context.Global
import hep.dataforge.control.ports.Port
import io.ktor.network.selector.ActorSelectorManager
import io.ktor.network.sockets.aSocket
import io.ktor.network.sockets.openReadChannel
@ -19,7 +19,8 @@ val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
}
@OptIn(KtorExperimentalAPI::class, InternalAPI::class)
fun CoroutineScope.launchPiDebugServer(port: Int, virtualPort: Port): Job = launch(exceptionHandler) {
fun Context.launchPiDebugServer(port: Int, axes: List<String>): Job = launch(exceptionHandler) {
val virtualDevice = PiMotionMasterVirtualDevice(this@launchPiDebugServer, axes)
val server = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().bind(InetSocketAddress("localhost", port))
println("Started virtual port server at ${server.localAddress}")
@ -31,7 +32,7 @@ fun CoroutineScope.launchPiDebugServer(port: Int, virtualPort: Port): Job = laun
val output = socket.openWriteChannel()
val sendJob = launch {
virtualPort.receiving().collect {
virtualDevice.receiving().collect {
//println("Sending: ${it.decodeToString()}")
output.writeAvailable(it)
output.flush()
@ -43,7 +44,7 @@ fun CoroutineScope.launchPiDebugServer(port: Int, virtualPort: Port): Job = laun
input.read { buffer ->
val array = buffer.moveToByteArray()
launch {
virtualPort.send(array)
virtualDevice.send(array)
}
}
}
@ -61,10 +62,8 @@ fun CoroutineScope.launchPiDebugServer(port: Int, virtualPort: Port): Job = laun
fun main() {
val port = 10024
val virtualDevice = PiMotionMasterVirtualDevice(Global, listOf("1", "2"))
val virtualPort = VirtualPort(virtualDevice, Global)
runBlocking(Dispatchers.Default) {
val serverJob = launchPiDebugServer(port, virtualPort)
val serverJob = Global.launchPiDebugServer(port, listOf("1", "2"))
readLine()
serverJob.cancel()
}