From 5322bf743bc74b3c23a87fb011457d328549cffe Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Wed, 20 Oct 2021 12:53:48 +0300 Subject: [PATCH 1/4] Implement VirtualCar --- .../controls/demo/virtual_car/VirtualCar.kt | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt new file mode 100644 index 0000000..c8dfcee --- /dev/null +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt @@ -0,0 +1,74 @@ +package ru.mipt.npm.controls.demo.virtual_car + +import kotlinx.coroutines.launch +import ru.mipt.npm.controls.properties.* +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.double +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.transformations.MetaConverter +import java.time.Instant +import kotlin.time.Duration +import kotlin.time.ExperimentalTime + +class VirtualCar : DeviceBySpec(VirtualCar) { + private var speedState: Pair = Pair(0.0, 0.0) + get() { + val previous_speed = field + val current_acceleration = this.accelerationState + val now = Instant.now().toEpochMilli().toDouble() + val time_difference = now - this.timeState + this.timeState = now + field = Pair( + previous_speed.first + time_difference * current_acceleration.first, + previous_speed.second + time_difference * current_acceleration.second + ) + return field + } + + private var accelerationState: Pair = Pair(0.0, 0.0) + set(value) { + this.speedState + field = value + } + + private var timeState = Instant.now().toEpochMilli().toDouble() + + object DoublePairMetaConverter : MetaConverter> { + override fun metaToObject(meta: Meta): Pair = Pair( + meta["x"].double ?: 0.0, + meta["y"].double ?: 0.0 + ) + + override fun objectToMeta(obj: Pair): Meta = Meta { + "x" put obj.first + "y" put obj.second + } + + } + + companion object : DeviceSpec(::VirtualCar) { + val speed by property(DoublePairMetaConverter) { this.speedState } + + val acceleration by property(DoublePairMetaConverter, VirtualCar::accelerationState) + + val carProperties by metaProperty { + Meta { + val time = Instant.now() + "time" put time.toEpochMilli() + "speed" put DoublePairMetaConverter.objectToMeta(read(speed)) + "acceleration" put DoublePairMetaConverter.objectToMeta(read(acceleration)) + } + } + + @OptIn(ExperimentalTime::class) + override fun VirtualCar.onStartup() { + launch { + speed.read() + acceleration.read() + } + doRecurring(Duration.milliseconds(50)){ + carProperties.read() + } + } + } +} From 7c4d0a5f36efb306eb0532e6141f6b0d4cdde3de Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Wed, 20 Oct 2021 16:50:18 +0300 Subject: [PATCH 2/4] Change VirtualCar state and add controller --- .../controls/demo/virtual_car/VirtualCar.kt | 32 +++-- .../demo/virtual_car/VirtualCarController.kt | 125 ++++++++++++++++++ 2 files changed, 143 insertions(+), 14 deletions(-) create mode 100644 demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCarController.kt diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt index c8dfcee..c8a8fc8 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt @@ -11,28 +11,33 @@ import kotlin.time.Duration import kotlin.time.ExperimentalTime class VirtualCar : DeviceBySpec(VirtualCar) { - private var speedState: Pair = Pair(0.0, 0.0) + private var _speedState: Pair = Pair(0.0, 0.0) + private val speedState: Pair get() { - val previous_speed = field - val current_acceleration = this.accelerationState - val now = Instant.now().toEpochMilli().toDouble() - val time_difference = now - this.timeState - this.timeState = now - field = Pair( - previous_speed.first + time_difference * current_acceleration.first, - previous_speed.second + time_difference * current_acceleration.second - ) - return field + updateSpeedLocationTime() + return this._speedState } private var accelerationState: Pair = Pair(0.0, 0.0) set(value) { - this.speedState + updateSpeedLocationTime() field = value } private var timeState = Instant.now().toEpochMilli().toDouble() + private fun updateSpeedLocationTime() { + val previousSpeed = this._speedState + val currentAcceleration = this.accelerationState + val now = Instant.now().toEpochMilli().toDouble() + val timeDifference = now - this.timeState + this.timeState = now + this._speedState = Pair( + previousSpeed.first + timeDifference * currentAcceleration.first * 1e-3, + previousSpeed.second + timeDifference * currentAcceleration.second * 1e-3 + ) + } + object DoublePairMetaConverter : MetaConverter> { override fun metaToObject(meta: Meta): Pair = Pair( meta["x"].double ?: 0.0, @@ -43,7 +48,6 @@ class VirtualCar : DeviceBySpec(VirtualCar) { "x" put obj.first "y" put obj.second } - } companion object : DeviceSpec(::VirtualCar) { @@ -66,7 +70,7 @@ class VirtualCar : DeviceBySpec(VirtualCar) { speed.read() acceleration.read() } - doRecurring(Duration.milliseconds(50)){ + doRecurring(Duration.seconds(1)){ carProperties.read() } } diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCarController.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCarController.kt new file mode 100644 index 0000000..314bccb --- /dev/null +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCarController.kt @@ -0,0 +1,125 @@ +package ru.mipt.npm.controls.demo.virtual_car + +import io.ktor.server.engine.ApplicationEngine +import javafx.beans.property.DoubleProperty +import javafx.scene.Parent +import javafx.scene.control.TextField +import javafx.scene.layout.Priority +import javafx.stage.Stage +import kotlinx.coroutines.launch +import org.eclipse.milo.opcua.sdk.server.OpcUaServer +import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText +import ru.mipt.npm.controls.api.DeviceMessage +import ru.mipt.npm.controls.client.connectToMagix +import ru.mipt.npm.controls.controllers.DeviceManager +import ru.mipt.npm.controls.controllers.install +import ru.mipt.npm.controls.demo.virtual_car.VirtualCar.Companion.acceleration +import ru.mipt.npm.controls.opcua.server.OpcUaServer +import ru.mipt.npm.controls.opcua.server.endpoint +import ru.mipt.npm.controls.opcua.server.serveDevices +import ru.mipt.npm.magix.api.MagixEndpoint +import ru.mipt.npm.magix.rsocket.rSocketWithTcp +import ru.mipt.npm.magix.server.startMagixServer +import space.kscience.dataforge.context.* +import tornadofx.* + +class VirtualCarController : Controller(), ContextAware { + + var device: VirtualCar? = null + var magixServer: ApplicationEngine? = null + var opcUaServer: OpcUaServer = OpcUaServer { + setApplicationName(LocalizedText.english("ru.mipt.npm.controls.opcua")) + endpoint { + setBindPort(9999) + //use default endpoint + } + } + + override val context = Context("demoDevice") { + plugin(DeviceManager) + } + + private val deviceManager = context.fetch(DeviceManager) + + fun init() { + context.launch { + device = deviceManager.install("virtual-car", VirtualCar) + //starting magix event loop + magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) + //Launch device client and connect it to the server + val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) + deviceManager.connectToMagix(deviceEndpoint) + + opcUaServer.startup() + opcUaServer.serveDevices(deviceManager) + } + } + + fun shutdown() { + logger.info { "Shutting down..." } + opcUaServer.shutdown() + logger.info { "OpcUa server stopped" } + magixServer?.stop(1000, 5000) + logger.info { "Magix server stopped" } + device?.close() + logger.info { "Device server stopped" } + context.close() + } +} + + +class VirtualCarControllerView : View(title = " Virtual car controller remote") { + private val controller: VirtualCarController by inject() + private var accelerationXProperty: DoubleProperty by singleAssign() + private var accelerationXTF: TextField by singleAssign() + private var accelerationYProperty: DoubleProperty by singleAssign() + private var accelerationYTF: TextField by singleAssign() + + override val root: Parent = vbox { + hbox { + label("AccelerationX") + pane { + hgrow = Priority.ALWAYS + } + accelerationXProperty = doubleProperty() + accelerationXTF = textfield(accelerationXProperty) + } + hbox { + label("AccelerationY") + pane { + hgrow = Priority.ALWAYS + } + accelerationYProperty = doubleProperty() + accelerationYTF = textfield(accelerationYProperty) + } + button("Submit") { + useMaxWidth = true + action { + controller.device?.run { + launch { + acceleration.write(Pair(accelerationXProperty.get(), accelerationYProperty.get())) + } + } + } + } + } +} + +class VirtualCarControllerApp : App(VirtualCarControllerView::class) { + private val controller: VirtualCarController by inject() + + override fun start(stage: Stage) { + super.start(stage) + controller.init() + } + + override fun stop() { + controller.shutdown() + super.stop() + } +} + + +fun main() { + launch() +} \ No newline at end of file From b1d9d8e8badb8a0e7722255c0f3632323aa49767 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Wed, 20 Oct 2021 16:59:30 +0300 Subject: [PATCH 3/4] Add location property in virtual car --- .../controls/demo/virtual_car/VirtualCar.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt index c8a8fc8..b886d6b 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt @@ -18,6 +18,13 @@ class VirtualCar : DeviceBySpec(VirtualCar) { return this._speedState } + private var _locationState: Pair = Pair(0.0, 0.0) + private val locationState: Pair + get() { + updateSpeedLocationTime() + return this._locationState + } + private var accelerationState: Pair = Pair(0.0, 0.0) set(value) { updateSpeedLocationTime() @@ -28,6 +35,7 @@ class VirtualCar : DeviceBySpec(VirtualCar) { private fun updateSpeedLocationTime() { val previousSpeed = this._speedState + val previousLocation = this._locationState val currentAcceleration = this.accelerationState val now = Instant.now().toEpochMilli().toDouble() val timeDifference = now - this.timeState @@ -36,6 +44,14 @@ class VirtualCar : DeviceBySpec(VirtualCar) { previousSpeed.first + timeDifference * currentAcceleration.first * 1e-3, previousSpeed.second + timeDifference * currentAcceleration.second * 1e-3 ) + val locationDifference = Pair( + timeDifference * 1e-3 * (previousSpeed.first + currentAcceleration.first * timeDifference * 1e-3 / 2), + timeDifference * 1e-3 * (previousSpeed.second + currentAcceleration.second * timeDifference * 1e-3 / 2) + ) + this._locationState = Pair( + previousLocation.first + locationDifference.first, + previousLocation.second + locationDifference.second + ) } object DoublePairMetaConverter : MetaConverter> { @@ -53,6 +69,8 @@ class VirtualCar : DeviceBySpec(VirtualCar) { companion object : DeviceSpec(::VirtualCar) { val speed by property(DoublePairMetaConverter) { this.speedState } + val location by property(DoublePairMetaConverter) { this.locationState } + val acceleration by property(DoublePairMetaConverter, VirtualCar::accelerationState) val carProperties by metaProperty { @@ -60,6 +78,7 @@ class VirtualCar : DeviceBySpec(VirtualCar) { val time = Instant.now() "time" put time.toEpochMilli() "speed" put DoublePairMetaConverter.objectToMeta(read(speed)) + "location" put DoublePairMetaConverter.objectToMeta(read(location)) "acceleration" put DoublePairMetaConverter.objectToMeta(read(acceleration)) } } @@ -69,6 +88,7 @@ class VirtualCar : DeviceBySpec(VirtualCar) { launch { speed.read() acceleration.read() + location.read() } doRecurring(Duration.seconds(1)){ carProperties.read() From 418a97f99ae3abc9417360a5704778c926def7e9 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Sat, 23 Oct 2021 12:12:19 +0300 Subject: [PATCH 4/4] Create Coordinates class instead of Pair and refactor state update --- .../controls/demo/virtual_car/VirtualCar.kt | 62 +++++++++---------- .../demo/virtual_car/VirtualCarController.kt | 2 +- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt index b886d6b..738b3dd 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt @@ -10,22 +10,22 @@ import java.time.Instant import kotlin.time.Duration import kotlin.time.ExperimentalTime +data class Coordinates(val x: Double = 0.0, val y: Double = 0.0) + class VirtualCar : DeviceBySpec(VirtualCar) { - private var _speedState: Pair = Pair(0.0, 0.0) - private val speedState: Pair - get() { + private var speedState: Coordinates = Coordinates() + private fun updateAndGetSpeed(): Coordinates { updateSpeedLocationTime() - return this._speedState + return this.speedState } - private var _locationState: Pair = Pair(0.0, 0.0) - private val locationState: Pair - get() { + private var locationState: Coordinates = Coordinates() + private fun updateAndGetLocation(): Coordinates { updateSpeedLocationTime() - return this._locationState + return this.locationState } - private var accelerationState: Pair = Pair(0.0, 0.0) + private var accelerationState: Coordinates = Coordinates() set(value) { updateSpeedLocationTime() field = value @@ -34,52 +34,52 @@ class VirtualCar : DeviceBySpec(VirtualCar) { private var timeState = Instant.now().toEpochMilli().toDouble() private fun updateSpeedLocationTime() { - val previousSpeed = this._speedState - val previousLocation = this._locationState + val previousSpeed = this.speedState + val previousLocation = this.locationState val currentAcceleration = this.accelerationState val now = Instant.now().toEpochMilli().toDouble() val timeDifference = now - this.timeState this.timeState = now - this._speedState = Pair( - previousSpeed.first + timeDifference * currentAcceleration.first * 1e-3, - previousSpeed.second + timeDifference * currentAcceleration.second * 1e-3 + this.speedState = Coordinates( + previousSpeed.x + timeDifference * currentAcceleration.x * 1e-3, + previousSpeed.y + timeDifference * currentAcceleration.y * 1e-3 ) - val locationDifference = Pair( - timeDifference * 1e-3 * (previousSpeed.first + currentAcceleration.first * timeDifference * 1e-3 / 2), - timeDifference * 1e-3 * (previousSpeed.second + currentAcceleration.second * timeDifference * 1e-3 / 2) + val locationDifference = Coordinates( + timeDifference * 1e-3 * (previousSpeed.x + currentAcceleration.x * timeDifference * 1e-3 / 2), + timeDifference * 1e-3 * (previousSpeed.y + currentAcceleration.y * timeDifference * 1e-3 / 2) ) - this._locationState = Pair( - previousLocation.first + locationDifference.first, - previousLocation.second + locationDifference.second + this.locationState = Coordinates( + previousLocation.x + locationDifference.x, + previousLocation.y + locationDifference.y ) } - object DoublePairMetaConverter : MetaConverter> { - override fun metaToObject(meta: Meta): Pair = Pair( + object CoordinatesMetaConverter : MetaConverter { + override fun metaToObject(meta: Meta): Coordinates = Coordinates( meta["x"].double ?: 0.0, meta["y"].double ?: 0.0 ) - override fun objectToMeta(obj: Pair): Meta = Meta { - "x" put obj.first - "y" put obj.second + override fun objectToMeta(obj: Coordinates): Meta = Meta { + "x" put obj.x + "y" put obj.y } } companion object : DeviceSpec(::VirtualCar) { - val speed by property(DoublePairMetaConverter) { this.speedState } + val speed by property(CoordinatesMetaConverter) { this.updateAndGetSpeed() } - val location by property(DoublePairMetaConverter) { this.locationState } + val location by property(CoordinatesMetaConverter) { this.updateAndGetLocation() } - val acceleration by property(DoublePairMetaConverter, VirtualCar::accelerationState) + val acceleration by property(CoordinatesMetaConverter, VirtualCar::accelerationState) val carProperties by metaProperty { Meta { val time = Instant.now() "time" put time.toEpochMilli() - "speed" put DoublePairMetaConverter.objectToMeta(read(speed)) - "location" put DoublePairMetaConverter.objectToMeta(read(location)) - "acceleration" put DoublePairMetaConverter.objectToMeta(read(acceleration)) + "speed" put CoordinatesMetaConverter.objectToMeta(read(speed)) + "location" put CoordinatesMetaConverter.objectToMeta(read(location)) + "acceleration" put CoordinatesMetaConverter.objectToMeta(read(acceleration)) } } diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCarController.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCarController.kt index 314bccb..7be087e 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCarController.kt +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCarController.kt @@ -97,7 +97,7 @@ class VirtualCarControllerView : View(title = " Virtual car controller remote") action { controller.device?.run { launch { - acceleration.write(Pair(accelerationXProperty.get(), accelerationYProperty.get())) + acceleration.write(Coordinates(accelerationXProperty.get(), accelerationYProperty.get())) } } }